michaelk Posted May 17, 2020 Share Posted May 17, 2020 Somewhere, at some time in the past, I ran across an example script showing how to format OIPs. I'm interested in grouping parameters and maybe adding buttons. I don't see it at https://developer.vectorworks.net. Was I dreaming? Quote Link to comment
Sam Jones Posted May 17, 2020 Share Posted May 17, 2020 Is your PIO event aware? Quote Link to comment
Sam Jones Posted May 18, 2020 Share Posted May 18, 2020 Well. To do what you want it is going to have to be. You might have some wiggle room grouping parameters, but not much. It depends on what you mean by "grouping". Buttons in the OIP require an event aware PIO. If you want to go there, you probably need to get an example of a simple, event aware PIO. There is a structure to it and some arcane (somebody has to tell you) object variables and constants you have to set up. If Kevin Linzey, @Josh Benghiat, @Andy Dunning, or @Pat Stanford have one, super. I'm trying to finish an update right now, but if no one chimes in after a while, I'll strip one of mine out and send it to you or post it. That's how I ushered into event aware PIOs, so I owe it to somebody. If your lucky Josh will send you a structure example. His code is beautiful. Event aware PIOs are really cool, but they are an order of magnitude more complex. Take a deep breath and think about if the functionality, or cosmetics you gain are worth the added complexity and work. I had to be dragged kicking and screaming into event awarenesses, but once there, even my simplest objects are event aware. Quote Link to comment
michaelk Posted May 18, 2020 Author Share Posted May 18, 2020 Wow. Thanks Sam. I can't wait to stare into the face of event awareness. My next question was going to be if anyone had a "Hello World" event aware example. 😀 Quote Link to comment
Sam Jones Posted May 18, 2020 Share Posted May 18, 2020 That is going to be what you need, and that is what you are going to get. If you get it from me, it will be in Vectorscript. Also, I may forget about you, so send me a tickle in a week if no one comes through. The simple example will might declare over 100 constants, at least 15 global variables and then roughly 100-150 lines of code for the main and subroutines. The some of the subs and main deal with rare cases can be ignored and/or left out. :-) Quote Link to comment
michaelk Posted May 18, 2020 Author Share Posted May 18, 2020 This is going to be fun. Or overwhelming. Quote Link to comment
MullinRJ Posted May 18, 2020 Share Posted May 18, 2020 (edited) Michael, I'm not Josh, but I do have an example straight from the horse's mouth. { From VCOR, posted by Charles Chandler Jan 2007, updated 20 Dec 2015 } PROCEDURE Example3; CONST kResetEventID = 3; kObjOnInitXProperties = 5; kObjXPropHasUIOverride = 8; kWidgetButton = 12; kObjOnObjectUIButtonHit = 35; buttonID_1 = 1234; {user-definable index} VAR result :BOOLEAN; sourceFieldNumber :INTEGER; buttonEventID :INTEGER; theEvent, theButton :LONGINT; thisDoesNothing :LONGINT; displayString :STRING; BEGIN vsoGetEventInfo(theEvent, theButton); CASE theEvent OF {User has single-clicked the object's icon.} kObjOnInitXProperties: { 5 } BEGIN {This tells VW to let the object decide what goes onto the Object Info palette.} result := SetObjPropVS(kObjXPropHasUIOverride, TRUE); {Now we manually add the "normal" parameters...} {One way is to use this single call to add all of the existing parameters.} result := vsoInsertAllParams; {Alternatively, you can use this to tack individual parameters onto the end of the list one at a time. This way, you don't have to use SetParameterVisibility in the reset event to hide parameters that you never want to see.} sourceFieldNumber := 1; displayString := 'My Great Field Name'; result := vsoAppendParamWidget(sourceFieldNumber, displayString, thisDoesNothing); {Finally, we add the button.} displayString := 'My Great Button'; result := vsoAppendWidget(kWidgetButton, buttonID_1, displayString, thisDoesNothing); END; { 5 } {User has clicked a button in the Object Info palette.} kObjOnObjectUIButtonHit: { 35 } BEGIN CASE theButton OF buttonID_1: BEGIN AlrtDialog('Custom Button Dialog'); END; END; END; { 35 } {Object reset has been called.} kResetEventID: { 3 } BEGIN Rect(0, 1, 1, 0); { draw a square } END; { 3 } END; { CASE } END; RUN(Example3); Never go on an Event Enabled Trip without directions from Charles Chandler. It looks better with the keywords color coded in my text editor. Have fun, Raymond Edited May 18, 2020 by MullinRJ Quote Link to comment
Sam Jones Posted May 18, 2020 Share Posted May 18, 2020 Unfortunately, the Chandler example is missing the kOnWidgetPrepEvent (41) which is where most of the formatting of the OIP and populating of drop down menus happens.. Strangely, indenting of parameters needs to happen in the kObjOnInitXProperties (5), not the kOnWidgetPrepEvent. Josh corrected me there. Also, you need a: Result := GetCustomObjectInfo(gPIOName, gPIOHandle, gPIORecordHandle, gWallHandle); statement before the CASE test so that you have those variable values to use both in all case events and before the case events. Understanding the difference between a widget and a parameter is a bit of arcane knowledge. Widgets are what is displayed in the OIP, and parameters hold object data. The vsoInsertAllParams statement makes a widget for every parameter and connects them. You soon come to think of parameters as OIP objects, but they are not. Yes you send data to the parameters to display in the OIP, but that display is done by the parameter widgets. It is also good to have a: ParamChangeFlag := vsoStateGetParamChng(PIOHan,ParamChangeWidget,ParamChangeIndex,OldParam); statement near the beginning of your ResetEvent routine so that you will know when a parameter has been changed, in case you need to process as a result of the change. In a Data Cable object when a Part parameter value changes all the other part parameters have to process the change entered in the Part parameter widget. BTW, you can see that you need the GetCustomObjectInfo() function to harvest the PIO handle to use in the vsoStateGetParamChng() function. Also missing is the kObjOnAddState (44) event and the GetStatChange Procedure which has the: MsgData := vsoStateAddCurrent(PIOHan, MsgData); procedure that will track state changes. I.E. has anything in the OIP changed. Then there is non parameter widget placement and handling, which is what a button is. Chandler has that in the case test, but understanding where that button is and how that placement effects parameter widgets, I don't think is documented anywhere in the VS docs. Are we having fun yet? :-) I think I can find an example that was sent to me by Andy Dunning when I first got started that was invaluable. But I'm still hoping to find a Josh Benghiat example that was a little more comprehensive, but I will need to check with them before sending anything out. Quote Link to comment
michaelk Posted May 18, 2020 Author Share Posted May 18, 2020 Oy vey. Thanks for doing this. I’m really looking forward to trying to climb up to the next plateau. Is there a point at which all the undocumented arcane knowledge has been encountered and you are limited only by your limited coding talent? Quote Link to comment
Pat Stanford Posted May 18, 2020 Share Posted May 18, 2020 56 minutes ago, michaelk said: Is there a point at which all the undocumented arcane knowledge has been encountered and you are limited only by your limited coding talent? No. As you go deeper there is always something else that you want to do is deeper still. There is no limit to the acquisition of arcane knowledge. ;-) Quote Link to comment
Sam Jones Posted May 18, 2020 Share Posted May 18, 2020 Quick answer is no. The long answer is that you can get very far with a good PIO template and asking the rest of us what gives. I'm kind of at the bottom of the asking wrung. Andy knows a lot about VS drawing and he gets help from the mothership because they keep purchasing upgrades to stuff he sells them. Not as much as he might occasionally want but enough to get the job done. Josh did a deep dive of research into SDK constants and then into the SDK itself, so I consider him my ultimate source. If there were enough of us, I would try to get him to teach a course on scripting PIOs, but I would be lucky to find 4 guys willing to pay money. So, I try to keep my questions to him one at a time. In some ways, you are lucky. Since you are just beginning scripting event aware PIOs, you will be able to go far with a good template and questions here. You can also try and hit up Kevin, Josh, Andy, and I, directly. Everyone one has a day job, so turn around time varies. These days, as sucky as they are, have tended to improve response times. Again, after a week, hit me up again for a little fuller template, unless my mentors come through first. BTW, Carlotta is another wonderful and talented resource, but as I understand it she has 2 jobs and very very busy. Quote Link to comment
JBenghiat Posted May 19, 2020 Share Posted May 19, 2020 (edited) Hm, my @ears are burning. I think my template is not too off from Charles Chandler's, though there are certainly many more rabbit holes to go down. I also have a python template if you're moving in that direction. Specifically, any changes you make to the OIP are in the InitPIO event. When developer mode is off, this runs once per session the first time you raise the defaults dialog or select the PIO causing the OIP to draw. Widget prep runs just before the OIP displays. This is where you deal with dynamic data, like filling a menu, or hide/show and enable/disable. If you're not doing any of this, you really don't need to handle widget prep. A widget is each item in the OIP. Think of the parameters as fields in a database. vsoInsertAllParams will insert a widget for every parameter and automatically synchronize data between the widget and parameter, but otherwise there is no relationship between a widget and a parameter unless you code it. You can group parameters by inserting a separator widget. There may be too much to get into with a single post, so I might suggest starting with an appended button and ask for more from there. {---------------------------------------------------------------------} PROCEDURE InitPIO; BEGIN {This tells VW to let the object decide what goes onto the Object Info palette.} result:= SetObjPropVS(kObjXPropHasUIOverride, TRUE); result := SetObjPropVS(12, TRUE); {kObjXHasCustomWidgetVisibilities} SetPrefInt(kParametricEnableStateEventing, 1 ); result := SetObjPropVS(kObjXPropAcceptStates, TRUE); {Now we manually add the "normal" parameters...} {One way is to use this single call to add all of the existing parameters.} result:= vsoInsertAllParams; {Finally, we add the button.} result:= vsoAppendWidget(kWidgetButton, buttonID_1, '', thisDoesNothing); END; {---------------------------------------------------------------------} PROCEDURE WidgetPrep; BEGIN vsoSetEventResult( -8 {kObjectEventHandled} ); END; {---------------------------------------------------------------------} PROCEDURE GetStateChange; BEGIN eventMessage := vsoStateAddCurrent(PIHan, eventMessage); END; {---------------------------------------------------------------------} PROCEDURE ButtonHandler; BEGIN CASE eventMessage OF buttonID_1: BEGIN END; END; {CASE} END; {---------------------------------------------------------------------} PROCEDURE Main; BEGIN END; {---------------------------------------------------------------------} BEGIN boo:=GetCustomObjectInfo(PIName, PIHan, PIRecHan, PIWallHan); vsoGetEventInfo(theEvent, eventMessage); CASE theEvent OF {User has single-clicked the object's icon.} kObjOnInitXProperties: InitPIO; kObjOnWidgetPrep: WidgetPrep; kObjOnAddState: GetStateChange; {User has clicked a button in the Object Info palette.} kObjOnObjectUIButtonHit: ButtonHandler; {Object reset has been called.} kResetEventID: Main; END; {Event case} END; Edited May 20, 2020 by JBenghiat Quote Link to comment
michaelk Posted May 19, 2020 Author Share Posted May 19, 2020 Your ears should be burning! OK. I'm going to go sit quietly in a corner and think about my choices in life. Is it safe to assume that kObjOnWidgetPrep and the other k____ values are constant integer values and they might be listed in an appendix? Thanks! MK Quote Link to comment
twk Posted May 19, 2020 Share Posted May 19, 2020 12 minutes ago, JBenghiat said: I also have a python template if you're moving in that direction. I have a set one up similar to one Josh had posted somehwere before. Could I have a peak at yours Josh, to compare? Por Favor 😀 Quote Link to comment
MullinRJ Posted May 19, 2020 Share Posted May 19, 2020 5 minutes ago, michaelk said: Is it safe to assume that kObjOnWidgetPrep and the other k____ values are constant integer values and they might be listed in an appendix? NO! - You have to memorize all of the constants! What kind of programmer do you want to be? Spoon fed?!? Seriously, here are a few: kResetEventID = 3; kObjXPropPreference = 4; kObjOnInitXProperties = 5; kObjXPropHasUIOverride = 8; kObjXHasCustomWidgetVisibilities = 12; kObjXPropAcceptStates = 18; kObjOnObjectUIButtonHit = 35; kObjOnWidgetPrep = 41; kObjOnAddState = 44; Yes, there is a file in the SDK. You should download a copy if you haven't already. I'll see if I can give you a file name in a minute. Raymond 1 Quote Link to comment
michaelk Posted May 19, 2020 Author Share Posted May 19, 2020 I always considered the SDK label like a green "Mr. Yuck" sticker that parents used to put on poisonous household chemicals. Meant to keep kids away from Drano and me away from more coding that I couldn't handle. 😀 Quote Link to comment
MullinRJ Posted May 19, 2020 Share Posted May 19, 2020 Good analogy. Be prepared to hold your nose a lot and swallow your Cod Liver Oil, like a good little programmer. After you download the SDK look for this file: /SDK/SDKVW(######)/SDKLib/Include/Kernel/MiniCadHookIntf.h It's full of headaches you never thought possible, but it's full of answers, too; probably preceded by lots of questions. Bottom's Up 😝, Raymond 1 Quote Link to comment
twk Posted May 19, 2020 Share Posted May 19, 2020 My python work-in-progress template: def __temp_funcs(self): kObjXPropEditGroup = vs.SetObjPropVS(1, True) # -- Object has non-default "Enter Group" behavior. PIO objects can enter the profile or path group kObjXPropHasLayerScaleDeps = vs.SetObjPropVS(2, True) # -- Object wants to be reset with when its layer scale changes. kObjXPropPreference = vs.SetObjPropVS(4, True) # -- Object Definition Procedure (ODP) handles ParametricPreferencesMessage::kAction event. kObjXPropDirectModeling = vs.SetObjPropVS(5, True) # -- Object Definition Procedure (ODP) handles handles Direct Modeling events. kObjXPropAttributeTool = vs.SetObjPropVS(6, True) # -- Object Definition Procedure (ODP) handles handles Attribute Mapping events. kObjXPropCustomCursorResize = vs.SetObjPropVS(9, True) # -- // // boolean value // // The O kObjXPropDefaultPropertyUI = vs.SetObjPropVS(11, True) # -- // // unsigned char // // The bits in this property describe the UI displayed kObjXPropCustomHideFactor = vs.SetObjPropVS(16, True) # -- The parametric object provides custom scale threshold to be used to hide inner objects kObjXHasCustomWidgetValues = vs.SetObjPropVS(17, True) # -- OnObjectWidgetValueCall::kAction kObjXPropAcceptStates = vs.SetObjPropVS(18, True) # -- The parametric accpets ObjectState::kAction event kObjXPropPreserveContents = vs.SetObjPropVS(20, True) # -- VectorWorks will not delete the contents of the parametric kObjXPropHasContextMenu = vs.SetObjPropVS(21, True) # -- Sends ObjectContextMenuEvent::kAction_Init and ObjectContextMenuEvent::kAction_Event kObjXPropResetBeforeExport = vs.SetObjPropVS(23, True) # -- This plugin will be reset before Print or Export kObjXPropRedSymbolIsStyle = vs.SetObjPropVS(25, True) # -- Red Symbol of this object can be used to drag&drop kObjXPropHasAttrMappingGeom = vs.SetObjPropVS(26, True) # -- Sends ObjectGetSpecificGeometryCall::kAction with message is (OnObjectGetSpecificGeometry*) kObjXPropHasSectionVPGeom = vs.SetObjPropVS(27, True) # -- Sends ObjectGetSpecificGeometryCall::kAction with message is (OnObjectGetSpecificGeometry*) kObjXPropHasLayerElevDeps = vs.SetObjPropVS(28, True) # -- Resets the parametric if the layer elevation changes kObjXPropHasLayerHeightDeps = vs.SetObjPropVS(29, True) # -- Resets the parametric if the layer height changes kObjXPropLeaderLinePtIndex = vs.SetObjPropVS(33, True) # -- Controls whether how and a paremetric will be recognized by the align leader line menu command kObjXPropLabelPtIndex = vs.SetObjPropVS(34, True) # -- Controls whether how and a paremetric will be recognized by the align leader line menu command kObjXPropDataNameDisabled = vs.SetObjPropVS(35, True) # -- Controls whether the name field on the data pane should be disabled kObjXPropDirectionArrow = vs.SetObjPropVS(39, True) # -- Controls whether the direction arrow should be displaied kObjXPropIsSymbolBased = vs.SetObjPropVS(40, True) # -- Controls whether the object is symbol based and to show context menu "Locate Symbol In Resource Browser" kObjXPropTextStyleSupport = vs.SetObjPropVS(42, True) # -- Controls whether parametric objects can use class text style attribute and get Text Style on OIP kObjXPropDontShowZLocation = vs.SetObjPropVS(45, True) # -- When set to true will remove the Z coordinate for the location widget in the OIP for the parametric kObjXPropGetDragEvents = vs.SetObjPropVS(47, True) # -- Controls whether a parametric will recieve Drag events allowing it to draw custom geometry kObjXPropOIPNameHidden = vs.SetObjPropVS(48, True) # -- Controls whether the name field on the OIP should be hidden kObjXSupportsStyles = vs.SetObjPropVS(49, True) # -- Controls whether a parametric type support plug-in styles kObjXPropUseLayerCutPlane = vs.SetObjPropVS(51, True) # -- Controls whether a parametric object supports Layer Cut Plane values. kObjXPropAllowFreeHybrid2DPath = vs.SetObjPropVS(52, True) # -- Controls whether a Hybrid 2D Path parametric can have a path on a different plane than the object kObjXCatalogSupport = vs.SetObjPropVS(63, True) # -- Controls whether a parametric type supports catalogs kObjXPropDontShowTextureWidgets = vs.SetObjPropVS(64, True) # -- When set to true will remove the texture widgets in the Render pane of OIP for the parametric object kObjXPropHidePlanarDropDown = vs.SetObjPropVS(71, True) # -- When set to true will hide planar dropdown for the widget in the OIP for the parametric Quote Link to comment
JBenghiat Posted May 19, 2020 Share Posted May 19, 2020 I don’t have my computer in front of me, but I believe the file with all the PIO constants is MiniCADHookInf. MiniCADCallbacks is also useful. Even if you don’t actually use the SDK, the vs uses the same constants. Basically everything in the vs appendix is in there plus a whole bunch of other values. Doing a multi file search with an app like BBEdit is a great way to find what you need. Keep in mind that vs will only look at the first, I think, 20 characters of an identifier, and c++ doesn’t have that limit. That means kVeryLongVariableNameOne and kVeryLongVariableNameTwo look identical to the compiler and defining the second constant will throw an error. In those cases, you need to come up with your own shortened name rather than just cut and paste from the SDK file. Quote Link to comment
Sam Jones Posted May 19, 2020 Share Posted May 19, 2020 Josh, your template does not have a Reset event subroutine. Its not necessary, and the Main routine has the necessary event case of Reset event. However, the ResetEvent case calls the Main. Essentially, Main keeps calling itself. Recursion is cool, but I don't see a way to get out of the recursion loop. I think there needs to be a Reset subroutine that is called by Main in the Case event test. If not, if what you show actually works, please explain how you get out of the recursion loop. This could be cool, or it could be that you were in a hurry :-) Quote Link to comment
JBenghiat Posted May 20, 2020 Share Posted May 20, 2020 @Sam Jones you are correct. I pulled my template from two BBEdit Snippets, one for all the sub procedures, and one for the event case. I inserted the event case one line too high, so the END at the bottom of the code should be for the main sub. Quote Link to comment
michaelk Posted May 20, 2020 Author Share Posted May 20, 2020 Thanks everyone for your help. Although it's hurting my head. Do I understand correctly that the handling of the control geometry - GetCustomObjectInfo - is now moved outside the main part of the program (which is where I've always been doing it) to after the main program. Josh, in your example, the code that contains GetCustomObjectInfo and calls all the procedures - InitPrep, WidgetPrep, GetStateChange, etc - isn't in a Procedure. It's just a Begin & End after the main part of the program. Is there syntax missing? Or does the program just have two Begins and two Ends? Quote Link to comment
MullinRJ Posted May 20, 2020 Share Posted May 20, 2020 Hi Michael, The PIO main loop processes EVENTS, usually in a CASE statement. From that loop all other routines get called. Drawing your geometry is only done when the kResetObj event is issued. There are only a few events you need to look for. The reset object event, kResetObj (3) is the event where you draw should almost everything. Actually it deletes all existing geometry, then redraws it again based on the state of your parameters. Like a Modern Dialog, there is a setup event, kObjOnInitXProperties (5), where you do setup stuff, and a button event, kObjOnObjectUIButtonHit (35) where you do stuff when buttons get pressed. If you've ever programmed a Modern Dialog, this works in a very similar fashion. I'm sure this is still too vague. Other people may describe it better than I, so take two aspirin and write back, the fun has just begun. Raymond 1 Quote Link to comment
michaelk Posted May 20, 2020 Author Share Posted May 20, 2020 It's making more sense. The main part of the program is now PROCEDURE Main; I've made a little progress. I got it to compile, but not do anything different than it was doing with FAR few lines of code. 🤣 Here are my next questions: result:= vsoAppendWidget(kWidgetButton, ButtonID_1, 'Do Something', 0); kWidgetButton is 12 Is the next argument for the Button ID any LONGINT or does it refer to the place it occupies in the OIP? The string is the button label. What is the last argument? The example code in the function reference has it set to 0. Changing it to other values I tried didn't make a difference. In any case, the script below produces no button. ******************************* eventMessage := vsoStateAddCurrent(PIHan, eventMessage); What does this do? Not sure what the LONGINTs for vsoGetInfo(theEvent, eventMessage); refer to. ******************************* Thanks for all your help. I was NOT expecting to have compiled code tonight! This runs. But no button. Procedure Test; {Badly Scripted by Michael Klaers.} CONST kCR = CHR(13); kObjOnInitXProperties = 5; kObjOnWidgetPrep = 41; kObjOnAddState = 44; kObjOnObjectUIButtonHit = 35; kResetEventID = 3; kObjXPropHasUIOverride = 8; kParametricEnableStateEventing = 590; kObjXPropAcceptStates = 18; kWidgetButton = 12; ButtonID_1 = 1; VAR PIHan, PIRecHan, PIWallHan : HANDLE; resultStatus, result : BOOLEAN; PIName : STRING; eventMessage,theEvent : LONGINT; {---------------------------------------------------------------------} PROCEDURE InitPIO; BEGIN {This tells VW to let the object decide what goes onto the Object Info palette.} result:= SetObjPropVS(kObjXPropHasUIOverride, TRUE); result := SetObjPropVS(12, TRUE); {kObjXHasCustomWidgetVisibilities} SetPrefInt(kParametricEnableStateEventing, 1 ); result := SetObjPropVS(kObjXPropAcceptStates, TRUE); {Now we manually add the "normal" parameters...} {One way is to use this single call to add all of the existing parameters.} result:= vsoInsertAllParams; {Finally, we add the button.} result:= vsoAppendWidget(kWidgetButton, ButtonID_1, 'Do Something', 1); END; {---------------------------------------------------------------------} PROCEDURE WidgetPrep; BEGIN vsoSetEventResult( -8 {kObjectEventHandled} ); END; {---------------------------------------------------------------------} PROCEDURE GetStateChange; BEGIN eventMessage := vsoStateAddCurrent(PIHan, eventMessage); END; {---------------------------------------------------------------------} PROCEDURE ButtonHandler; BEGIN CASE eventMessage OF ButtonID_1: BEGIN END; END; {CASE} END; {---------------------------------------------------------------------} PROCEDURE Main; BEGIN BeginXtrd(0,pExtrusion); RectangleN(0, 0, 1, 0, pWidth, pHeight); EndXtrd; {Message( 'pWidth: ',pWidth,kCR, 'pHeight: ',pHeight,kCR, 'pExtrusion: ',pExtrusion ); } END; {---------------------------------------------------------------------} BEGIN resultStatus := GetCustomObjectInfo(PIName, PIHan, PIRecHan, PIWallHan); vsoGetEventInfo(theEvent, eventMessage); CASE theEvent OF {User has single-clicked the object's icon.} kObjOnInitXProperties: InitPIO; kObjOnWidgetPrep: WidgetPrep; kObjOnAddState: GetStateChange; {User has clicked a button in the Object Info palette.} kObjOnObjectUIButtonHit: ButtonHandler; {Object reset has been called.} kResetEventID: Main; END; {Event case} END; RUN(Test); PS. why are the tab stops all messed up when I copy/paste code? 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.