Jump to content


  • Content Count

  • Joined

  • Last visited

Everything posted by MullinRJ

  1. GioPet, The only way I know of that Layer(), or vs.Layer(), will create another numbered layer is if the name passed to the Layer() procedure is an empty string ( i.e., vs.Layer("") ) and that would occur if the handle variable 'item' is NIL. You should put a trap in your routine to test for 'item' being NIL. Here's one way, albeit an overly simple one. for item in ExtendedGroups: if (item != vs.Handle()): layHand = vs.GetLayer(item) layName = vs.GetLName(layHand) vs.Layer(layName) vs.SetSelect(item) vs.Ungroup() vs.NameClass(TargetClass) vs.Group() vs.DSelectAll() else: vs.AlrtDialog("item == NIL") Raymond
  2. GioPet, I am not sure how you got that code snippet to run, but the first line is not syntactically correct. layerName: 'Design Layer-1'; should read: layerName := 'Design Layer-1'; When your variable "layerName" has the value 'Design Layer-1', it will not create 'Design Layer-2' and it will either make 'Design Layer-1' active, or leave it active. BUT, if "layerName" has the empty string value '', it will create 'Design Layer-2'. My guess is that your variable "layerName" is uninitialized when you pass it to the LAYER() procedure. HTH, Raymond PS - be careful copying code from Safari (my browser), and potentially other browsers, so that you don't introduce INVISIBLE characters to your code. When I copied your two line example, and then copied pieces of it to embellish my answeer, I introduced about a dozen copies of unicode character 0xFEFF. This character is invisible, unless you know how to look for it. Always filter your code through a word processor that can show INVISIBLE characters.
  3. Correct. The first approach we discussed ignores the VW Preference entirely and forces a Fill Color and Pattern after the text is created. The second approach changes the preference to allow the current document Fill attribute to be applied when the text is created, then restores the preference to its original state. So either way you go, that VW Preference remains unchanged after the script has run. Raymond
  4. Hi @avictorgm , when I wrote my earlier replies, I was not at my best. I completely forgot about the VW Preference "Create text without fill". The Python call "vs.CreateText()" will create text with the document fill color and pattern IF the "Create text without fill" PREF is disabled, but if the preference is set, then the text background fill color and pattern are ignored, as if the document fill pattern were set to NONE. So, you could do as I suggested above, and force the fill after creating the text, OR, you could toggle the preference OFF before creating your text, and restore its setting after your text is created. Something like this: CreateTextWOFillPref = vs.GetPref(8) # save pref setting (boolean) vs.SetPref(8, False) # disable pref setting vs.CreateText(str("Motor ") + str(X1) + str(", ") + str(Y1)) vs.SetPref(8, CreateTextWOFillPref) # restore pref setting This code snippet will create TEXT using the document fill color and pattern and ignore the VW preference to "Create text without fill". If your code is working as you like it, there is absolutely no need to change anything. I am just trying to provide a more comprehensive answer. All the best, Raymond
  5. Hello @SLFY I don't know that I can add much to what was previously written, except to support what @orso b. schmid has written. Orso's advice is spot on. You definitely want to use ValidNumStr() to convert string data to numbers in document units. This function will also perform rudimentary arithmetic such that the string ' 1mm + 1" ' will evaluate to 1.03937 if the document is in INCHES or it will evaluate to 26.4 if the document is in mm. Note leading and trailing white spaces are ignored. This is indeed a very powerful function. LABELS must be declared and used (with the GOTO command) at the same level in the program. If you need to escape nested routines, then you will need to use a unique LABEL at each nestled level and use a GOTO at that level to jump to the end of the routine you are escaping. You will also need one, or more, global variables (boolean flags) to signal that you are coming out early. This way you can leapfrog all the way to the end of the main program, if that is your intention. Here's a quick example of 3 nested subroutines, each with a LABEL used to escape out to the next level.: PROCEDURE MAIN; LABEL 1; VAR gError :Boolean; procedure SubroutineA; LABEL 2; procedure SubroutineB; LABEL 3; BEGIN { SubroutineB } AlrtDialog('I am at 3 - Sub B entry'); { stuff that coud cause an error. } if gError then GOTO 3; { Do stuff here if no ERROR } AlrtDialog('I am at 4'); 3: AlrtDialog('I am at 5 - Sub B exit'); END; { SubroutineB } BEGIN { SubroutineA } AlrtDialog('I am at 2 - Sub A entry'); SubroutineB; if gError then GOTO 2; AlrtDialog('I am at 6'); { Do stuff here if no ERROR } { more stuff that coud cause an error. } if gError then GOTO 2; AlrtDialog('I am at 7'); { Do stuff here if no ERROR } 2: AlrtDialog('I am at 8 - Sub A exit'); END; { SubroutineA } BEGIN { MAIN } AlrtDialog('I am at 1 - MAIN entry'); SubroutineA; if gError then GOTO 1; AlrtDialog('I am at 9'); 1: AlrtDialog('I am at 10 - MAIN exit'); END; Run(MAIN); As written, this will execute with all Alert Dialogs counting up from 1 - 10. Add this line into the code to simulate ERRORS that will invoke the GOTO calls to escape out of nested sections. Try placing it at line 15 for starters. gError := True; { move this statment around to see how it affects execution. } Lastly, it should be possible to write PASCAL code entirely without GOTOs, but if they are working for you there is no need to rewrite anything. Use what works save the new stuff for a rainy day. I'm not sure if I helped you, but I hope this helps someone, somewhere. All the best, Raymond
  6. You're very welcome. A word of caution; using color calls like "vs.FillBack(4)" with a single argument that represents a palette position may produce unexpected results: in different files, on different systems, or after VW software updates, as palette definitions may vary under these conditions. Using these calls with three explicit color values should always produce the repeatable results. All the best, Raymond
  7. @avictorgm , After a light investigation, it appears that "vs.CreateText()" does not use the document FILL PATTERN, nor does, "vs.CreateText()" return a handle. You will have to set the FILL attribute after you create the text. So, change: h = vs.CreateText(str("Motor ") + str(X1) + str(", ") + str(Y1)) to: vs.CreateText(str("Motor ") + str(X1) + str(", ") + str(Y1)) Then use: h = vs.LNewObj() # if you want to save the handle to the text object vs.SetFPat(h, 1) Or use: vs.SetFPat(vs.LNewObj(), 1) # if you don't need to save the handle to the text object Also, all calls using color values use LONGINTS and need to be in the 0-65535. Multiply color values in the 0-255 range by 257 to convert them to the LONGINT range. Lastly, you need to pass the color values as a TUPLE, and not three Longint values. Eg. vs.PenBack((0, 65535, 0)) # GREEN HTH, Raymond
  8. LOL – Thank you, Justin. If I had a mantle, I'd place it there. Perhaps I can tape it to the refrigerator. 😉 Raymond
  9. Thank you,@klinzey. I vaguely remember something being mentioned to that effect, but details and dates tend to blur the longer I do this. I started back in 1990 with MC+2.0. There's a lot of blur piling up. Yes, I see the difference now. Very nice to know. Thanks for mentioning it. @Sam Jones, If you count the number of characters in the Dynarray in my example you'll see why I said, "but it runs and returns all 437 characters in string "S",". In another test I tried it with a text block twice that amount to see if there was a 512 limit, but it worked with 874 characters. The Devil's in the Details. You can store Dynarrays in a record field, but that's local to file. With a little bit of work you could split your DynArray into 255-char-bite-sized-chunks and store each piece in the SavedSettings library. Some reassembly is required. Or, you could write your data to a file if all else fails. Raymond
  10. Sam, A simple test script would give you your answer. I happen to have one here: PROCEDURE xxx; CONST category = 'DummyCatAGory'; setting = 'SomeSetting'; VAR valueIN, valueOUT :Dynarray of Char; S :String; BEGIN valueIN := '/F1 /Helvetica ff [-144 72 div 0 0 144 72 div 0 0] mf def /F2 /Helvetica ff [-20 72 div 0 0 20 72 div 0 0] mf def /F3 /Helvetica ff 144 72 div scf def /F4 /Helvetica ff 20 72 div scf def /F5 /Helvetica ff 28 72 div scf def /F6 /Helvetica ff [-28 72 div 0 0 28 72 div 0 0] mf def /F7 /Helvetica ff 16 72 div scf def /F8 /Helvetica-Bold ff 24 72 div scf def /F9 /Helvetica ff 36 72 div scf def /F10 /Helvetica ff 96 72 div scf def'; SetSavedSetting(category, setting, valueIN); if GetSavedSetting(category, setting, S) then message(S) else message('Nope'); SysBeep; END; Run(xxx); Now what you need to explain is why this script works. 1) When I declare "valueOUT" as a Dynarray of Char the complier kicks an error with GetSavedSetting(), saying valueOUT is not the correct variable type. 2) When I substitute string "S" for the return variable "valueOUT", which GetSavedSetting() DOES accept, not only does the script compile (as I expect it to), but it runs and returns all 437 characters in string "S", much more than a STRING's allotted 255 characters is supposed to handle. So, to answer your questions, YES, SetSavedSetting() WILL accept a DynArray; and NO, GetSavedSetting() WILL NOT accept a DynArray. But who cares since the latter call works with a STRING. ——— Caveat, this script was only run in VW 2019. Your mileage may vary in earlier VW versions. ——— Raymond
  11. Not even one 👍? ¡¡¡Oh, la inhumanidad de todo!!! :-p You're welcome. 🙂
  12. Hi Justin, Yes, look for an object inside the CSG container. If it returns NIL, then it is a Generic Solid. If the handle exists, then the Solid has history. Run this one line script on various selected 3D objects. vs.Message(vs.FIn3D(vs.FSActLayer()), ' ', vs.GetTypeN(vs.FIn3D(vs.FSActLayer()))) Raymond
  13. Hi rosebud5, I opened your file and I see your problem. The artwork you are importing was not drawn to be filled. It is line art and close is definitely good enough for the person who created it. You obviously want it to perform better than it was designed. Okay, I can appreciate that. When you say you are "wasting a lot of time closing the gaps", are you using the Connect/Combine Tool and/or the Join Tool to remove the gaps, or are you adjusting the Polys, Lines, and Arcs manually? If you are doing the latter, you can save a lot of time by using the right built-in tools. If you're already using those tools, then perhaps a custom script is in order. Though I haven't written one, perhaps one of the other scripters on this forum already has. I'd wait a little longer to see if someone else responds with a suitable answer. If nobody responds, you might want to pursue having a custom script written for you. Depending on how often you need to perform cleanup on these imported drawings, it might well be worth the investment. It won't be a simple script, so it won't be overly cheap; but if it saves you 10-50 hours (or more) a year it should be reasonably affordable. For now, All the best, Raymond
  14. rosebud5, Here's a no-frills script to CLOSE all Polygons and Polylines on the Active Layer. This script does not discern if the opening is "a short distance away". It will close ALL Polys. If you need that capability, will you please define how "close" is "close enough to close", or how "open" is "too far to close". PROCEDURE ClosePolys; { Close all Polygons and Polylines on Active Layer. } { This script: } { - will enter Groups on the Active Layer; } { - will NOT "show" segments that are "hidden" in the middle of a Polyline; } { - does not require objects to be selected, nor will it change the selection state; } { - will NOT work on a collection of LINES that "looks" like a Poly. } { 26 Sep 2018 - Raymond J Mullin } { DISCLAIMER: THIS SCRIPT IS OFFERED AS IS. SAVE YOUR FILE BEFORE USING THIS SCRIPT. } { UNDO WILL WORK, BUT ALWAYS BE SAFE. } function CloseIt(H :Handle) :Boolean; { Closes Polygons and Polylines. Does not affect Polys that are already CLOSED. } Var OT :Integer; Begin OT := GetTypeN(H); { object type } if (OT=5) | (OT=21) then { Polygon or Polyline } SetPolyClosed(H, TRUE); { close the Poly } End; { CloseIt } BEGIN ForEachObjectInLayer(CloseIt, 0, 1, 0); { All objects, Enter Groups, Active Layer } SysBeep; END; Run(ClosePolys); Raymond
  15. Markus, It works on my system, both as a Palette Script, and as a Menu Command. Have you tried restarting VW? Is your VW an English language version? Raymond
  16. Larry, I've been at this a long time, but it was exactly two days ago I tried, as you did, to use hDuplicate() with polar coordinates and it failed. Here's a short program I used to test the snippet of code I used to make it work. The built in vector function Ang2Vec() converts Polar to Rectangular coordinates. If you want, copy the PolarDup() function to your code and use it like you would use HDuplicate(). PROCEDURE xxx; { Duplicate selected object and move it a fixed distance in the direction of a user drawn Line. } CONST Dist = 3; VAR H, H1 :Handle; P1, P2 :Vector; Ang :Real; function PolarDup(H :Handle; Dist, Ang :Real) :Handle; { Same as HDuplicate(), but accepts POLAR coordinates. Returns a handle to the new object. } Var V :Vector; Begin V := Ang2Vec(Ang, Dist); { set distance and direction to move } PolarDup := HDuplicate(H, V.x, V.y); { duplicate retains select state of original } End; { PolarDup } BEGIN GetLine(P1.x, P1.y, P2.x, P2.y); { Get 2 points to define direction, distance is not important } Ang := Vec2Ang(P2-P1); H := FSActLayer; H1 := PolarDup(H, Dist, Ang); SysBeep; END; Run(xxx); HTH, Raymond
  17. Congratulations, Eduardo. Your persistence has definitely paid off. If you continue scripting you will find you can make wonderfully custom tools that work exactly the way you want them. For me, the scripting facet of VW is its most important asset. When you use something again, it pays dividends on all the effort you put into it. All the very best, Raymond
  18. Eduardo, You can construct any grouping you want. The two RadioButtonGroupBoxes split your dialog in half. If you want all four groups active at once, then don't use RadioButtonGroupBoxes. Use GroupBoxes instead. One GroupBox for each set of buttons that need to toggle independently from the others. Remember, only one RadioButton can be set in one group at one time. The dialog handler will take care of turning them off as you turn others on. Only you know how many groups you need. That is part of designing the dialog. Sketch it out on paper first if that helps you visualize it. To read a RadioButton's state, use GetBooleanItem(). If you want independent operation of all the buttons, perhaps you want to use CheckBoxes instead. With CheckBoxes, more than one can be set at a time. You also read them with GetBooleanItem(). HTH, Raymond
  19. Eduardo, Create a GROUP for each set of Radio Buttons. CreateGroupBox(dialogID :LONGINT; itemID :LONGINT; text :STRING; hasFrame :BOOLEAN); The dialog mechanism will treat the radio buttons in each group as individual sets and toggle them independently of each other. Here's a quick example, PROCEDURE ExampleRadioButtonGroups; { Example of Radio Button groups in a Modern Dialog. } { Radio Buttons placed within a Modern Dialog act as single set. To have } { multiple sets of Radio Buttons, place each set in its own GroupBox. } { } { This script does nothing but toggle 4 sets of radio buttons independently. } { The indentation used in this code is not necessary, but only shows the } { logical grouping of the dialog elements. } { 01 Sep 2018 - Raymond J Mullin } VAR id, result :LONGINT; PROCEDURE Dialog_Handler(VAR item :LONGINT; data :LONGINT); BEGIN END; { Dialog_Handler } BEGIN id := CreateLayout('Radio Button Groups', FALSE, 'OK', 'Cancel'); CreateRadioButtonGroupBox(id, 4, 'Retângulo', TRUE); CreateGroupBox(id, 5, 'Group 1', FALSE); CreateRadioButton(id, 6, 'RB 1'); CreateRadioButton(id, 7, 'RB 2'); CreateRadioButton(id, 8, 'RB 3'); CreateRadioButton(id, 9, 'RB 4'); CreateGroupBox(id, 10, 'Group 2', FALSE); CreateRadioButton(id, 11, 'RB 5'); CreateRadioButton(id, 12, 'RB 6'); CreateRadioButton(id, 13, 'RB 7'); CreateRadioButton(id, 14, 'RB 8'); CreateRadioButtonGroupBox(id, 15, 'Oval', TRUE); CreateGroupBox(id, 16, 'Group 3', TRUE); CreateRadioButton(id, 17, 'RB A'); CreateRadioButton(id, 18, 'RB B'); CreateRadioButton(id, 19, 'RB C'); CreateRadioButton(id, 20, 'RB D'); CreateGroupBox(id, 21, 'Group 4', TRUE); CreateRadioButton(id, 22, 'RB E'); CreateRadioButton(id, 23, 'RB F'); CreateRadioButton(id, 24, 'RB G'); CreateRadioButton(id, 25, 'RB H'); SetFirstLayoutItem(id, 4); { RadioButtonGroupBox 1 } SetFirstGroupItem(id, 4, 5); { GroupBox 1 } SetFirstGroupItem(id, 5, 6); SetBelowItem(id, 6, 7, 0, 0); SetBelowItem(id, 7, 8, 0, 0); SetBelowItem(id, 8, 9, 0, 0); SetRightItem(id, 5, 10, 0, 0); { GroupBox 2 } SetFirstGroupItem(id, 10, 11); SetBelowItem(id, 11, 12, 0, 0); SetBelowItem(id, 12, 13, 0, 0); SetBelowItem(id, 13, 14, 0, 0); SetRightItem(id, 4, 15, 0, 0); { RadioButtonGroupBox 2 } SetFirstGroupItem(id, 15, 16); { GroupBox 3 } SetFirstGroupItem(id, 16, 17); SetBelowItem(id, 17, 18, 0, 0); SetBelowItem(id, 18, 19, 0, 0); SetBelowItem(id, 19, 20, 0, 0); SetRightItem(id, 16, 21, 0, 0); { GroupBox 4 } SetFirstGroupItem(id, 21, 22); SetBelowItem(id, 22, 23, 0, 0); SetBelowItem(id, 23, 24, 0, 0); SetBelowItem(id, 24, 25, 0, 0); result := RunLayoutDialog(id, Dialog_Handler); IF (result = 1) THEN BEGIN END; END; RUN (ExampleRadioButtonGroups); Have fun, Raymond
  20. Boh, It's possible you unintentionally copied some extra characters from the browser to the script editor. This has been a problem in the recent past. Try again. It worked for me this morning. If it still doesn't work, try this ZIP file: Pat's PIO Field Script.zip HTH, Raymond
  21. Olá Eduardo, In your code for the OK button (where item = 1), you have 4 statements where you really want 2. I've numbered them here: 1: BEGIN {1} IF GetEditInteger(id, 6, AltR) AND GetEditInteger(id, 8, LarR) THEN ALT := AltR; {2} LAR := LarR; {3} IF GetEditInteger(id, 11, AltO) AND GetEditInteger(id, 13, LarO) THEN ALT := AltO; {4} LAR := LarO; END; The structure you are trying to achieve requires more BEGIN / END statements, as such: 1: BEGIN {1} IF GetEditInteger(id, 6, AltR) AND GetEditInteger(id, 8, LarR) THEN BEGIN ALT := AltR; LAR := LarR; END; { first IF } {2} IF GetEditInteger(id, 11, AltO) AND GetEditInteger(id, 13, LarO) THEN BEGIN ALT := AltO; LAR := LarO; END; { second IF } END; { OK button } BUT... This won't work either, as both IF statements will evaluate to TRUE (even the boxes you don't type into will return 0) and ALT and LAR will have AltO and LarO in them. To get the right set of values into ALT and LAR you will have to test the RADIO BUTTONS, as such: VAR ... RectOval, Boo :BOOLEAN; { add these to your global variable list } ... 1: BEGIN { OK button code } GetBooleanItem(id, 4, RectOval); { get RADIO BUTTON selection } IF RectOval THEN BEGIN { test RADIO BUTTON selection here } Boo := GetEditInteger(id, 6, ALT); Boo := GetEditInteger(id, 8, LAR); END { Rectangle } ELSE BEGIN Boo := GetEditInteger(id, 11, ALT); Boo := GetEditInteger(id, 13, LAR); END; { Oval } END; { OK button } Note: I do not have to test Radio Button 9, since the dialog handler makes sure the buttons are in opposite states, and both buttons cannot be TRUE at the same time. Testing RADIO BUTTON 4 implies the state of RADIO BUTTON 9 will be in the opposite state RADIO BUTTON 4. LASTLY, after you exit your dialog, you need to decide which object to draw. Again, test the global variable RectOval to draw the right shape, like this: IF (result = 1) THEN BEGIN GetPt(pX, pY); if RectOval then Rect(pX-LAR/2, pY-ALT/2, pX+LAR/2, pY+ALT/2) else Oval(pX-LAR/2, pY-ALT/2, pX+LAR/2, pY+ALT/2); END; For a robust script, you should add code to handle what to do if bad data is entered into one of the INTEGER fields, but for the most part this should get you up and running. Hope to see you back with more questions. Happy scripting, Raymond
  22. @Nuno Antunes, I forgot to mention, the script palette with "ActiveLyr" does not have to be open for the worksheet to function properly, it just has to be in your file. Raymond
  23. Hi Nuno, The worksheet uses a custom function (or a user defined function) to supply the name of the active layer and that is the VectorScript "ActiveLyr" you see in the "ccc" script palette of my original file. So, you also have to copy the VectorScript "ActiveLyr" to your file for the worksheet to work properly. You may copy it into one of your script palettes, or import the "ccc" resource (script palette) to your file and the "ActiveLyr" vectorscript will come with it. You never run this script directly, you only call it from within a worksheet, but it does have to be in your file for the worksheet to function, the way I've set it up. I probably should have named the "ccc" script palette "Wks Scripts". That might have made it more obvious you needed it in your working file, too. If you still have questions, please write back before September 2019 and you should get a pretty prompt answer. :-p HTH, Raymond
  24. I have no way of verifying if this is a good conversion. Try it and see. Draft 1.8-Backup-20180602203117.zip Raymond
  25. You are welcome, Julia. Welcome to the world of scripting. While you are trying the One-Beep approach, let me offer another flavor. You can very easily make the script BEEP in the other direction, when LAYER PLANE mode is made active, if that is more to your liking. This line controls the BEEPing. if GetPref(1099) then SysBeep; and it beeps when GetPref(1099) is TRUE, or when SCREEN PLANE mode gets set. If you'd prefer the script to BEEP when LAYER PLANE mode gets set, then use this line instead: if not GetPref(1099) then SysBeep; the "not" keyword reverses the logic and says to BEEP when GetPref(1099) is FALSE, or LAYER PLANE mode gets set. If you find that you use this script a lot, the next step for you is to turn this script into a MENU COMMAND, add it to your workspace, and assign a Keyboard Shortcut to it. So instead of placing a copy of this script in every file you use, you will place it in the VW Plug-ins folder where it becomes a systemwide command. It's a little more work to set up, but well worth the effort if you want to work as efficiently as possible. When you figure out which flavor of the script suits you best, write back and I, or someone else here, will assist you to make it a custom Menu Command. All the best, 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...