Jump to content
Developer Wiki and Function Reference Links ×

Formatting OIP?


Recommended Posts

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.

Link to comment

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. :-)

Link to comment

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 by MullinRJ
Link to comment

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.

Link to comment

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?

Link to comment
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. ;-)

Link to comment

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.

Link to comment

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 by JBenghiat
Link to comment

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

 

Link to comment
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 😀

Link to comment
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

  • Laugh 1
Link to comment

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

  • Like 1
Link to comment

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

 

Link to comment

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. 

Link to comment

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 :-)

 

Link to comment

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?

Link to comment

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

 

 

  • Like 1
Link to comment

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?

Link to comment

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...