Jump to content
Developer Wiki and Function Reference Links ×

PIO Button in OIP


Recommended Posts

Based on the info found on this forum and on the VectorLab-website, I've managed to create a PIO (Plug In Object) with a custom button in the OIP (Object Info Palette).

I would like to use this button to create a legend in a custom Titleblock + geometry

Workflow: push the button -> mark some boolean items in a Custom Dialog Box -> close the Dialog -> result

On the VectorLab-website there is a note about creating geometry inside or outside the PIO, no problem up to here.

How can we create a geometry inside the PIO after pushing the button.

I can create the geometry after pushing the button, but then the geometry is created outside the geometry

and

I can create the geometry inside the PIO but then the button is rather useless as it's created by another part of the PIO-script.

Link to comment

If I understand you correctly:

- When pusing the button, you let the user select some options from a dialog that define ( a part) of the geometry inside your pio.

You can't do it directly as you think you can. As you've noticed you can draw stuff inside or outside your pio.

If you code something on the button click event it can't change the geometry of the pio, but it can change parameters.

You could make (in)visible parameters for those options and let the button dialog control those. The recalculation event can load these parameters and draw that geometry with these settings.

If these parameters are simple integers / text you might just work without the button and visible parameters instead..

What is the geometry you are talking about ?

Edited by hippothamus
Link to comment

I've opted the second option (I think):

Let the script look for hatches & line patterns in a ListBrowser and then display all the results in the legend using one simple standard Boolean parameter ('Show Legend').

This should also answer your question:

The geometry would consist of some hatched rectangles, some patterned lines, perhaps some symbols and some text explaining the forementioned.

Anyway the first option might answer the initial question: "How to create geometry inside a PIO by pushing a button".

The current Dialogbox appearing after pushing the button creates the desired geometry, but it does so outside the PIO.

If I understand you correctly, the button shouldn't control some Procedure(s) in the script, but should control parameters that control some Procedure(s) in the script.

Thanks.

Link to comment

Dimitri -

Assuming you've decided not to pursue DLegend (hat's off to Dieter for the simple elegance in that one), Dieter's advice about the button-click even writing to a hidden parameter (probably a Boolean) seems to be the best option. You could then use state change tracking to wait for any sort of change to that parameter - and then create the geometry if/when that happens.

Interesting question...

Link to comment

Probably trying too much too fast, but ...:

How can I reset the PIO and keep the changes executed by the button-click-event?

If I click the button that opens a Custom Dialog Box - containing some boolean items - and change some of these items, then after I close this dialog box, the PIO is being reset bij the kResetObject-event.

By resetting the object all the boolean items go back to their initial vanlue (seems logical), but how can I maintain these changes in the memory of the PIO?

(Meanwhile, I guess I'd better start looking up on how to store stuff in a Dynamic Array ...)

Edited by DeSignature
Link to comment

Also, the PIO does not automatically reset after a button event (the event may be setting a preference or displaying a version number, for example). You have to issue a ResetObject command.

Also by design, button interaction is only with the last selected object. If you want the dialog to apply to all selected instances of that PIO, you have to apply the change to selected PIOs with ForEachObject or FEOInList.

HTH,

Josh

Link to comment

@ Mr. Dunning,

I wasn't very clear on that one.

The values aren't lost when exiting the Dialog, but do seem to be lost when re-entering the Dialog.

@ Mr. Geerts,

For now I'm trying to use the SetRField to control 1 hidden parameter, I was hoping that this parameter could control all the different booleans from the Dialog, but apparently, "that kite won't go up", for now.

@ Mr. Benghiat,

Could it be that the Dialog behind the Button of the PIO is being reset after exiting, not the PIO itself?

The PIO (and its different button-parts) should keep it's properties per Sheet-Layer, but I guess that criterium wouldn't be that hard to script ...

Link to comment
@ Mr. Dunning,

I wasn't very clear on that one.

The values aren't lost when exiting the Dialog, but do seem to be lost when re-entering the Dialog.

You can keep the previous "choices" for each object, but again if you store them in hidden parameters. Running the dialog with these defaults.

@ Mr. Geerts,

For now I'm trying to use the SetRField to control 1 hidden parameter, I was hoping that this parameter could control all the different booleans from the Dialog, but apparently, "that kite won't go up", for now.

You have some options there:

- create a hidden parameter for each boolean you need

- Create one hidden string var, containing all booleans (1,1,0,0,1). Create some functions exploding it into an array of booleans and code your dialog with this.

- Create a record containing field booleans

Link to comment

Well ... I've tried some different scenario's, but ...

Hope you don't me dropping some code of one them (Point Object):

PROCEDURE PIOButtonInOIP;
CONST
Reset_PIO			= 03;
PushIcon			= 05;
PushButton		= 35;
VAR
PIO_N				: STRING;
PIO_H, PIO_R, PIO_W		: HANDLE;
Event_Id, Event_Result	: LONGINT;
Dial_Id, Dial_Result		: LONGINT;
Result				: BOOLEAN;
Bool, Item				: ARRAY [1 .. 2] OF BOOLEAN;

PROCEDURE Dialog;
	PROCEDURE DialogLayout;
	BEGIN
		Dial_Id	:= CreateLayout ('Custom Dialog Box', TRUE, 'OK', 'Cancel');
		CreateCheckBox (Dial_Id, 04, 'Draw Rectangle A');
		CreateCheckBox (Dial_Id, 05, 'Draw Rectangle B');

		SetFirstLayoutItem	(Dial_Id, 04);
		SetBelowItem		(Dial_Id, 04, 05, 0, 0);
	END;

	PROCEDURE DialogEvents (VAR Item: LONGINT; Data: LONGINT);
		PROCEDURE DialogSettings;
		BEGIN
			SetBooleanItem (Dial_Id, 04, FALSE);
			SetBooleanItem (Dial_Id, 05, FALSE);
		END;

		PROCEDURE EventOK;
		BEGIN
			GetBooleanItem (Dial_Id, 04, Bool [01]);
			GetBooleanItem (Dial_Id, 05, Bool [02]);
		END;

		PROCEDURE EventCancel;
		BEGIN
		END;
	BEGIN
		CASE Item OF SetupDialogC: DialogSettings;
			01: EventOK;
			02: EventCancel;
		END;
	END;

	PROCEDURE Bools;
	BEGIN
		IF Bool [01] = TRUE THEN NewField ('Bools', 'Item [01]', 'TRUE', 2, 1);
		IF Bool [02] = TRUE THEN NewField ('Bools', 'Item [02]', 'TRUE', 2, 1);

		IF GetCustomObjectInfo (PIO_N, PIO_H, PIO_R, PIO_W) THEN SetRecord (PIO_H, 'Bools');
		IF Bool [01] = TRUE THEN SetRfield (PIO_H, 'Bools', 'Item [01]', 'TRUE');
		IF Bool [02] = TRUE THEN SetRfield (PIO_H, 'Bools', 'Item [02]', 'TRUE');
	END;		
BEGIN
	DialogLayout;
	IF VerifyLayout (Dial_Id) THEN Dial_Result := RunLayoutDialog (Dial_Id, DialogEvents);
	Bools;
END;

PROCEDURE Geometry;
BEGIN
	IF GetCustomObjectInfo (PIO_N, PIO_H, PIO_R, PIO_W) THEN
	BEGIN
		IF GetRField (PIO_H, 'Bools', 'Item [01]') = 'TRUE' THEN Rect (0, -75, 100, -25);
		IF GetRField (PIO_H, 'Bools', 'Item [02]') = 'TRUE' THEN Rect (0, 25, 100, 75);
		Message ('Item [01]: ', Item [01], Chr (13), 'Item [02]: ', Item [02]);
	END;
END;
BEGIN
vsoGetEventInfo (Event_Id, Event_Result);
CASE Event_Id OF
	PushIcon: 		BEGIN
					Result	:= SetObjPropVS (08, TRUE);
					Result	:= vsoInsertAllParams;
					Result	:= vsoAppendWidget (12, 100, 'Button', 0);
				END;

	PushButton:	CASE Event_Result OF 
					100:	Dialog;
				END;

	Reset_PIO:		BEGIN
					Geometry;
				END;
END;
END;
RUN (PIOButtonInOIP);

The OIP tells me that Item [01] & [02] are TRUE, but the Message ( ) tells me the're FALSE, so I know there's something(s) fundamentally wrong.

(By the way, is anybody else experiencing some trouble with the 'Preview Post' on this forum while posting?)

Edited by DeSignature
Link to comment

A few things jump out.

In your dialog, DialogSettings sets the controls to FALSE no matter what. This is why your dialog doesn't seem to "remember" the values -- you need to fetch them from the PIO's parameters and set the controls accordingly.

You are making parameter access more difficult than it needs to be. The record to which we are referring is the PIO's parameter storage, which happens to have the same name as your PIO. In your case, PIO_N. The field names are the universal parameter names (the first field in your parameter setup). You don't have to attach records and create fields. BTW, if you did, you should check if the record is already attached before assigning it and if it exists before creating new fields. This may also be causing you to overwrite data.

You can access the PIO's parameters with SetRField and GetRField as if it were a regular record format. So, if you wanted to write data to a parameter called Bool_1, you could issue:

SetRField ( PIO_H, PIO_N, 'Bool_1', Concat( item[1] ));

Note I use Concat() to convert the boolean to a string. This means you can set the value without using IF / THEN.

To retrieve the value, you can use GetRField, but for your purposes your are best off just getting the parameter constant, which in my example would be PBool_1. The type automatically matches the parameter type, so you can just say item[1] := PBool_1. No need to worry about if the field retrieves as True, TRUE, or true. You can also just use the constant directly:

IF PBool_1 THEN...

The P Parameter value is essentially a constant for the run of the script, so if your script possible changes and then uses the value, set a variable to the PParam value and reference the variable for the rest of your script.

Also, in your Geometry procedure, you don't assign a value to item[1] or item[2], so they will always return false in your message.

HTH,

Josh

Link to comment

Josh,

What if one doesn't know how many parameters will be used in the PIO?

Since you have to define (i. e. name) all the __parameters in the parameter setup, you should know - at least - how many parameters will be used, which I don't.

That is why I tried to write them to a Record, because I think (so, I'm not sure) that it's possible to write an undefined number of fields to a record.

I think this is what Hippothamus suggested.

For now, I'll experiment a bit with your - very helpful - suggestions and see where it takes me.

Thanks.

Link to comment

If you want to keep the previously chosen data yes you'll need to store it somewhere. Depending on the size you could just stick with one parameter, a record, xml, memory

If you don't need to keep the data its way easier. You can even create a variable dialog layout depending on your object. Dynarrays are your friend.

I've created a dialog that lets me edit a chosen object. It shows "depending on the selected object" X tabs with the record names contaning Y fields from that record. All can be edited accordingly and depending on the record field type, the parameter is a string, int, bool etc...

Link to comment

This should be a bit better:

PROCEDURE PIOButtonInOIP;
CONST
Reset_PIO	= 03;
PushIcon	= 05;
PushButton	= 35;
CR			= Chr (13);
VAR
PIO_N					: STRING;
PIO_H, PIO_R, PIO_W		: HANDLE;
Event_Id, Event_Result	: LONGINT;
Dial_Id, Dial_Result	: LONGINT;
Result					: BOOLEAN;
Bool, Item				: ARRAY [1 .. 2] OF BOOLEAN;

PROCEDURE Dialog;
	PROCEDURE DialogLayout;
	BEGIN
		Dial_Id	:= CreateLayout ('Custom Dialog Box', TRUE, 'OK', 'Cancel');
		CreateCheckBox (Dial_Id, 04, 'Draw Rectangle A');
		CreateCheckBox (Dial_Id, 05, 'Draw Rectangle B');

		SetFirstLayoutItem	(Dial_Id, 04);
		SetBelowItem		(Dial_Id, 04, 05, 0, 0);
	END;

	PROCEDURE DialogEvents (VAR Item: LONGINT; Data: LONGINT);
		PROCEDURE DialogSettings;
		BEGIN
			SetBooleanItem (Dial_Id, 04, Bool [1]);
			SetBooleanItem (Dial_Id, 05, Bool [2]);
		END;

		PROCEDURE EventOK;
		BEGIN
			GetBooleanItem (Dial_Id, 04, Bool [1]);
			GetBooleanItem (Dial_Id, 05, Bool [2]);

			NewField ('Bools', 'Item [1]', Concat (Bool [1]), 2, 1);
			NewField ('Bools', 'Item [2]', Concat (Bool [2]), 2, 1);

			IF GetCustomObjectInfo (PIO_N, PIO_H, PIO_R, PIO_W) THEN
			BEGIN
				SetRField (PIO_H, 'Bools', 'Item [1]', Concat (Bool [1]));
				SetRField (PIO_H, 'Bools', 'Item [2]', Concat (Bool [2])); 
			END;
		END;

		PROCEDURE EventCancel;
		BEGIN
		END;
	BEGIN
		CASE Item OF SetupDialogC: DialogSettings;
			01: EventOK;
			02: EventCancel;
		END;
	END;
BEGIN
	DialogLayout;
	IF VerifyLayout (Dial_Id) THEN Dial_Result := RunLayoutDialog (Dial_Id, DialogEvents);

	IF GetCustomObjectInfo (PIO_N, PIO_H, PIO_R, PIO_W) THEN
	Message ('Bool [1] = ', Bool [1], CR, 'Bool [2] = ', Bool [2], CR, CR, 'Item [1] = ', GetRField (PIO_H, 'Bools', 'Item [1]'), CR, 'Item [2] = ', GetRField (PIO_H, 'Bools', 'Item [2]'));
END;

PROCEDURE Geometry;
BEGIN
	IF GetCustomObjectInfo (PIO_N, PIO_H, PIO_R, PIO_W) THEN
	BEGIN
		Record (PIO_H, 'Bools');
		IF GetRField (PIO_H, 'Bools', 'Item [1]') = 'True' THEN Rect (0,  0, 20, 10);
		IF GetRField (PIO_H, 'Bools', 'Item [2]') = 'True' THEN Rect (0, 15, 20, 25);
	END;
END;
BEGIN
vsoGetEventInfo (Event_Id, Event_Result);
CASE Event_Id OF
	PushIcon: 	BEGIN
					Result	:= SetObjPropVS (08, TRUE);
					Result	:= vsoInsertAllParams;
					Result	:= vsoAppendWidget (12, 100, 'Button', 0);
				END;

	PushButton:	CASE Event_Result OF
					100:	Dialog;
				END;

	Reset_PIO:	Geometry;
END;
ResetObject (PIO_H);
END;
RUN (PIOButtonInOIP);

This creates geometry inside the PIO after the PushButton-Event.

I'm sure the code is liable for improvements, so if anyone would care to make the efford, please do so (again ...)

Thanks for all the suggestions.

Next question/topic will be about storing the data in the 'Item [ ]-fields'.

@Hippothamus:

Before calling these DynArrays my friend, I guess I'll have to get acquainted to them ...

Edited by DeSignature
Link to comment

Dimitri -

This might be tool little/too late, but did you consider at-all something along these lines:?

- Create a hidden OIP parameter for each of the given data types/groups you're needing to pass in and out of the dialog - but all being Sting types.

- When you click "OK" in the dialog, all of the related field values are collects, concatenated into a single string with a known divider in-between each, and that string is then written to the given hidden parameter.

- Your "main" code then parses out the individual values and uses them as-needed. (Same is true when the dialog is created.)

That way, no extra records are created and it is relatively easy to "watch" the data being passed back-and-forth (AlrtDialog/Message and "P" values).

Just a thought.

Link to comment

Of course I didn't, but I do believe this is another option Hippothamus suggested in his post from 05/20.

In the actual PIO (of which this code is just a part) there is already a record being created, I was able to integrate these fileds into that record, so stricktly spoken there is no extra record being created.

Considering there might easily about 20 booleans being created in this part:

_ Is there a major disadvantage in using records?

_ Is there a limit to the number of fields?

I was hoping to keep a clear view by using different (Dyn)Arrays.

And by the way: these suggestions are never too late, there are no lives at stake, I'm just in an everlasting learning-proces, so the're always welcome.

Link to comment

We might need a little more information on your end goal to be of more help. In general, if you need to store a variable number of data items, I would take Andy's suggestion of concatenating the data into a single string for storage.

I see you're using 2014. You may want to look at shifting your development to Python. That will give you access to Python's excellent built-in list functions, including sorting and iterating. In addition, you can use eval() and repr() to effortlessly move between an array of data and a string for storage.

-Josh

Link to comment

Actually, you can store more than 256 characters -- you just need to access the field with Set/GetRField. Using the PParamName constant or any of the UI access methods (e.g. Obj Info or Paramater setup dialog) will truncate the field.

That was an added feature a few versions ago. VW 2011?

-Josh

Link to comment

The endgoal would be a drawing-border with titleblock, since VectorWorks 2014-Fundamentals no longer supports this - fairly handy - bluid-in plugin anymore

(Why would someone using Fundamentals not use the remarks & revisions part?)

Actualy, it was no longer supported since VW 2009 or 2010, but with some basic "trickery" I was able to use older plugin.

Due to some obsolete functions in that plugin it became problematic, so the general plan is a better (to my personal requirements), more complete drawing-border + titleblock plugin.

Some extra features would be:

_ a legend, based on the used hatches, linetypes and symbols (see code in this topic + some ListBrowser functions);

_ a firm-selector (in use);

_ better - metric - dimensions (in use);

_ better folding marks (Work In Progress (W. I. P.));

_ automatic field updates (drawing name, drawing date, drawing number of total number of drawings, ...) (in use)

_ Re-enabled remarks-section (another button in OIP, W. I. P);

_ Re-enabled revisions-section (another button i the OIP, W. I. P.);

_ ...

I've tried the Python-editor but encountered some - really irritating - problems:

When the editor displayed an error and the error was fixed correctly in the script, the editor kept displaying this error untill the the editor was restarted.

I must say: I haven't checked if this problem is solved by now (or maybe I missed something else, since I don't really know the Python-language).

Link to comment

Actually, you can store more than 256 characters -- you just need to access the field with Set/GetRField. Using the PParamName constant or any of the UI access methods (e.g. Obj Info or Paramater setup dialog) will truncate the field.

That was an added feature a few versions ago. VW 2011?

And you also need to declare the variable holding these values as DYNARRAY OF CHAR (2M char?), which is the default type for GetRField / SetRField. If the variable is declared as a STRING, then it will be truncated to 256 char because that is the maximum that a STRING can hold.

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