Jump to content

tbexon

Member
  • Posts

    76
  • Joined

  • Last visited

Reputation

19 Good

Personal Information

  • Homepage
    https://www.tb-lx.com/prod-lx-plugins/
  • Location
    United Kingdom

Recent Profile Visitors

1,751 profile views
  1. Hi All I'm trying to add a resource selector to a Event enabled, custom Widget enabled PIO, that will allow the user to select a texture. I'm using the kWidgetButtonResourcePopup (107) widget type. I can insert the ResourcePopup into the OIP fine (SS Below), however when clicked I'm not getting any button ID passed to kObjOnObjectUIButtonHit (35). I added a generic test button to make sure it wasn't something else and that successfully passes the Button ID to the kObjOnObjectUIButtonHit event. Is there possibly a different event type I need to look for? Or is there a setting I need to enable? I'm aware I need to use vsoButtonGetResource() to handle the Resource browser, which brings me onto my next question, according to https://developer.vectorworks.net/index.php?title=VS:vsoButtonGetResource the "Parameter name is a string that will receive the resource name" however this appears to be a param I pass to the function and, certainly according to the docs atleast, it will only return a bool. How am I meant to extract the Param string? Relevant code below! Thanks in Advance! kWidgetButtonResourcePopup = 107 def execute(): global PIO,localized_True_bool GetImageButton_ID = 1001 theEvent, eventMessage = None, None theEvent, eventMessage = vs.vsoGetEventInfo(theEvent, eventMessage) if theEvent == tb.Constants.kObjOnInitXProperties: # If the event is the initilisation of object propeties # Enable custom shape pane ok = vs.SetObjPropVS(tb.Constants.kObjXPropHasUIOverride, True) # Enables customisation fo widgets ok = vs.SetObjPropVS(tb.Constants.kObjXHasCustomWidgetVisibilities, True) # allows PIO to control the widget state in OIP ok = vs.SetObjPropVS(tb.Constants.kObjXPropPreference, True) # Enables preferences menu for PIO vs.SetPrefInt(tb.Constants.kParametricEnableStateEventing, 1); ok = vs.SetObjPropVS(tb.Constants.kObjXPropAcceptStates, True); ok = vs.vsoInsertAllParams() # Inserts all Parameters into OIP bool = vs.vsoAddWidget(GetImageButton_ID,kWidgetButtonResourcePopup,'Select Image') # Adds Select Image Button bool = vs.vsoAppendWidget(tb.widgetTypes.WidgetButton,1234,'Test', 0) elif theEvent == tb.Constants.kObjOnWidgetPrep: # If event is widget Prep tb.WidgetGenericPrep() # Sends message that all widget prep has been completed elif theEvent == tb.Constants.kObjOnObjectUIButtonHit: vs.AlrtDialog(eventMessage) if eventMessage == GetImageButton_ID: vs.vsoButtonGetResource(GetImageButton_ID, 97, 0, 0) elif theEvent == tb.Constants.kObjOnAddState: GenericGetStateChange(eventMessage) elif theEvent == tb.Constants.kObjXPropPreference: MakePropDialog() elif theEvent == tb.Constants.kResetEventID: # If the event is a object reset start_unit_index = tb.SetUnitsToMM() # Gets Current Units, and sets units to MM init() vs.Units(start_unit_index) # Resets Units vs.vsoStateClear(PIO.PIHan)
  2. Amazing thanks! Working code if it's of help for anyone: projectionMode = vs.GetProjection(vs.ActLayer()) if projectionMode != 6: # If the user is NOT in 2D Top/Plan View xAngleR, yAngelR, zAngleR, offset = vs.GetView() # Saves current view for Later vs.SetView("0d", "0d", "0d", "0", "0", "0") # Sets view to top down vs.SetPref(tb.PreferenceSelectors.DisableScreenRedraw, True) # Disables screen redraws temporarily # ------------- Other Code here ----------------------# if projectionMode != 6: # If user is NOT in Top/Plan, then reset 3d view vs.SetView(xAngleR, yAngelR, zAngleR,offset[0], offset[1],offset[2]) vs.SetPref(tb.PreferenceSelectors.DisableScreenRedraw, False) # Enables screen redraws
  3. HI All I'm sure I'm missing something obvious, but I've been searching through the Function reference aswell as preference selectors, and I'm struggling to find what I'm looking for. Basically within a PIO I need to set the view to a specific view, then for a better User Experience I want to return the user to their previous view (Code Below). This works fine if the user is in a 3d view, however if they are in Top/Plan it will always swap to 3d, which whilst not a massive disaster also isn't ideal. What i'm looking for is some kind of command where I can check whether the user is in a 3d view or 2d, and then, if in 2D, set the view BACK to 2d once the operation has been completed. xAngleR, yAngelR, zAngleR, offset = vs.GetView() # Saves current view for Later vs.SetView("0d", "0d", "0d", "0", "0", "0") # Sets view to top down vs.SetView(xAngleR, yAngelR, zAngleR,offset[0], offset[1],offset[2]) # Resets View back to previous user view Thanks in Advance!
  4. HI All I've been bashing my head against this problem for a couple of days now, and struggling to come up with a solution. I'm trying to create a PIO that draws a line of rectangle extrudes that are rotated by a specific amount, and aligned so that the bottom left corner touches the previous Bottom right. (image showing desired end result) At the moment it nearly works, however I am having issues with the more objects I insert they start to "drift" and gaps appear. (SS Below) I think this is basically because as the rotation of the rectangle increases the total length along the X axis reduces, and I am struggling to find a way to calculate this offset. I've managed to deal with the Y Offset by using vs.GetBBox() to get the Bottom RightY Value and using this as Y value for lining the next object up with the previous. However for the X axis this doesn't seem to work as the X bounding box value will default to furthest point on X axis which is the TOP Right point not bottom right. Is there a way I can easily get the co ordinates of each corner of the rectangle once it's rotated? Or do I need to try and remember how to do Trig and calculate this myself? I'm also wondering whether the issue is with the X, Y, Z Distance within Set3d Rot? Currently i'm trying to make the rotation centre the bottom left corner, but i'm wondering whether I should be using some other static centre for all of them. Any help is greatly appreciated! I've been staring at this for so long now I can't see the wood for the trees! Relevant Code below: def MakeCurvedScreen(): """ Creates a curved screen based on user specified number of panels & rows :return: """ TileWidth = 1000 # Tile Width (X) TileDepth = 95 # Tile Depth (Y) TileHeight = 1000 # Tile Height (Z) TileQty = 8 # Number of tiles in each Row NoOfRows = 1 # Number of Rows tileZRot = -2 # Rotation angle for each Tile tileXoffset = 0 # Amount to offset each tile along X Axis by to create rows tileZOffset = 0 # Amount to offset each tile along the Z axis by to create columns curTileQty = 0 # Tracks current amoutn of tiles inserted in current row curtileZRot = 0 # Tracks current height of row being inserted totalTiles = TileQty * NoOfRows # Total Tiles in screen col_index = 0 # Tracks what Column we are currently inserting into prevTileYLoc = 0 CorrectionX = 0 for EachTile in range(totalTiles): # Iterates through all tiles building wall if curTileQty == TileQty: # If a full row of tiles has been inserted, start new row tileZOffset += TileHeight curtileZRot += tileZRot curTileQty = 0 tileXoffset = 0 col_index = 0 prevTileYLoc = 0 # Creates Tile as extrude, adds a Y offset to account for the rotated tile being lower tileH = MakeSingleTile(0 + tileXoffset , 0 + tileZOffset, TileWidth, TileDepth, TileHeight, YOffset=prevTileYLoc) # Rotate Tile vs.Set3DRot(tileH, 0, 0, tileZRot * col_index, TileWidth*col_index,(TileDepth*col_index)*-1, 0) # Rotates Tile (p1X, p1Y), (p2X, p2Y) = vs.GetBBox(tileH) # Gets previous tiles co ords prevTileYLoc = p2Y # Gets the Bottom Right corner of current Tile for use with lining up the next tile tileXoffset += TileWidth # Increases Overall Row width count by panel width curTileQty += 1 col_index += 1 def MakeSingleTile(startX,startZ,width,depth,height, **kwargs): """ Creates a Single Tile from Rectangle of user specified Width & Depth :return: Handle """ vs.BeginXtrd(0,height) vs.RectangleN((0,0),(1,0),width,depth) vs.EndXtrd() tileH = vs.LNewObj() if 'YOffset' in kwargs: # If a Y Offset has been specified vs.Move3DObj(tileH, startX, kwargs['YOffset'], startZ) else: vs.Move3DObj(tileH,startX,0,startZ) return tileH
  5. Thanks Pat! That was exactly what I was looking for! Absolute hero!
  6. HI All Apologies if this has been answered somewhere already I'm struggling to find any answers. I am using vs.NameClass() to create a new class within a PIO. Is there a way to make that Class automatically visible in Viewports AND saved views similar to functionality within the new class dialog (SS below for reference) I'm basically looking for a way to force that class to be visible within all Viewports & Saved views to stop the user having to go through and edit them afterwards. I'm guessing I could iterate through every single viewport once the class is created and edit the visibility there. But that seems massively time consuming and inefficient. Especially for a large document. If this is the only option available to me, does anyone know if this approach would also work with Saved Views? I can see a function for creating a new Saved View (vs.SaveSheet()) but nothing to get a list of saved views or edit one. Thanks in Advance!
  7. So if I'm reading this correctly you want to be able to Run a command in VWX that will export a set of data as a CSV to then import this into your label printer software? First question is where is the Data coming from? Is the data in an already existing worksheet or do you need to get it yourself? If the latter is the case you have a couple of options: You can either build a worksheet that collects the data for you or you can get the data yourself using the ForEachObjectInLayer() command to cycle through all the relevant objects and retrieve the data you need. You would then write this data to a worksheet (have a look at these commands). You can then export the worksheet to CSV. Below is the code I use to achieve this. It will export the CSV to the specified directory. def exportWStoCSV(ws, FilePath, **kwargs): ''' subRowNo: specify sub row to extract data from HeaderRowNo: States which row the Column Headers are in :param ws: string :param FilePath: string :param kwargs: :return: ''' import csv try: h = ws vs.TargetSprdSheet(h) # Makes ws active vs.RecalculateWS(h) # recalculates the worksheet except: return False if 'subRowNo' in kwargs: if 'subRowNo' in kwargs: subrowNo = kwargs['subRowNo'] subRowCount = vs.GetWSSubrowCount(h, subrowNo) # Counts the total Sub Rows in Row 2 subRowCountRange = list(range(subRowCount + 1)) # Creates a itertable range for N.o of subRows zz = subRowCountRange.pop() # Removes the last value from list to account for 0 index number_of_rows_and_cols = list(vs.GetWSRowColumnCount(h)) # Counts total Rows and Columns in Worksheet totalColumnsRange = list(range(number_of_rows_and_cols[1])) # Creates a itertable range for N.o of columns totalRowRange = list(range(number_of_rows_and_cols[0])) # Creates a itertable range for N.o of rows if 'HeaderRowNo' in kwargs: headerRow = kwargs['HeaderRowNo'] # States which row the Column Headers are in else: headerRow = 1 # States which row the Column Headers are in headersList = [vs.GetWSCellStringN(h, headerRow, eachColumn + 1) for eachColumn in totalColumnsRange] # Creates a list with all the Column Headers if 'subRowNo' in kwargs: list_of_cells = [[vs.GetWSSubrowCellString(h, 2, eachColumn + 1, eachSubRow + 1) for eachColumn in totalColumnsRange] for eachSubRow in subRowCountRange] # Creates a List containing a list of each row e.g overalllist = [[Row1],[Row2],[Row3]] else: list_of_cells = [ [vs.GetWSCellStringN(h, eachRow +1, eachColumn + 1) for eachColumn in totalColumnsRange] for eachRow in totalRowRange] # Creates a List containing a list of each row e.g overalllist = [[Row1],[Row2],[Row3]] list_of_cells.pop(0) # Removes header row try: with open(FilePath, 'w', newline='', encoding='utf-8') as csvfile: # opens or Creates CSV Called Loom Data writer = csv.writer(csvfile) writer.writerow(headersList) # Writes the Headers to the file. for eachList in list_of_cells: writer.writerow(eachList) # Writes each Row to CSV return True except Exception as e: vs.AlrtDialog("Error Occured, CSV Not Written! Error Message to follow.") vs.AlrtDialog(str("Error Msg: {}".format(e))) If you're collecting the data yourself don't necessarily need to write the data to a worksheet you could just build the data into the required format for csvwriter. However I find for a better user experience it's good to show them the data within VWX aswell. Hope that's helpful!
  8. I’m on site today, but I’ll try and check over the weekend when I’m back at my PC!
  9. Have you definitely got developer mode disabled? For me, I found it had somehow got enabled system wide in the VWX preferences menu.
  10. I'll be honest I've just gone back over my template, and I've no idea why I'm using kObjXPropPreference... It's been a couple of years since I made that template. But certainly in terms of functionality it appears to work the same as kObjOnInitXProperties. (though I'm sure there is a difference) maybe @JBenghiat can shed some light). But basically within that call i'm just building the preferences dialog, and setting default values
  11. So thanks to the wonderful @VladoI have managed to get this working! There is a bug with developer mode that was causing the crash. Disabling developer mode, so that the script didn't run twice each time solved the crashing issue. Below is an explanation of my process, and what I understood is happening based on Vlados Explanation to me. Working with a Event Enabled Plug In Object, use the below template. When the button in the OIP is clicked, the ButtonClick() function is called. Within this function we call the vs.GetPt() command, passing a function as an arguement (in this case PickPointCallback). The way I understand what's happening is: When you run GetPt, it effectively restarts the script, clearing out all your code, and JUST runs whatever code is within the PickPointCallback function. Hence why any variables you may try and call or save won't affect anything outside of the function. To get around this, within the PickPointCallback we can save whatever values we wish to the Value Repository, which will temporarily save them into the documents session. I am saving 2 Values. Firstly a string containing the Co ordinates the user selected. The second is a bool indicating that the GetPt has been called but not handled. The reason for this is that once the GetPt call has been made the PIO won't automatically reset. We need to manually send it a reset event using vs.ResetObject(objectHandle). The reason i'm using the 'newFixtureBool' is because otherwise the main Init code won't know whether it's running after the UIButton has been pressed, or from some other Reset Event. There are other approaches, you don't necessarily need to use a Bool to track whether the button has been hit or not, it really depends on your specific project. Hopefully this is of help to someone, if anything is unclear, please do just ask, and I will do my best to help! Once again massive thanks to Vlado for figuring this out, and sending me on the right path! def makeOIP(): global objectHand , BackgrdColourDict, ForeGrdColDict, position_note_rec_name ok, objectName, objectHand, recordHand, wallHand = vs.GetCustomObjectInfo() theEvent, theEventData = vs.vsoGetEventInfo() # Gets Object Event info if theEvent == tb.Constants.kObjOnInitXProperties: # If the event is the initialisation of the object ok = vs.SetObjPropVS(tb.Constants.kObjXPropPreference, True) # Allows for creation of custom OIP ok = vs.SetObjPropVS(tb.Constants.kObjXPropHasUIOverride, True) # Custom OIP Overrides standard UI ok = vs.SetObjPropVS(tb.Constants.kObjXPropPreference,True) # Enables preferences menu for PIO ok = vs.SetObjPropVS(tb.Constants.kObjXHasCustomWidgetVisibilities,True) vs.vsoInsertAllParams() # Inserts all Parameters floato OIP result, scaleWidID = vs.vsoPrmName2WidgetID('','Scale') thisDoesNothing = 0 result = vs.vsoAppendWidget(tb.widgetTypes.WidgetButton, link_pos_button_id,'Get Position Info', thisDoesNothing) if theEvent == tb.Constants.kObjOnWidgetPrep: WidgetPrep() # Sets Info within the OIP if theEvent == tb.Constants.kObjXPropPreference: defaultVals = {} # Creates blank Dict for default vals MakePropertiesDialog() if theEvent == tb.Constants.kObjOnObjectUIButtonHit: # If the event is a button being pressed if theEventData == link_pos_button_id: # If the Button Pressed is Link Position Button ButtonClickFunction() # Runs the GetPt Command vs.ResetObject(objectHand) # Resets the Plug In object if theEvent == tb.Constants.kResetEventID: # If Object is reset init() # Creates the Object makeOIP() def ButtonClickFunction(): """ Prompts the user to select a point on the drawing, and then calls the PickPointCallback function with the co ordinates for the user specified location as an arguement :return: """ try: vs.GetPt(PickPointCallback) # Prompts user to select a point on the drawing except Exception as e: vs.AlrtDialog(str(e)) h, p = 0,0 def PickPointCallback(pt): """ Callback function used in GetPt call to allow user to select :param pt: XY Co ords user selects :return: """ # Stores the fixture position as a VWX Value string for retrieving later after GetPt callback has been run vs.Rpstr_SetValueStr('tb__PositionLoc',str(pt)) vs.Rpstr_SetValueBool('tb__newFixtureBool',True) def init(): """ Runs whenever object reset event is called :return: """ NewFixH_bool = vs.Rpstr_GetValueBool('tb__newFixtureBool',False) Fix_H = vs.Rpstr_GetValueStr('tb__PositionLoc', False) # mGets the saved fixture handle if NewFixH_bool: # If the GetPt call has been made, but not handled SetPositionData() # Gets the saved GetPt data and do whatever you would like with it
  12. Thanks Dom & Pat, I was worried this may be the case. Thank you for your suggested solution Dom, unfortunately because I'm only looking to get user input when a button is clicked not on script initialisation, the only way I could see to implement this is to rewrite the base code for building the PIO in Vectorscript, and then call multiple python scripts at appropriate points. Given that this Plug-In is over 2000 Lines of Python, I just don't see the work required to implement this as a reasonable option for the functionality gain. I'd be curious to see what is stopping this functionality from being implemented within the Python side of Vector script, Python itself as a programming language definitely has tools to support this. From a personal perspective it certainly would be a big benefit to some of my tools!
  13. Hi All So I'm trying to add functionality into an already existing Event based PIO that allows the user to click a button in the OIP and choose a Lighting fixture on the drawing, and get some information from it. The bit I'm struggling with is finding a way to use vs.GetPt() (or any similar user interactive command) within a Python Event based plug in object. The structure of the Plug In is pretty standard, the main outline of which is below. def makeOIP(): global objectHand , BackgrdColourDict, ForeGrdColDict, position_note_rec_name ok, objectName, objectHand, recordHand, wallHand = vs.GetCustomObjectInfo() theEvent, theEventData = vs.vsoGetEventInfo() # Gets Object Event info if theEvent == tb.Constants.kObjOnInitXProperties: # If the event is the initialisation of the object ok = vs.SetObjPropVS(tb.Constants.kObjXPropPreference, True) # Allows for creation of custom OIP ok = vs.SetObjPropVS(tb.Constants.kObjXPropHasUIOverride, True) # Custom OIP Overrides standard UI ok = vs.SetObjPropVS(tb.Constants.kObjXPropPreference,True) # Enables preferences menu for PIO ok = vs.SetObjPropVS(tb.Constants.kObjXHasCustomWidgetVisibilities,True) vs.vsoInsertAllParams() # Inserts all Parameters floato OIP result, scaleWidID = vs.vsoPrmName2WidgetID('','Scale') thisDoesNothing = 0 result = vs.vsoAppendWidget(tb.widgetTypes.WidgetButton, link_pos_button_id,'Get Position Info', thisDoesNothing) if theEvent == tb.Constants.kObjOnWidgetPrep: WidgetPrep() # Sets Info within the OIP if theEvent == tb.Constants.kObjXPropPreference: defaultVals = {} # Creates blank Dict for default vals MakePropertiesDialog() if theEvent == tb.Constants.kObjOnObjectUIButtonHit: # If the event is a button being pressed if theEventData == link_pos_button_id: # If the Button Pressed is Link Position Button link_to_hp() # Runs if theEvent == tb.Constants.kResetEventID: # If Object is reset init() # Creates the Object When the button is pressed it calls a function, link_to_hp() def link_to_hp(): vs.SetObjPropVS(tb.Constants.kObjXHasCustomWidgetVisibilities, False) # Sets widget custom visibility to false try: vs.GetPt(PickPointCallback) except Exception as e: vs.AlrtDialog(str(e)) h, p = 0,0 Fix_H = vs.Rpstr_GetValueStr(tb.ValueStrings.FixtureHandle_TempValString,False) # Stores the Fixture handle as a Vectorworks Value String vs.Message(str(Fix_H)) vs.SetObjPropVS(tb.Constants.kObjXHasCustomWidgetVisibilities, True) # Sets widget custom visibility to True def PickPointCallback(pt): vs.Message('X: ', pt[0], 'Y: ', pt[1]) Fix_H = vs.vstGetPickObject(pt[0],pt[1]) # Stores the fixture handle as a VWX Value string for retrieving later after GetPt callback has been run vs.Rpstr_SetValueStr(tb.ValueStrings.FixtureHandle_TempValString,Fix_H) vs.SetObjPropVS(tb.Constants.kObjXHasCustomWidgetVisibilities, True) # Sets the Custom Widget visibulites back to True The main issue I'm having is that whenever I press the button, the GetPt cross hairs appear, I'm able to select a point, but then Vectorworks crashes, with no error message. I understand the main issue is that the Python function GetPt will not block execution, so I need to use the callback function. However I am still getting the same results. From a fair amount of searching of the forums, I have found several suggested solutions such as @Paolo suggestion in this thread to disable custom widget visibilities whilst vs.GetPt is running, however sadly this does not seem to have any affect. I've seen suggestions that there cannot be any functions inbetween the GetPt call, purely as a test I've tried adding a vs.GetPt() at the end of the of the file, with some very dodgy logic to allow it only to run after the UIButton has been hit. E.g: makeOIP() if runGetPt: vs.GetPt(PickPointCallback) Still no improvement, still crashes with no error. I've tried completely emptying the PickPointCallback, in case it was something inside there, but still no change. Does anyone have any suggestions as to how to achieve this kind of functionality within a Python Script? Surely people have managed to find a way around this? @Vlado @JBenghiat @twk Thank you in advance!
  14. Just to reinforce both @michaelk and @Sam Jones throughly recommend truss tapes. We used to use Lightning tapes, and moved to Autoplot Truss tapes, and haven’t looked back since!
  15. Do you need to set the preference in the install file? Could you not just set it when whatever script you are installing first runs?
×
×
  • Create New...