tsw Posted October 28, 2025 Share Posted October 28, 2025 I'd like to add choices to a pop-up parameter of my custom plugin by reading from a worksheet. Do I need to have an event-enabled plugin to do this? (I suspect I do...) My plugin is currently working with hard-coded choices in the pop-up parameter. Not sure I'm ready to make the leap into event-enabled plugins, so I wanted to confirm. Quote Link to comment
twk Posted October 28, 2025 Share Posted October 28, 2025 Yes, you need to have an event-enabled plugin to do this 1 Quote Link to comment
twk Posted October 29, 2025 Share Posted October 29, 2025 I recently indexed the entire Vectorworks official scripting documentation from GitHub using DeepWiki - it's a service that creates an AI-powered chat interface for any GitHub repository's documentation. (<Vectorworks/developer-scripting>) Instead of hunting through dozens of markdown files trying to figure out event-enabled plugins, I just asked DeepWiki directly and it guided me through the entire process. I used it to build this complete testWorksheet.py example that does exactly what you're asking for - dynamically populating popup choices from worksheet data. # testWorksheet.py import vs # Event constants kObjOnInitXProperties = 5 kParametricRecalculate = 3 kObjOnWidgetPrep = 41 kObjOnAddState = 44 # Added constant for clarity # Property constants kObjXPropHasUIOverride = 8 kObjXHasCustomWidgetVisibilities = 12 kObjXPropAcceptStates = 18 # Added constant for clarity kParametricEnableStateEventing = 590 # Added constant for clarity # Event result constants kObjectEventHandled = -8 # Configuration constants POPUP_PARAM_NAME = "WorksheetValues" # Change to match your popup parameter name WORKSHEET_NAME_PARAM = "WorksheetName" def read_popup_values_from_worksheet(worksheet_name: str, start_row: int = 2, end_row: int = 20, column: int = 1): """ How/Why: Reads a clean list of values from a specific worksheet column so a popup can reflect external data. - Uses Vectorworks worksheet API safely, ignoring empty cells and trimming strings. - Keeps row/column 1-based to match VW conventions. """ popup_values = [] # Locate worksheet ws_h = vs.GetObject(worksheet_name) if not ws_h: vs.AlrtDialog(f'Worksheet "{worksheet_name}" not found') return popup_values # Collect values (strings prioritized; numbers converted) for row in range(start_row, end_row + 1): try: if not vs.IsValidWSCell(ws_h, row, column): continue if vs.IsWSCellString(ws_h, row, column): s = vs.GetWSCellStringN(ws_h, row, column) if s and s.strip(): popup_values.append(s.strip()) elif vs.IsWSCellNumber(ws_h, row, column): val = vs.GetWSCellValue(ws_h, row, column) popup_values.append(str(val)) except Exception as e: # Keep robust on malformed cell content vs.AlrtDialog(f"Worksheet read error at row {row}, col {column}: {e}") return popup_values def populate_popup_from_worksheet(widget_id: int, worksheet_name: str): """ How/Why: Rebuilds the popup choices to reflect current worksheet content during WidgetPrep, ensuring OIP shows up-to-date options without forcing recalculation. """ # Reset current popup items vs.vsoWidgetPopupClear(widget_id) # Validate input if not worksheet_name or not worksheet_name.strip(): return # Configure read window start_row = 2 # Skip header end_row = 20 # Adjust for your data column = 1 # 1-based # Read and add values = read_popup_values_from_worksheet(worksheet_name, start_row, end_row, column) for value_id, display_text in enumerate(values): # Note: API takes integer IDs; documentation may show string vs.vsoWidgetPopupAdd(widget_id, value_id, display_text) # Optional separator + edit affordance if values: vs.vsoWidgetPopupAdd(widget_id, len(values), "-") vs.vsoWidgetPopupAdd(widget_id, len(values) + 1, "Edit Worksheet...") def create_plugin_geometry(): """ How/Why: Keeps geometry creation isolated so ParametricRecalculate stays tidy and testable. Replace this with your actual object creation code. """ # Example placeholder geometry vs.Rect(0, 0, 100, 100) vs.Circle(50, 50, 25) vs.Line((0, 0), (100, 100)) # 3D example: # vs.Box(0, 0, 0, 100, 100, 50) def main(): """ How/Why: Central event router that follows VW's event-enabled plugin rules. - InitX: declare capabilities and insert OIP widgets - AddState: only add state (no other logic) - WidgetPrep: populate dynamic UI (popups, visibilities, etc.) - Recalculate: build/update geometry and clear plugin state """ event, message = vs.vsoGetEventInfo() # Get plugin info (may be incomplete very early during init; do not exit early) ok, plugin_name, plugin_handle, record_handle, wall_handle = vs.GetCustomObjectInfo() # Map param name to OIP widget ID (param index is 0-based; widget ID is 1-based) # Guard for name lookup to avoid exceptions during very early init popup_param_index = None if plugin_name: idx = vs.vsoParamName2Index(plugin_name, POPUP_PARAM_NAME) if idx is not None and idx >= 0: popup_param_index = idx + 1 if event == kObjOnInitXProperties: # Enable extended behaviors and insert OIP widgets vs.SetObjPropVS(kObjXPropHasUIOverride, True) vs.SetObjPropVS(kObjXHasCustomWidgetVisibilities, True) vs.SetPrefInt(kParametricEnableStateEventing, 1) vs.SetObjPropVS(kObjXPropAcceptStates, True) vs.vsoInsertAllParams() elif event == kObjOnAddState: # VW rule: only add current state here if plugin_handle: _ = vs.vsoStateAddCurrent(plugin_handle, message) elif event == kObjOnWidgetPrep: # Rebuild dynamic popup content if popup_param_index is not None and plugin_handle and plugin_name: ws_name = vs.GetRField(plugin_handle, plugin_name, WORKSHEET_NAME_PARAM) if ws_name and ws_name.strip(): populate_popup_from_worksheet(popup_param_index, ws_name) # Must mark as handled vs.vsoSetEventResult(kObjectEventHandled) elif event == kParametricRecalculate: # Main geometry update create_plugin_geometry() # Clear state after successful recalc if plugin_handle: vs.vsoStateClear(plugin_handle) # PIOs are executed by VW; keep for standalone testing if needed: # if __name__ == "__main__": # main() This is honestly a game-changer for learning Vectorworks scripting. Tools like LLMs (ChatGPT/Claude/Gemini, etc) and now DeepWiki make it so much easier than the old days of digging through scattered documentation and forum posts. You can literally have a conversation with the official docs! This link is another prompt I asked it to prepare docs on event-enabled plugins using the script as context - really helpful for understanding the full workflow. https://deepwiki.com/search/earlier-we-built-this-code-tog_a8da7663-214c-428f-8690-e5863ffcac2b?mode=fast Basically: Event-Enabled Plugin Quick Reference Your testWorksheet.py demonstrates a parametric plugin that handles four key events to customize the Object Info Palette and manage dynamic content. Core Events Event 5 (InitXProperties) - Plugin setup Enable UI customization with SetObjPropVS(kObjXPropHasUIOverride, True) Add all parameters as widgets with vsoInsertAllParams() Event 44 (AddState) - State tracking Only call vsoStateAddCurrent(plugin_handle, message) - nothing else! Event 41 (WidgetPrep) - Dynamic UI updates Populate your popup with vsoWidgetPopupClear() and vsoWidgetPopupAdd() Must end with vsoSetEventResult(-8) Event 3 (ParametricRecalculate) - Geometry creation Create your geometry here Always call vsoStateClear(plugin_handle) when finished Key Documentation Links: Object Events Overview - Complete event system guide Parametric Custom Shape Pane Popup - Dynamic popup examples Plug-in with widget basic example - Python widget tutorial Widget Tips: Use vsoParamName2Index(plugin_name, param_name) + 1 to get widget IDs Parameter index is 0-based, widget ID is 1-based (hence the +1) The pattern is simple: setup (5) → track changes (44) → update UI (41) → create geometry (3). 1 Quote Link to comment
twk Posted October 29, 2025 Share Posted October 29, 2025 here are some screenshots: The temp object/drawing on document, the worksheet with the values, and the object info palette: The Plugin-Manager, the Plugin Definition: The Parameters: also plugin needs to be event enabled: 1 Quote Link to comment
tsw Posted November 6, 2025 Author Share Posted November 6, 2025 This certainly gives me a place to start diving into event enabled plugins. Thanks for the guidance! Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.