Jump to content

MullinRJ

Member
  • Content Count

    1,464
  • Joined

  • Last visited

Posts posted by MullinRJ


  1. Hi Michael,

       I didn't read through all of your code, but I do see a mistake in your first FOR loop. You only index to the next layer inside an IF statement testing for Design Layers. If the layer is not TYPE 1 (a Design Layer) you don't index through the Sheet Layers. You need to move the NextLayer() call to the bottom of the FOR loop like this:

    For Counter := 1 to HowManyLayers DO
    BEGIN
    	LyrTypInt := GetObjectVariableInt(LayerInQuestion, 154);
    
    	{***************************************************************************}
    	{Layer type 1 is a design layer.  Ignore and go to the next layer.}
    	IF (LyrTypInt = 1) THEN
    	Begin
    	{	LayerInQuestion := NextLayer(LayerInQuestion);	}	{vvv  Move to bottom of FOR loop  vvv}
    		{ Do Design Layer stuff here, or remove this IF if there is nothing to do }
    	End;		{If LyrTypInt is 1 (i.e. Design Layer)}
    
    	{***************************************************************************}
    	{Now we're in business.  Layer type 2 is a Sheet Layer}
    
    	IF (LyrTypInt = 2) THEN
    	Begin
    		ShtLayer[SheetsOnly] := LayerInQuestion;
    		ReportStr := Concat(ReportStr, GetName(ShtLayer[SheetsOnly]));
    		SheetsOnly := SheetsOnly + 1;
    	End;		{If LyrTypInt is 2 (i.e. Sheet Layer)}
    
    	LayerInQuestion := NextLayer(LayerInQuestion);		{****  It works best here  ****}
    END;		{1 to number of layers}	

     

    HTH,

    Raymond

     


  2. @relume ,

    Use:

    def  vs.ConvertToPolygon(h, resolution):

       return HANDLE

     

    Setting integer "resolution" higher creates more vertices.

     

    Notes:

    1) This function creates a duplicate object, so you don't have to duplicate it beforehand.

    2) If the original is selected, the duplicate will be selected, and vice versa.

    3) Magic numbers for the resolution are powers of 2.

     

    Raymond


  3. Hi Dave,

       Use SetName().

     

       If you have a symbol named 'Bob', the following will change the symbol's name to 'Larry', where 'H' is a variable of type HANDLE;

     

    H := GetObject('Bob');
    SetName(H, 'Larry');
     

    HTH,

    Raymond

    • Like 1

  4. Hello @G.B ,

       It is a problem with the "Interior Elevation Marker" tool. Does the problem repeat after you restart VW? If so, someone at the factory will have to answer your questions. I tried it on my licensed version of VW and it works. It could be related to the evaluation version, or it could be a problem with the tool and the way you are using VW (setup, units, view, etc.). It's hard to tell from here.

     

       Restart and write back.

     

    Raymond

     


  5. Hi Andrew,

       I adapted Pat's script to also set the Point Size of the text. This will set all objects to the same size which could make some documents look weird. You could break the script into two scripts to give you more control. To just set the point size, comment out (or remove) the SetTextFont() command.

     

       You can also make the script work only on SELECTED text objects only by changing the  "& ALL"  to  "& SEL"  in the ForEachObject() command near the bottom.

    PROCEDURE ChangeAllFonts;
    { Changes the font of all text on a drawing to the selected font }
    { Traverses into groups, symbols and viewport annotations }
    
    { January 6, 2012 }
    { © 2012, Coviana, Inc - Pat Stanford pat@coviana.com }
    { Licensed under the GNU Lesser General Public License }
    
    { 17 February 2020 - R. Mullin }
    { Added ability to set the Point Size of all text objects at the same time. }
    { Same GNU License as above. }
    
    VAR
    	Hd :Handle;
    	Font :String;
    	FontID, dInt :Integer;
    	PointSize, dReal	:Real;
    
    	Procedure ChangeFont(Hd:Handle);
    	Begin
    		if (GetTypeN(Hd) = 10) then begin
    			SetTextFont(Hd, 0, len(gettext(Hd)), GetFontID(Font));
    			SetTextSize(Hd, 0, len(gettext(Hd)), PointSize);
    		end;
    	End;
    
    BEGIN
    	Font := 'Arial';
    	FormatTextDialog(Font, dInt, PointSize, dInt, dReal, dInt, dInt, 60);	{ correct Point Size ommission - RJM 7 Oct 2020 }
    	ForEachObject(ChangeFont, INSYMBOL & INOBJECT & INVIEWPORT & ALL);
    END;
    Run(ChangeAllFonts);

     

    Raymond

     

    • Like 1

  6. Hello Benedick,

       Try this, instead of setting "Make All Attributes By Class", set the attributes to "Remove By Class Settings" before you create any text. Also, make sure the "Use At Creation" check box (in the top left corner) is OFF in the "Edit Class(es)" dialog. Leave the "Text Style - Use At Creation" checkbox ON. Lastly, use a different color for the Text Style than for the Class Pen Color. That way you'll see which color is making it through.

     

       Whatever the deal is with this feature, it is not designed to work well. I only just now discovered that using "Make All Attributes By Class" would improve the expected response, and that was on a whim. I've messed with this feature several times over the last few months and had the same confusion you're experiencing. It seems that using "Make All Attributes By Class" comes in after everything is created and overwrites the Pen Color, and possibly the Fill Color, too. I haven't tested the class Fill Color yet.

     

       WAD or not, this feature needs improvement. This is anything but intuitive.

     

    Raymond

    • Like 1

  7. Hi @AllisonCPA ,

       Could you elaborate a little more? I just drew 5 dimension objects in a Viewport Annotation and I was able to select them all using Object Type as the criteria. It also works if I only have Font as my lone criteria. What option(s) do you have selected for the tool? Do you get a dialog stating "The selected activation options can't be used for this object."?

     

    Raymond


  8. Thanks, Josh. @JBenghiat

       Advice heeded. I've wanted to do this in the past only for investigative purposes, but never for anything practical. On one occasion (only one) I stored the string values of handles so I could sort them and do a binary search of the Symbol Library. It worked perfectly. Thanks for the warning.

     

    Raymond


  9. @twk ,

       The Always Execute Scripts button will reset when VW re-launches, so you are not giving up your ability to block script execution forever. That was my biggest concern when I first ran into this dialog. Was afraid to press the button. I don't know if this is documented, I found it out by trial and error and I was very pleased to see the function resets at the end of a VW session. I feared it would open the door for scripts to execute forever. On a tangential note: it would be extremely helpful for this dialog to have a Help window to explain the scope of each button's functionality. 

     

    HTH,

    Raymond


  10. 14 hours ago, Pat Stanford said:

    ... return the Handle to the object as a LongInt (or String) you could then grab the data from that cell and convert back to a handle.

     

    I wish this were possible. To the best of my knowledge you can only look at a handle value, but you cannot cast the value back to a handle. On a similar track, since the VW App went to 64 bit, I've seen some handle values (not all) that exceed the Longint range. There is no 64-bit integer type in VS that would accommodate really large integers.

     

    @Pat Stanford if I'm wrong and you do know of a way to convert values back to a handle, would you mind sharing. I could use this.

     

    Thanks,

    Raymond


  11. And I am hoping you figure it out Pat, so I may ask you questions. If you want a another blind minion to walk with you through the maze, I'll offer my directionless services. I've often wanted to do the same.

     

    Raymond


  12. @sully8391 ,

    Although Pat's advice is pretty much spot on, let me lessen the gravity of it a little. If you're tenacious, patient, and inquisitive, you can probably have your dialog up and running within a day or two. Once you get the first one done all the hurdles come down a bit. And as with all other disciplines: practice makes perfect, and failure is mandatory.

     

    Modern Dialogs are constructed in three parts. The first part can be a procedure, or inline code, that contains all of the statements that define the dialog, while the second part is a procedure containing code to run the dialog (the event loop). The last part is typically one or two lines in the main program that call the dialog code.


    Here is a short example in Pascal (sorry, I think faster this way.) Translating it shouldn't be too hard. The CASE statement in the Event Loop can be replaced with an "if / elif" tree.

    PROCEDURE DialogExample;
    { This is a relatively simple example of a Modern Dialog that converts temperatures. }
    { It has 6 fields, 3 static text labels, and 3 Edit Text fields. }
    { It also as a basic event loop to control the dialog when it is running. }
    { Every key press and every click generates an event which is handled by the event loop. }
    { Events can be handled or ignored as you see fit. }
    { 03 Jan 2020 - Raymond J Mullin - Use or modify to your wildest whims. }
    
    VAR
    	dlogID, dlogResult :Longint;
    
    	function BuildDialog :Longint;
    	{ This dialog returns the Dialog ID and has 2 parts. }
    	{ 1) Code that creates the dialog elements. }
    	{ 2) Code that arranges the elements in the dialog, either below or to the right of the previous element. }
    	{ This function could also contain two more parts... }
    	{ 3) Code that controls the alignment of the elements to each other. This is optional. }
    	{ 4) Code that defines Help Strings when the cursor hovers over dialog elements. This is also optional. }
    	Var
    		dlogID :Longint;
    	Begin
    		{ This is the most common way to start. }
    		dlogID := CreateLayout('Temperature', False, 'OK', '');	{ No HELP text, OK button and no Cancel button }
    
    		{ create 1st element at 4 or higher, it's arbitrary. I usually start at 10 for static text and 20 for the edit fields }
    		CreateStaticText(dlogID, 10, 'Fahrenheit (°F)', 13);	{ Name says it all - it's static (usually) }
    		CreateStaticText(dlogID, 11, 'Celsius (°C)', 13);	{ the last number is its width in characters (approximately))... }
    		CreateStaticText(dlogID, 12, 'Kelvin (°K)', 13);	{   ...average characters, not the skinny ones }
    		CreateEditReal(dlogID, 20, 1, 32, 16);			{ a field you can type in }
    		CreateEditReal(dlogID, 21, 1, 0, 16);			{ another field you can type in }
    		CreateEditReal(dlogID, 22, 1, 273.15, 16);		{ a third field you can type in, or write to }
    
    		{ The next section arranges the dialog elements either right or down }
    		SetFirstLayoutItem(dlogID, 10);				{ everything else is anchored to this item }
    		SetBelowItem(dlogID, 10, 11, 0, 0);			{ item 11 goes below item 10 }
    		SetBelowItem(dlogID, 11, 12, 0, 0);			{ item 12 goes below item 11 }
    		SetRightItem(dlogID, 10, 20, 0, 0);			{ item 20 goes to right of item 10 }
    		SetRightItem(dlogID, 11, 21, 0, 0);			{ item 21 goes to right of item 11 }
    		SetRightItem(dlogID, 12, 22, 0, 0);			{ item 22 goes to right of item 12 }
    
    		BuildDialog := dlogID;					{ this is your "return" value in Python }
    	End;		{ BuildDialog }
    	
    	
    	procedure DialogEventLoop(var item :Longint; data :Longint);	{ this procedure is always defined this way }
    	{ This is the Event Loop. For every event (keystroke or click) this procedure is executed }
    	Var
    		Fahr, Cel, Kel :Real;
    	Begin
    		{ use this next line for debug to see what events are called and when. Comment it out when done. }
    		{	AlrtDialog(concat('item= ', item, '   data= ', data));	}
    		
    		{ if you want something to happen when an event occurs, put the item # (aka, the event #) in the case statement }
    		case item of
    			SetupDialogC : begin
    				{ initialize all dialog values here }
    				{ this event only gets called once at the beginning of dialog execution }
    			end;		{ SetupDialogC }
    			
    			1: begin		{ OK button was pressed }
    				SysBeep;	{ BEEP for joy - it worked }
    			end;		{ 1 }
    				
    			2: begin		{ code for CANCEL button was pressed, if you had one (but you don''t) }
    			end;		{ 2 }
    			
    			20: begin
    				if GetEditReal(dlogID, 20, 1, Fahr) then begin
    					Cel := (Fahr-32) * 5/9;
    					SetEditReal(dlogID, 21, 1, Cel);
    					SetEditReal(dlogID, 22, 1, Cel+273.15);
    				end;		{ if }
    			end;		{ 20 }
    			
    			21: begin
    				if GetEditReal(dlogID, 21, 1, Cel) then begin
    					Fahr := Cel * 9/5 + 32;
    					SetEditReal(dlogID, 20, 1, Fahr);
    					SetEditReal(dlogID, 22, 1, Cel+273.15);
    				end;		{ if }
    			end;		{ 21 }
    				
    			22: begin
    				if GetEditReal(dlogID, 22, 1, Kel) then begin
    					Cel := Kel - 273.15;
    					SetEditReal(dlogID, 21, 1, Cel);
    					SetEditReal(dlogID, 20, 1, Cel*9/5+32);
    				end;		{ if }
    			end;		{ 22 }
    		end;		{ case }
    		
    		{ the dialog exits when (item == 1) or (item == 2) at the end of this procedure. }
    		{ this procedure will "return item" in Python }
    	End;		{ DialogEventLoop }
    
    BEGIN		{ main program }
    	{ This line builds the dialog and returns its ID. }
    	dlogID := BuildDialog;
    	
    	{ VerifyLayout() checks the dialog, if it passes then it runs the dialog }
    	{ in the next statement with the Event Loop specified. }
    	if VerifyLayout(dlogID) then
    		dlogResult := RunLayoutDialog(dlogID, DialogEventLoop);
    		
    	{ Check the "dlogResult", 1 = OK; 2 = Cancel. Proceed accordingly. }
    END;		{ main program }
    Run(DialogExample);

     

    Keep the Function Reference close. Even with all its shortcomings it is still an invaluable aid. The next best reference source is this forum. Write back when you have more questions and @Pat Stanford will most likely beat me to a response. 😉

     

    Raymond

     

     

    • Like 1

  13. Hi @sully8391 ,

       The "vs.BeginDialog()" and "vs.EndDialog()" commands are part of the Classic Dialog set which were discontinued in VW 2010. They continued to run for several years after that, but the shift was toward the Modern Dialog commands introduced in VW 9. They are the currently supported dialog calls.

     

       Consult the Developer Wiki at http://developer.vectorworks.net/index.php/VS:Function_Reference or the HTML Script Reference that ships with the application in the VWHelp folder @ "VWHelp/Script Reference/ScriptFunctionReference.html". Start with "CreateLayout()" and peruse that section. There are several examples in that section. If you're looking at the HTML Reference,, search for "Example" when you're in the "Modern Dialog" area.

     

       Simple dialogs are pretty easy to layout by hand. More complicated dialogs might require the use of the Dialog Builder. Good luck and write back.

     

    Raymond

     

    PS - Please create a signature with a basic description of your computer and VW version. It will help us answer your questions a little faster and possibly allow us to offer advice that is pertinent to your setup.

    • Like 1

  14. @hagemeijer ,

       If you have a handle to an ARC (or CIRCLE) object, you can't GET the radius directly, but you can calculate it:

          GetArc(H, Start, Sweep);
          Radius := hPerim(H) / Deg2Rad(Sweep);

    where "H" is the handle to the object.

     

    OR as Josh says:

        Get2DPt(H, 1, Pc.x, Pc.y);
        Get2DPt(H, 2, P1.x, P1.y);
        Radius := Norm(P1-Pc);

    where "H" is a Handle to the object and Pc and P1 are Vectors.

     

    Raymond


  15. Sam,

       I assume from your description you want to preserve the "L" shape. If the objects are drawn in a way that adjacent objects are next to each other in the stacking order, AND each object's BBox overlaps (or touches) the BBoxes next to it, you could create a Polygon for each object's BBox and add the Polygons together with the AddSurface command. When you are done they will all be connected but not necessarily in a "pretty" fashion. However, if there are gaps between the individual BBoxes, this approach won't work. 

     

       Any approach you take that will work will be very dependent on the configuration of shapes before you start.

     

    Raymond


  16. Sam,

       A new Group is always created on top of the stacking order, so if you are on a design or sheet layer, LActLayer will return a handle to a new Group. If you are drawing inside a container, you can use the Waldo method that Pat mentioned. With the handle you can then get the Group's BBox.

     

    Raymond

     

     


  17. Samantha,

       "Sanctuary Floor Plan.vwx" and "Eaton Hall Floor Plan Renovations.vwx" do not appear to be VW files when opened with a text editor. They both start with headers that look like they may be AutoCAD files.

     

    "Sanctuary Floor Plan.vwx" starts with "AC1018" and,

    "Eaton Hall Floor Plan Renovations.vwx" starts with "AC1021".

     

    Change their file extensions to".dwg" and open them with AutoCAD, or import them into the program of your choice (that reads DWG files).

     

    The other two files are VW files. I'll let someone with better experience in doing DWG Exports help you there.

     

    Raymond


  18. Hi Sam,

       That's the change I thought would have the most impact on your problem, yet I'm not exactly sure what vsoStateAddCurrent() does under the hood. It's one of those things that works and I have not spent any time trying to figure out more about it. 

     

       As you surmised, that is the only statement I have under that event in all of my PIO's. If you ever find another example on how it's used differently, please let me know.

     

    Happy New Year,

    Raymond


  19. Hi Sam,

       I have only two suggestions which I've marked with comments in your code. Try them one at a time to see if either of them works. Then try them together if neither works. After that, I'm out of ideas. 

     

    	GetVersion(Major,Minor,Maintenance,Platform);
    	GetUnits(Fraction,Display,Format,UPI,UnitName,SquareName);
    	Result := GetCustomObjectInfo(PIOName,PIOHan,PIORec,WallHdl);
    	vsoGetEventInfo(EventMessage, MsgData);
    	OK := GetLocalizedPlugInName(PIOName,LocalPIOName);
    	IsNew := IsNewCustomObject(PIOName);
    
    	BoxBottom := 3 * UPI;
    	CASE EventMessage OF
    		kResetEventID:		{ 3 }
    			BEGIN
    { MOVE vsoStateGetParamChng LINE FROM EVENT 44 TO EVENT 3 }
    				ParamChangeFlag := vsoStateGetParamChng(PIOHan,ParamChangeWidget,ParamChangeIndex,OldParam);
    
    				GetSymLoc3D(PIOHan, PIOX, PIOY, PIOZ);
    				ParamChangeFlag := vsoStateGetParamChng(PIOHan,ParamChangeWidget,ParamChangeIndex,OldParam);
    			AlrtDialog(concat('Event 3, kResetEventID  triggered', kcr,
    				'ParamChangeFlag = ', ParamChangeFlag, kcr,
    				'EventMessage = ', EventMessage, kcr,
    				'MsgData = ', MsgData));
    				IF (ParamChangeFlag=True) THEN 
    					BEGIN
    						{Do Stuff}
    					END;	{IF (ParamChangeFlag=True) }
    
    				ParamChangeTrig := FALSE;
    				PIOChangeTrig := FALSE;
    
    				SetUp;
    				MainConstruct;
    				vsoStateClear(PIOHan );
    			END;	{ 3 - kResetEventID }
    
    		kObjOnAddState:		{ 44 }
    			BEGIN
    				{This event is needed to track state changes}
    { CHANGE EventMessage TO MsgData (x2) in following line }
    				MsgData := vsoStateAddCurrent(PIOHan, MsgData);
    			AlrtDialog(concat('Event 44, kObjOnAddState  triggered', kcr,
    				'ParamChangeFlag = ', ParamChangeFlag, kcr,
    				'EventMessage = ', EventMessage, kcr,
    				'MsgData = ', MsgData));
    			END;	{ 44 - kObjOnAddState }
    
    		kObjOnInitXProperties:		{ 5 }
    			BEGIN
    				Result := SetObjPropVS (kObjXPropHasUIOverride,True);
    				Result := SetObjPropVS(12, TRUE); {kObjXHasCustomWidgetVisibilities}
    				
    				SetPrefInt(590, 1 );		{kParametricEnableStateEventing}
    				Result := SetObjPropVS (18,TRUE);    {kObjXPropAcceptStates}
    				
    				Result := vsoInsertAllParams;
    				Result := SetObjPropVS(kObjXPropDataNameDisabled, TRUE);{kObjXPropDataNameDisabled}
    				SetPrefInt (590,1);		{varParametricEnableStateEventing,ResetStatesEvent}
    			END;	{ 5 }
    
    		kObjOnWidgetPrep:    { 41 }
    			BEGIN
    				WidgetPrep;
    			END;		{ 41 - kObjOnWidgetPrep }
    
    	END;	{ case }    

     

    Have a Merry Xmas, (or holiday of your choice),

    Raymond


  20. Hi Sam,

       Do you have your call

               ParamChangeFlag := vsoStateGetParamChng(PIOHan, ParamChangeWidget, ParamChangeIndex, OldParam);

    in Event 3 (kResetEventID)?

     

    Everything else you have in your kObjOnInitXProperties event is what I have, though you call 

              SetPrefInt(590, 1 );    {kParametricEnableStateEventing}
    twice, where I only call it once at the end of Event 5 (kObjOnInitXProperties). I doubt it makes a difference, but I don't know.

     

       Also, do you have "Event-Based" checked in the "Edit Plug-in Definition" dialog, Options tab?

     

       Lastly, I execute these 3 commands first before I enter the CASE statement testing the events:

          result := GetCustomObjectInfo(PIOName, PIOHand, recHand, wallHand);
          IsNew := IsNewCustomObject(PIOName);
          vsoGetEventInfo(theEvent, theMessage);

    The first line makes sure the PIOHand variable is valid, and the third line returns the current EVENT.


    Raymond

 

7150 Riverwood Drive, Columbia, Maryland 21046, USA   |   Contact Us:   410-290-5114

 

© 2018 Vectorworks, Inc. All Rights Reserved. Vectorworks, Inc. is part of the Nemetschek Group.

×
×
  • Create New...