User:CBM-c-/VS-Py Rosetta Stone: Difference between revisions
(→Variables: fix) |
m (expand) |
||
(9 intermediate revisions by the same user not shown) | |||
Line 30: | Line 30: | ||
* Download '''Visual Studio Code''' from code.visualstudio.com and install it | * Download '''Visual Studio Code''' from code.visualstudio.com and install it | ||
* Install the '''Python extension''': launch Visual Studio Code, click on Extensions (shift +cmd + X), click on the search field, enter ms-python, click on '''install Python'''. | * Install the '''Python extension''': launch Visual Studio Code, click on Extensions (shift +cmd + X), click on the search field, enter ms-python, click on '''install Python'''. | ||
* create a script library folder for all your Python tools and commands. I named mine _VS. | |||
* In this script library place a file called vs.py containing all the Vectorscript calls. You can find this file in the SDK. | |||
* in Visual Studio Code, open the Settings. In Extensions: | |||
*: Pylance > Python › Analysis: Extra Paths > add item: add the path to the above mentioned script library folder (by me it would be _VS). | |||
*: Python > Auto Complete Extra Paths > Edit Settings Json: enter the path to the above mentioned script library folder, it will look roughly so: | |||
"python.autoComplete.extraPaths": [ | |||
"/Users/userName/path/to/my/scriptLibrary/_VS" | |||
], | |||
* Place in the script library folder a vs.py file. It contains the actual list of Vectorscript routines. This file will also allow for intelligent hints (intelligence) while using an Editor capable of it. You can download the file from the SDK: www.vectorworks.net/support/custom/sdk | |||
* In Vectorworks > Script Editor > Script Options: enter the path to your script library folder. | |||
[[File:IncludePyAPI.png| 500px]] | |||
Intellisense from Visual Studio Code: | |||
[[File:IncludePyIntellisense.png| 500px]] | |||
Line 62: | Line 79: | ||
: <code>AlrtDialog('test 1'); AlrtDialog('test 2'); { OK }</code> | : <code>AlrtDialog('test 1'); AlrtDialog('test 2'); { OK }</code> | ||
: <code>AlrtDialog('test 1') AlrtDialog('test 2') { error }</code> | : <code>AlrtDialog('test 1') AlrtDialog('test 2') { error }</code> | ||
| '''Only needed for multiple statements on one line:''' | | '''Only needed for multiple statements on one line:''' | ||
: <code>vs.AlrtDialog('test 1') </br>vs.AlrtDialog('test 2') # OK</code> | : <code>vs.AlrtDialog('test 1') </br>vs.AlrtDialog('test 2') # OK</code> | ||
Line 117: | Line 135: | ||
| '''Supports implicit conversion:''' | | '''Supports implicit conversion:''' | ||
: Both [[VS:Concat| Concat]] and [[VS:Message| Message]] support multiple variable types and convert them into string. | : Both [[VS:Concat| Concat]] and [[VS:Message| Message]] support multiple variable types and convert them into string. | ||
: | |||
: <code>t := Concat(10, ' fingers'); { OK }</code> | : <code>t := Concat(10, ' fingers'); { OK }</code> | ||
: <code>Message(10, ' fingers'); { OK }</code> | : <code>Message(10, ' fingers'); { OK }</code> | ||
: <code>AlrtDialog(Concat(10, ' fingers')); { OK }</code> | : <code>AlrtDialog(Concat(10, ' fingers')); { OK }</code> | ||
: <code>v.x := 1; v.y := 0; { vector v }</code> | |||
: <code>AlrtDialog(Concat(v)); { returns 1, 0 }</code> | |||
| '''Doesn't support implicit conversion:''' | | '''Doesn't support implicit conversion:''' | ||
: For example an integer won't automatically be converted into string. Wrap it in [[VS:Concat| vs.Concat]] or [[VS:Message| vs.Message]], alternatively perform the needed conversion. | : For example an integer won't automatically be converted into string. Wrap it in [[VS:Concat| vs.Concat]] or [[VS:Message| vs.Message]], alternatively perform the needed conversion. | ||
: | |||
: <code>t = 10 + ' fingers' # error</code> | : <code>t = 10 + ' fingers' # error</code> | ||
: <code>t = vs.Concat(10, ' fingers') # OK</code> | : <code>t = vs.Concat(10, ' fingers') # OK</code> | ||
Line 128: | Line 150: | ||
: <code>vs.AlrtDialog(10 + ' fingers') # error </code> | : <code>vs.AlrtDialog(10 + ' fingers') # error </code> | ||
Error Message: TypeError: unsupported operand type(s) for +: 'int' and 'str' | Error Message: TypeError: unsupported operand type(s) for +: 'int' and 'str' | ||
: Vectors are tuples and won't resolve with vs.Concat(): | |||
: | |||
: <code>vs.AlrtDialog(str( v )) # OK: returns tuple v as v[0], v[1]</code> | |||
: <code>vs.AlrtDialog(vs.Concat( v )) # returns an empty string</code> | |||
: <code>vs.AlrtDialog(vs.Concat(str( v )) # OK: returns the values in the tuple v </code> | |||
|} | |} | ||
Line 153: | Line 181: | ||
<code lang="pas"> | <code lang="pas"> | ||
FUNCTION GetCustomObjectInfo( | FUNCTION GetCustomObjectInfo( | ||
VAR objectName :STRING; | |||
VAR objectHand :HANDLE; | |||
VAR recordHand :HANDLE; | |||
VAR wallHand :HANDLE) : BOOLEAN; | |||
ok := GetCustomObjectInfo( objectName, objectHand, recordHand, wallHand ); | ok := GetCustomObjectInfo( objectName, objectHand, recordHand, wallHand ); | ||
Line 163: | Line 191: | ||
<code lang="pas"> | <code lang="pas"> | ||
PROCEDURE HMove( | PROCEDURE HMove( | ||
objectHand :HANDLE; | |||
xOffset :REAL; | |||
yOffset :REAL); | |||
HMove( objectHand, xOffset, yOffset ); | HMove( objectHand, xOffset, yOffset ); | ||
Line 224: | Line 252: | ||
VAR | VAR | ||
{ good praxis: label globals with "g" } | { good praxis: label globals with "g" } | ||
gIndex, gNum : INTEGER; { gNum is valid starting from here | gIndex, gNum : INTEGER; | ||
{ gNum is valid starting from here } | |||
PROCEDURE Increment; | PROCEDURE Increment; | ||
BEGIN | BEGIN | ||
{ gNum is not | { var gNum is not declared in this subroutine, | ||
the parser climbs up parent containers | the parser climbs up parent containers | ||
until it finds a declaration for the var gNum. | until it finds a declaration for the var gNum. | ||
Line 396: | Line 425: | ||
An ordered list whose items are unchangeable | An ordered list whose items are unchangeable | ||
c = vs.GetSymLoc(symH) | c = vs.GetSymLoc(symH) | ||
c[0] = c[0] + | c[0] = c[0] +1m | ||
# error! | # error! | ||
Line 458: | Line 487: | ||
import main | import main | ||
# import the script main.py, in the same folder as the running .vso/.vsm/.vst plug-in | # import the script main.py, in this case it resides in the same folder as the running .vso/.vsm/.vst plug-in | ||
; Other Libraries: | ; Other Libraries: | ||
Line 488: | Line 517: | ||
</code> | </code> | ||
; VS API: you must always include the VS | ; VS API: you must always include the VS AP or you won't be able to use it. This file will also allow for intelligent hints while using an Editor capable of it. Download it from the SDK: www.vectorworks.net/support/custom/sdk | ||
* copy the file SDKVW(...)/SDKLib/Include/vs.py in your script library (defined in the Script Options). If you don't have such a library you can copy the file in the folder where your plug-in object (.vso) or command (.vsm) is. | * copy the file SDKVW(...)/SDKLib/Include/vs.py in your script library (defined in the Script Options). If you don't have such a library you can copy the file in the folder where your plug-in object (.vso) or command (.vsm) is. | ||
* link to the API using | * link to the API using | ||
Line 536: | Line 564: | ||
''When True the Python engine is the same for the execution of all scripts, this solves some issues with Py_Initialize and Py_Finalize. For example, when debugging externally python leaves threas that cause crash if Py_Initialize and Py_Finalize is used for each script call. So, this allows the engine to be preserved between calls, however Vectorworks will delete all custom modules and objects defined in the engine prior each execution.'' | ''When True the Python engine is the same for the execution of all scripts, this solves some issues with Py_Initialize and Py_Finalize. For example, when debugging externally python leaves threas that cause crash if Py_Initialize and Py_Finalize is used for each script call. So, this allows the engine to be preserved between calls, however Vectorworks will delete all custom modules and objects defined in the engine prior each execution.'' | ||
* Enable the developer mode in Preferences > Session | |||
* Open your Plug-in in the Script Editor, add: | |||
import vs # import Vectorscript definitions | |||
import imp # import Python library, see note below | |||
import module | |||
from module import _script # main script for your tool | |||
imp.reload(_script) # Make sure the module ist always up to date and has initial values | |||
vs.SetPref(412, True) #Turns off include caching | |||
_script.mainRoutineInScript() # execute the main routine in the script | |||
# The Import libraries changed across Python versions: | |||
# For Python2.x | |||
# reload(module) | |||
# For above 2.x and <= Python 3.3 | |||
# import imp | |||
# imp.reload(module) | |||
# For >= Python 3.4 | |||
# import importlib | |||
# importlib.reload(module) | |||
|} | |} | ||
Latest revision as of 17:21, 26 June 2022
VS-Python Rosetta Stone
Transitioning from Vectorscript Pascal to Vectorscript Python has its challenges. Here I note down the difficulties I encounter as they come, together with the solution. What troubles me will likely trouble all others.
- Note
- as of today the wiki doesn't accept external links for safety reasons, which are thus not clickable.
Set up Python (Mac)
As of this writing, Vectorworks requires Python 3.x. MacOS X before 10.15 ships with Python 2.x. VW delivers the right Python, so you don't need to do anything special if there are no external, vw-unrelated reasons to do so. Should you wish to modify the Python installed on your machine you can proceed as follows, but this won't modify the shipped VW Python. For example, you might want to do some terminal tutorials.
- launch the Terminal
- type:
python3 --version
- if there is an error, you should install Python 3 and configure it.
- follow the instructions on installpython3.com/mac/.
This document is very complete and leads you step by step through the rather cryptical configurations. During this process you will install XCode and Homebrew through the terminal.
- Warning
- For some reason the XCode wouldn't install on my MacOS X.14. After various hours of fumbling, I gave up and went straight to the official Python 3 installer, which is available at www.python.org/downloads/release/python-391/
After that I proceeded with the configurations as described in installpython3.com/mac/ and all seems well enough.
Set up an editor
I tried using Aptana but couldn't configure it. Then I found at Computerworks excellent instructions by Oliver Daus for Visual Studio Code: (German) www.vectorworksforum.eu/topic/14087-entwicklungsumgebung-für-vectorworks-python-plug-ins/
- Download Visual Studio Code from code.visualstudio.com and install it
- Install the Python extension: launch Visual Studio Code, click on Extensions (shift +cmd + X), click on the search field, enter ms-python, click on install Python.
- create a script library folder for all your Python tools and commands. I named mine _VS.
- In this script library place a file called vs.py containing all the Vectorscript calls. You can find this file in the SDK.
- in Visual Studio Code, open the Settings. In Extensions:
- Pylance > Python › Analysis: Extra Paths > add item: add the path to the above mentioned script library folder (by me it would be _VS).
- Python > Auto Complete Extra Paths > Edit Settings Json: enter the path to the above mentioned script library folder, it will look roughly so:
"python.autoComplete.extraPaths": [ "/Users/userName/path/to/my/scriptLibrary/_VS" ],
- Place in the script library folder a vs.py file. It contains the actual list of Vectorscript routines. This file will also allow for intelligent hints (intelligence) while using an Editor capable of it. You can download the file from the SDK: www.vectorworks.net/support/custom/sdk
- In Vectorworks > Script Editor > Script Options: enter the path to your script library folder.
Intellisense from Visual Studio Code:
VS - Py
Syntax
Description | Vectorscript Pascal | Vectorscript Python
|
---|---|---|
Indenting | irrelevant | Fatal:
you used both spaces and tabs to indent your code. This occurs frequently while copy-pasting across sources. Error Message: unindent does not match any outer indentation level you added an extra indent. This is common in Pascal, just to visually organise the code. Error Message: unexpected indent
|
Semicolon | Always needed:
|
Only needed for multiple statements on one line:
Error Message: SyntaxError: invalid syntax
|
Case sensitivity | Not case sensitive:
|
Case sensitive:
Error Message: AttributeError: 'module' object has no attribute 'vs.ALRTDIALOG'
|
Brakets
|
No brakets
|
Brakets
Error Message: - none! be careful! - |
FOR statements | Runs including last value:
{ runs 3 times! 1, 2 and 3 } FOR i := 1 TO 3 DO AlrtDialog(Concat(i)); |
Runs excluding last value:
# runs 2 times! 1 and 2 for i in range(1, 3): vs.AlrtDialog(str(i))
|
Concatenate text | Supports implicit conversion: | Doesn't support implicit conversion:
Error Message: TypeError: unsupported operand type(s) for +: 'int' and 'str'
|
Variables
Perhaps the largest source of error while transitioning to python are the deep differences in variables.
Description | Vectorscript Pascal | Vectorscript Python
|
---|---|---|
Returning Values | Results left, VARs and arguments right
FUNCTION ActiveClass : STRING; classN := ActiveClass; FUNCTION GetCustomObjectInfo( VAR objectName :STRING; VAR objectHand :HANDLE; VAR recordHand :HANDLE; VAR wallHand :HANDLE) : BOOLEAN; ok := GetCustomObjectInfo( objectName, objectHand, recordHand, wallHand ); PROCEDURE HMove( objectHand :HANDLE; xOffset :REAL; yOffset :REAL); HMove( objectHand, xOffset, yOffset ); |
Results left, VARs left, arguments right
def vs.ActiveClass(): return STRING classN = vs.ActiveClass() def vs.GetCustomObjectInfo(): return (BOOLEAN, objectName, objectHand, recordHand, wallHand) ok, objectName, objectHand, recordHand, wallHand = vs.GetCustomObjectInfo( ) def vs.HMove(objectHand, xOffset, yOffset): return None vs.HMove(objectHand, xOffset, yOffset) |
Variable Attributes
discovered by P. Winkler 2017 |
no | built in in VW
h = vs.FSActLayer() print(h.type) print(h.locked) print(h.name) print(h.selected) print(h.prev) print(h.next) print(h.parent)
|
Variable scope |
Global wins over local:
{ GLOBAL ACCESS } PROCEDURE Main; VAR { good praxis: label globals with "g" } gIndex, gNum : INTEGER; { gNum is valid starting from here } PROCEDURE Increment; BEGIN { var gNum is not declared in this subroutine, the parser climbs up parent containers until it finds a declaration for the var gNum. In this case it can be found in Main } gNum := gNum +1; SysBeep; END; BEGIN gNum := 10; { init } FOR gIndex := 1 TO 10 DO Increment; { increments the variable gNum } AlrtDialog(Concat(gNum)); { returns 20 } END; Run(Main); |
Local wins over global:
# LOCAL ACCESS def Increment(): # gNum is not defined in this subroutine # the parser creates a local instance of the var gNum! gNum +=1 vs.SysBeep gNum = 10 # init for gIndex in range(1, 10): Increment vs.AlrtDialog(str(gNum)) # returns 10! The global var didn't set # GLOBAL ACCESS: CORRECT def Increment(): global gNum # <----- gNum +=1 vs.SysBeep() gNum = 10 # init for gIndex in range(0, 10): Increment() vs.AlrtDialog(str(gNum)) # returns 20 # TRY GLOBAL ACCESS: WRONG def Increment(): global gNum gNum +=1 vs.SysBeep() # no init! for gIndex in range(0, 10): Increment() vs.AlrtDialog(str(gNum)) Error Message: NameError: global name 'gNum' is not defined # TRY GLOBAL ACCESS: WRONG global gNum # <----- wrong place! def Increment(): gNum +=1 vs.SysBeep() gNum = 10 for gIndex in range(0, 10): Increment() vs.AlrtDialog(str(gNum)) Error Message: UnboundLocalError: local variable 'gNum' referenced before assignment
|
|
h <> NIL |
h is None h is not None
h != None h != vs.Handle() # not inited instance of a handle, how cryptic See: www.python.org/dev/peps/pep-0008/#programming-recommendations |
Fetching Plug-in Parameters | Direct, not case sensitive:
MoveTo(PCONTROLPOINT01X, PCONTROLPOINT01Y); LineTo(PCONTROLPOINT02X, PCONTROLPOINT02Y); |
Prefixed with vs, case sensitive:
Use the exact parameter string as entered in the plug-in editor. Right casement: correct vs.MoveTo(vs.PControlPoint01X, vs.PControlPoint01Y) vs.LineTo(vs.PControlPoint02X, vs.PControlPoint02Y) Wrong casement: error vs.MoveTo(vs.PCOntrolPoint01X, vs.PControlPoint01Y) vs.LineTo(vs.PcontrolPoint02X, vs.PControlPoint02Y)
|
Using units | Yes:
GetSymLoc(symH, c.x, c.y); MoveTo(c.x, c.y); Line(1m, 0); { create an horizontal line 1m long, starting from point c } |
'needs conversion':
c = vs.GetSymLoc(symH) vs.MoveTo(c[0], c[1]) vs.Line(1m, 0) # error! # from JBenghiat def Str2Num(inStr): ok, outNum = vs.ValidNumStr(inStr) if ok: return outNum else: return 0 c = vs.GetSymLoc(symH) vs.MoveTo(c[0], c[1]) vs.Line(Str2Num ('1m'), 0) |
Vectors, Points | Can be manipulated in place:
GetSymLoc(symH, c.x, c.y); c.x := c.x +1m; |
Is Tuple:
An ordered list whose items are unchangeable c = vs.GetSymLoc(symH) c[0] = c[0] +1m # error! |
Colors |
|
Error Message: - none! be careful! - |
Includes / Imports
Description | Vectorscript Pascal | Vectorscript Python |
---|---|---|
Includes/Import
Load libraries of code |
$INCLUDE libraryFolder\libraryFile.vss $INCLUDE libraryFolder\libraryFile.px
In the example in screenshot the file z_Ramp.px is located 2 folders higher (..\..\) of the file .CBM-Ramp.vso, and within the folders \_VS\CBM-Ramp. There are compelling reasons to split the running code from the plug-ins. For example because you will want a plug-in for every VW major version, but not necessarily more code files.
|
Python calls Includes "imports". It searches for imports in the current directory, then in dedicated directories, if any (Script Options). For current directory Python understands the folder of the running .vso/.vsm/.vst file.
import main # import the script main.py, in this case it resides in the same folder as the running .vso/.vsm/.vst plug-in
import vs import math from subprocess import Popen, PIPE
import vs, math # not recommended
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
from . import sibling from .sibling import example See more in: www.python.org/dev/peps/pep-0008/#imports
import sys ver = sys.version_info vs.Message(repr(ver))
import vs Intellisense from Visual Studio Code:
|
Encryption
See encryption for more infos. |
{$INCLUDE ..\..\_VS_includes\_common\Utils.px} {$INCLUDE ..\..\_VS_includes\_common\Math.px} |
For encryption in Python there are difficulties. See instructions from Vlado on the Techboard, search for "problems-encrypting-a-python-script" (at the moment we cannot add external links to the present wiki).
|
Python Version | - | import sys
|
Python Caching
|
varPersistentPythonEngine = 412 { Boolean }
In the SDK starting from VW 2014 we can read: When True the Python engine is the same for the execution of all scripts, this solves some issues with Py_Initialize and Py_Finalize. For example, when debugging externally python leaves threas that cause crash if Py_Initialize and Py_Finalize is used for each script call. So, this allows the engine to be preserved between calls, however Vectorworks will delete all custom modules and objects defined in the engine prior each execution.
import vs # import Vectorscript definitions import imp # import Python library, see note below import module from module import _script # main script for your tool imp.reload(_script) # Make sure the module ist always up to date and has initial values vs.SetPref(412, True) #Turns off include caching _script.mainRoutineInScript() # execute the main routine in the script # The Import libraries changed across Python versions: # For Python2.x # reload(module) # For above 2.x and <= Python 3.3 # import imp # imp.reload(module) # For >= Python 3.4 # import importlib # importlib.reload(module) |
Lists
Lists are powerful in Python, below some fascinating lists manipulations. They remind me of Applescript:
months = "Jan Feb Mar Apr May Jun Jul" months = months.split() # no splitter defined and it will use the empty space --> ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'] months[2] # --> 'Mar' note that the index is 0-based months2 = "Jan, Feb, Mar, Apr, May, Jun, Jul" months2.split(', ') # --> ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'] use comma and empty space as splitter months.append('Jul') # --> ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'] append adds an item to a list months.pop() #- -> 'Jul' pop fetches the last item of a list ', sunny '.join(months) # --> ', sunny Jan, sunny Feb, sunny Mar, sunny Apr, sunny May, sunny Jun, sunny Sep' '-'.join(months[1:3]) # --> 'Feb-Mar' del months[2] # --> ['Jan', 'Feb', 'Apr', 'May', 'Jun', 'Jul'] months = {1: 'Jan', 2: 'Feb', 3: 'Mar'} # --> {1: 'Jan', 2: 'Feb', 3: 'Mar'}
Errors
Python Error Messages:
BaseExceptions:
+-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError +-- ImportError +-- LookupError | +-- IndexError | +-- KeyError +-- MemoryError +-- NameError | +-- UnboundLocalError +-- OSError | +-- BlockingIOError | +-- ChildProcessError | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError | | +-- ConnectionRefusedError | | +-- ConnectionResetError | +-- FileExistsError | +-- FileNotFoundError | +-- InterruptedError | +-- IsADirectoryError | +-- NotADirectoryError | +-- PermissionError | +-- ProcessLookupError | +-- TimeoutError +-- ReferenceError +-- RuntimeError | +-- NotImplementedError +-- SyntaxError | +-- IndentationError | +-- TabError +-- SystemError +-- TypeError +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning +-- UserWarning +-- FutureWarning +-- ImportWarning +-- UnicodeWarning +-- BytesWarning +-- ResourceWarning