Gibson431 Posted December 2, 2022 Share Posted December 2, 2022 I am attempting to create a point object plugin that generates a grid of boxes based on variable inputs for number of rows and number of boxes in each row. I have managed to make the plugin draw the grid after a dialog has been opened however I can't figure out how to get it to draw the grid on creation. I have the plugin set to event enabled with resets on for both move and rotate. I believe my issues are stemming from overriding the default behaviour for object events. Here is my program PROCEDURE testing; CONST {Event ID's} kEventID_ParametricRecalculate = 3; kEventID_OnObjPref = 4; kEventID_OnInitProperties = 5; kEventID_OnDoubleClick = 7; kEventID_OnUIButtonHit = 35; kEventID_OnAddState = 44; {Notification ID's} kNotifID_CreatedReset = 0; kNotifID_MovedReset = 1; kNotifID_RotatedReset = 2; kNotifID_ParameterChangedReset = 3; {permissions} kObjXPropHasUIOverride = 8; kObjXPropAcceptStates = 18; kDialogIDOffset = 4; kWidgetButton = 12; kButtonID = 1234; VAR objEvent, eventData :LONGINT; objH, recH, wallH, tempH :HANDLE; objName, recName :STRING; gDialogID :INTEGER; gNumEntries :INTEGER; gWidth, gHeight, gPadding :REAL; gVarArr :DYNARRAY[] of INTEGER; result :BOOLEAN; PROCEDURE DrawBoxes; var i,j, rowLength :INTEGER; rowStr : dynarray[] of char; isEmpty, isLinked :boolean; BEGIN result := GetCustomObjectInfo(objName, objH, recH, wallH); recName := getName(recH); if (objH <> nil) then {<--- seems to have an object usually} begin for i := 0 to gNumEntries-1 do BEGIN {vvv this is always an empty string until dialog is opened vvv} rowStr := GetRField(objH, recName, Concat('_',num2str(0,i))); rowLength := Str2Num(rowStr); for j:= 0 to rowLength-1 do BEGIN if rowLength <> 0 then Rect( j*(gWidth+gPadding), i*(gHeight+gPadding), j*(gWidth+gPadding)+gWidth, i*(gHeight+gPadding)+gHeight); end; END; end else AlertCritical('tried and failed', ''); {<--- never triggers} END; PROCEDURE DialogHandler(VAR item:LONGINT; data:LONGINT); VAR i, val :integer; BEGIN CASE item OF 1: BEGIN for i:= 0 to gNumEntries-1 do BEGIN result := GetEditInteger(gDialogID, (i*2)+kDialogIDOffset+1, val); SetRField(objH, recName, Concat('_', num2str(0,i)), Num2Str(0,val)); end; end; end; END; PROCEDURE DialogGenerator; var actionItem :LONGINT; result :BOOLEAN; i, tempVal :INTEGER; tempStr :dynarray[] of char; begin result := GetCustomObjectInfo(objName, objH, recH, wallH); gDialogID := CreateLayout('Boxes per Row', FALSE, 'OK', 'Cancel'); for i:= 0 to gNumEntries-1 do BEGIN CreateStaticText(gDialogID, (i*2)+kDialogIDOffset, concat('Row ', num2str(0, i+1)), 16); tempStr := GetRField(objH, recName, Concat('_',num2str(0,i))); {vvv creates parameters here for some reason vvv} if tempStr = '' then {if parameters don't exist yet} begin NewField(recName, Concat('_',num2str(0,i)), Num2Str(0,gNumEntries), 1,0); tempStr := Num2Str(0,gNumEntries); end; {^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^} CreateEditInteger(gDialogID, (i*2)+kDialogIDOffset+1, Str2Num(tempStr), 16); END; SetFirstLayoutItem(gDialogID, kDialogIDOffset); SetRightItem(gDialogID, kDialogIDOffset, kDialogIDOffset+1, 0, 0); for i:= 1 to gNumEntries-1 do BEGIN SetBelowItem(gDialogId, (i*2)+kDialogIDOffset-2, (i*2)+kDialogIDOffset, 0, 0); SetRightItem(gDialogId, (i*2)+kDialogIDOffset, (i*2)+kDialogIDOffset+1, 0, 0); END; actionItem := RunLayoutDialog(gDialogID, DialogHandler); end; PROCEDURE init; var i :integer; tempStr :dynarray[] of char; begin gNumEntries := PNumber_Of_Rows; gWidth := Pw; gHeight := Ph; gPadding := PPadding; result := GetCustomObjectInfo(objName, objH, recH, wallH); recName := GetName(recH); end; PROCEDURE setup; var i :integer; tempStr : dynarray[] of char; begin result := SetObjPropVs(kObjXPropHasUIOverride, true); if result = false then AlertCritical('Error','Failed to override UI'); result := SetObjPropVs(kObjXPropAcceptStates, true); if result = false then AlertCritical('Error','Failed to accept states'); result := vsoInsertAllParams; if result = false then AlertCritical('Error', 'There was an issue inserting parameters.'); if vsoAppendWidget(kWidgetButton, kButtonID, 'Select Columns', 0) = false then AlertCritical('Error', 'button broke'); result := GetCustomObjectInfo(objName, tempH, recH, wallH); {<--- this always fails} if result = false then begin message('setup failed'); end; recName := GetName(recH); if (objH <> nil) then {<--- this never triggers as GetCustomObjectInfo always fails} begin for i:=0 to gNumEntries-1 do begin if GetRField(objH, recName, Concat('_',num2str(0,i))) = '' then begin NewField(recName, Concat('_',num2str(0,i)), Num2Str(0,gNumEntries),1,0); end; end; end; end; procedure CheckRecalculations; var outWidgID : LONGINT; outPrmIdx : INTEGER; outOldVal : STRING; i, oldInt : INTEGER; begin {vvv never runs vvv} if vsoStateGetParamChng(objH, outWidgID, outPrmIdx, outOldVal) then begin if (outPrmIdx = 1) then begin oldInt := Str2Num(outOldVal); if (oldInt < gNumEntries) then for i:= oldInt-1 to gNumEntries-1 do NewField(recName, Concat('_',num2str(0,i)),Num2Str(0,gNumEntries),1,0); end; end; if vsoStateGet(objH, kNotifID_CreatedReset) then {<--- never runs} begin setup; end; end; BEGIN init; BeginGroup; vsoGetEventInfo(objEvent, eventData); CASE objEvent OF kEventID_OnInitProperties: setup; kEventID_OnUIButtonHit: DialogGenerator; kEventID_ParametricRecalculate: begin CheckRecalculations; DrawBoxes; vsoStateClear( objH ); end; kEventID_OnAddState: eventData := vsoStateAddCurrent( objH, eventData ); END; EndGroup; ResetObject(objH); end; Run(testing); Things I've tried: Adding the parameter creation code to the draw procedure - works in drawing the boxes but the defaults end up being random numbers ("-823648", "2893659") using CreateCustomObject - draws the grid on creation but in the origin and not part of the plugin using SetCustomObjectPath and setting it to FSActLayer - completely fails (im not really sure what the FSActLayer is, I was just following the function reference's example) To my understanding, the object has not actually been created yet in the initProperties event (5) which means I can't access its record to initialise the properties. This makes no sense and I'm almost certain I'm missing something. Any help would be greatly appreciated! Quote Link to comment
Pat Stanford Posted December 2, 2022 Share Posted December 2, 2022 I don't have time to review your code tonight, but one possible issue is that you can not use parameters as variables inside a script. They take a value at the start of the script and return that variable every time it is used during the execution of the script. You and set the value as many times as you want during the script and the last value will be what is saved when the script ends and what is used when the script runs the next time. The best policy is to read the parameters into a variable at the beginning of the script and write that variable back to the Parameter at the end of the script. HTH Quote Link to comment
LarryO Posted December 2, 2022 Share Posted December 2, 2022 I may in left field here in my understanding but I think I had a similar problem with a plugin that I built to draw pickets in a railing. I had to separate the drawing of the pickets from the collection of the points I required for the initial placement of the picket plugin. I used the predefined linear plugin with an additional point parameter as the base plugin to do all the work and used a tool script to gather the initial three definition points and insert them into the instance of the linear plugin's parameters that the tool script places into the document. Manipulation of the linear plugin afterwards is fully handled by the plugin's script. The tool script is only required to initialize the parameter values of the initial placement of the plugin. Quote Link to comment
MullinRJ Posted December 3, 2022 Share Posted December 3, 2022 On 12/1/2022 at 7:29 PM, Gibson431 said: To my understanding, the object has not actually been created yet in the initProperties event (5) which means I can't access its record to initialise the properties. This makes no sense and I'm almost certain I'm missing something. Hello @Gibson431, Welcome to the club. On your first At Bat, it's pretty much a given that nothing will make sense, and you are missing something, maybe everything. However, this is normal and no reason to turn around. There's only one way to go, and it's INTO the Rabbit Hole. There are many people down here and you're sure to get good advice on your journey. You are correct, when event 5 (initProperties) is issued, there is no geometry yet, so there is nothing for GetCustomObjectInfo() to return, except FALSE as the result. One thing I'd recommend you do is put an AlrtDialog() at the very front of your code, before the CASE statement and have it report the theEVENT and the theData (or theButton in the following example) for each execution of your code, the two pieces of info that are passed to your code every time it executes. This will show you what events are being sent and in what order, for every thing you do to your object (like place it, move it, rotate it, duplicate it, edit an OIP field, press a Widget Button, etc.) You'll be amazed how many time your script runs in response to every single user action, and which events are issued for each user action. I know, you want to have it draw something first so you can poke it, but trust me, knowing what events are coming, and in what order, will help you get there. For starters, just draw a Rectangle at the origin. Place the Rect() code in Event 3 (kResetEventID), then watch the show as you see the events file in. Here is a stripped out shell of an Event Enabled Object I've built in the past. There are things in it that need to be there and I can't explain all of them or what happens if they are not there. Much of the following was handed to me in dribs and drabs and I use it as a stepping stone to create new objects. Defining which procedures go into which events may require you to write back (a lot), but that is encouraged. Some are obvious, some are not. Trial and error are your friends. PROCEDURE YourCodeName; CONST kResetEventID = 3; kObjXPropPreference = 4; kObjOnInitXProperties = 5; kObjXPropHasUIOverride = 8; kObjXHasCustomWidgetVisibilities = 12; kObjXPropAcceptStates = 18; kObjOnObjectUIButtonHit = 35; kObjOnWidgetPrep = 41; kObjOnAddState = 44; kWidgetButton = 12; { 12 = Button WIdget } buttonID_1 = 1001; { user-definable index } buttonID_2 = 1002; { user-definable index } buttonID_3 = 1003; { user-definable index } VAR H, PIOHand, recHand, wallHand :Handle; result, IsNew, WidgetChanged :Boolean; WidgetIndex :Integer; theEvent, theButton, DoesNothing, WidgetID, WidgetMenuPos :Longint; PIOName, OldSel :String; P0 :Vector; BEGIN { MAIN LOOP } result := GetCustomObjectInfo(PIOName, PIOHand, recHand, wallHand); IsNew := IsNewCustomObject(PIOName); PIOrot := GetSymRot(PIOHand); GetSymLoc(PIOHand, P0.x, P0.y); { PIO insertion point } vsoGetEventInfo(theEvent, theButton); { VERY IMPORTANT } { comment out the following line when not needed. } AlrtDialog(concat('Top of Loop: event= ', theEvent, ' data= ', theButton, chr(13), 'hPIO≠nil ', PIOHand<>nil, ' Is new ', IsNew, chr(13), 'Result= ', result)); case theEvent of kObjOnInitXProperties: begin { 5 } { User has single-clicked the object's icon. } result := SetObjPropVS(kObjXPropPreference, TRUE); { 4, has custom Pref Dialog, OR don't open Object Properties dialog. } result := SetObjPropVS(kObjXPropHasUIOverride, TRUE); { 8, This tells VW to let the object decide what goes onto the OIP. } result := SetObjPropVS(kObjXHasCustomWidgetVisibilities, TRUE); { 12, enables Event 41 (kObjOnWidgetPrep). } result := SetObjPropVS(kObjXPropAcceptStates, TRUE); { 18, enable eventing for this plug-in. } result := vsoInsertAllParams; { Manually add the "normal" parameters... } result := vsoInsertWidget(9, kWidgetButton, 1003, 'Button 3 text', DoesNothing); { add button 3 } result := vsoInsertWidget(4, kWidgetButton, 1002, 'Button 2 text', DoesNothing); { add button 2 } result := vsoInsertWidget(0, kWidgetButton, 1001, 'Button 1 text', DoesNothing); { add button 1 } SetPrefInt(590, 1); { varParametricEnableStateEventing, kParametricStateEvent_ResetStateEvent } end; { 5 } kResetEventID: begin { 3 } { Object reset – redraw everything } WidgetChanged := vsoStateGetParamChng(PIOHand, WidgetID, WidgetIndex, OldSel); case WidgetIndex of { respond to changes in the OIP } end; { case } { draw PIO geometry } DrawSomething; vsoStateClear(PIOHand); { necessary } end; { 3 } kObjOnObjectUIButtonHit: begin { 35 } { User has clicked a button in the Object Info palette. } case theButton of buttonID_1: begin end; buttonID_2: begin end; buttonID_3: begin end; end; { case } ResetObject(PIOHand); end; { 35 } kObjOnWidgetPrep: begin { 41 } vsoSetEventResult(-8); { kObjectEventHandled } end; { 41 } kObjOnAddState: begin { 44 } theButton := vsoStateAddCurrent(PIOHand, theButton); end; { 44 } end; { case } END; Run(YourCodeName); One hint, PIOs pretty much start up not accepting any events. You enable the events you want to see with the SetObjPropVS() calls. I've commented what some of them do in the code above. When you get further in, you can play around with turning them ON and OFF to see what happens. There are a lot more events that are being ignored than the ones I've enabled, but many are specialized and you'll never need them. These will get you started. Welcome to the Warren, 🐰 Raymond Quote Link to comment
SamIWas Posted December 8, 2022 Share Posted December 8, 2022 Is it literally just a grid of squares? Can you enter your info via the OIP, or does it have to be by dialog? Because if it's just a grid of X x Y squares of A x B size with C padding, and the info can entered in the OIP, this seems overly complicated. Quote Link to comment
Gibson431 Posted December 9, 2022 Author Share Posted December 9, 2022 On 12/3/2022 at 4:46 PM, MullinRJ said: You are correct, when event 5 (initProperties) is issued, there is no geometry yet, so there is nothing for GetCustomObjectInfo() to return, except FALSE as the result. One thing I'd recommend you do is put an AlrtDialog() at the very front of your code, before the CASE statement and have it report the theEVENT and the theData (or theButton in the following example) for each execution of your code, the two pieces of info that are passed to your code every time it executes. This will show you what events are being sent and in what order, for every thing you do to your object (like place it, move it, rotate it, duplicate it, edit an OIP field, press a Widget Button, etc.) You'll be amazed how many time your script runs in response to every single user action, and which events are issued for each user action. I know, you want to have it draw something first so you can poke it, but trust me, knowing what events are coming, and in what order, will help you get there. For starters, just draw a Rectangle at the origin. Place the Rect() code in Event 3 (kResetEventID), then watch the show as you see the events file in. This is really useful, thank you so much! I will not lose hope! Quote Link to comment
Gibson431 Posted December 9, 2022 Author Share Posted December 9, 2022 5 hours ago, SamIWas said: Is it literally just a grid of squares? Can you enter your info via the OIP, or does it have to be by dialog? Because if it's just a grid of X x Y squares of A x B size with C padding, and the info can entered in the OIP, this seems overly complicated. Unfortunately this code is just an analog of the task I need to do. The grid of squares if just the way I decided to visualise the program. All it boils down to is being able to create a variable number of parameters based on input, and having them populate on object creation as well as be individually editable by the user in the OIP (or dialog in this case). Quote Link to comment
Vectorworks, Inc Employee klinzey Posted December 9, 2022 Vectorworks, Inc Employee Share Posted December 9, 2022 If I'm reading the code correctly you are trying to add fields to the parametric object record in Vectorscript which is not possible. If you are trying to build the OIP and the underlying parametric record dynamically in Vectorscript that's not possible. You can dynamically build a variable number controls in the dialog but the you will need to need to store the data differently because you can't add record fields to the parametric record via Vectorscript. You have 3 options. If you know the maximum number of fields create them all and show/hide them as needed. (This is the easiest solution but limiting.) You can create and store the data in a standard record format, that allows you to add new fields as necessary. Store the data in a delimited list in a hidden parameter of the parametric object. You will need to insert and extract the data as needed. (You may also be able to add the controls to the OIP but you will still need to manage the data for the controls with your code, Vectorworks will not do it automatically for you.) 1 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.