User:CBM-c-/VS-Py Rosetta Stone: Difference between revisions

From Vectorworks Developer
Jump to navigation Jump to search
(Create a new page for the VS-Py Rosetta stone)
 
m (expand)
 
(34 intermediate revisions by the same user not shown)
Line 5: Line 5:
= VS-Python Rosetta Stone =
= VS-Python Rosetta Stone =


Transitioning from Vectorscript Pascal to Vectorscript Python has its challenges, here a Rosetta Stone to help you out with the task.
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 ==


As of this writing, Vectorworks requires Python 3.x. MacOS X before 10.15 ships with Python 2.x, so you might need to install the version 3.
== 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
* launch the Terminal
* type: python3 --version; if there is an error, you should install Python 3 and configure it.
* type:  
python3 --version
: if there is an error, you should install Python 3 and configure it.
* follow the instructions on installpython3.com/mac/.  
* follow the instructions on installpython3.com/mac/.  


Line 19: Line 23:
; 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/
; 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.
After that I proceeded with the configurations as described in installpython3.com/mac/ and all seems well enough.


== Set up an editor ==
== Set up an editor ==


I tried using Aptana but couldn't configure it. Then I found at Computerworks excellent instructions for Visual Studio Code: (German) www.vectorworksforum.eu/topic/14087-entwicklungsumgebung-für-vectorworks-python-plug-ins/
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/


Below a rough translation until Computerworks writes a proper one in English.
* Download '''Visual Studio Code''' from code.visualstudio.com and install it
* Download '''Visual Studio Code'''
* 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]]




== VS - Py ==
== VS - Py ==
=== Syntax ===


{| class="wikitable"
{| class="wikitable"
! style="width:20%"| Description
! style="width:20%"| Description
! style="width:40%"| Vectorscript
! style="width:40%"| Vectorscript Pascal
! style="width:40%"| Python
! style="width:40%"| Vectorscript Python
 
 
|- style="vertical-align: top;"
| '''Indenting'''
| irrelevant
| '''Fatal:'''
 
you used both spaces and tabs to indent your code.  This occurs frequently while copy-pasting across sources.
: [[file:PythonIndent1.png]]
 
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.
: [[file:PythonIndent2.png]]
Error Message: unexpected indent
 


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Statement ending:'''
| '''Semicolon'''
| '''Semicolon always needed'''
| '''Always needed:'''
* <code>AlrtDialog('test 1') </br> AlrtDialog('test 2') { error }</code>
: <code>AlrtDialog('test 1') <br>AlrtDialog('test 2') { error }</code>
* <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>
| '''Semicolon needed only for multiple statements on one line:'''
 
* <code>vs.AlrtDialog('test 1') </br>vs.AlrtDialog('test 2') # OK</code>
| '''Only needed for multiple statements on one line:'''
* <code>vs.AlrtDialog('test 1'); vs.AlrtDialog('test 2') # OK</code>
: <code>vs.AlrtDialog('test 1') </br>vs.AlrtDialog('test 2') # OK</code>
* <code>vs.AlrtDialog('test 1') vs.AlrtDialog('test 2') # error</code>
: <code>vs.AlrtDialog('test 1'); vs.AlrtDialog('test 2') # OK</code>
: <code>vs.AlrtDialog('test 1') vs.AlrtDialog('test 2') # error</code>


  Error Message: SyntaxError: invalid syntax
  Error Message: SyntaxError: invalid syntax


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Case sensitivity'''
| '''Case sensitivity'''
| '''Not case sensitive:'''
| '''Not case sensitive:'''
* <code>AlrtDialog('test'); { OK }</code>
: <code>AlrtDialog('test'); { OK }</code>
* <code>alrtDialog('test'); { OK }</code>
: <code>alrtDialog('test'); { OK }</code>
* <code>alrtdialog('test'); { OK }</code>
: <code>alrtdialog('test'); { OK }</code>
* <code>ALRTDIALOG('test'); { OK }</code>
: <code>ALRTDIALOG('test'); { OK }</code>
| '''Case sensitive:'''
| '''Case sensitive:'''
* <code>vs.AlrtDialog('test') # OK </code>
: <code>vs.AlrtDialog('test') # OK </code>
* <code>vs.alrtDialog('test') # error</code>
: <code>vs.alrtDialog('test') # error</code>
* <code>vs.alrtdialog('test') # error</code>
: <code>vs.alrtdialog('test') # error</code>
* <code>vs.ALRTDIALOG('test') # error</code>
: <code>vs.ALRTDIALOG('test') # error</code>
Error Message: AttributeError: 'module' object has no attribute 'vs.ALRTDIALOG'
 
 
|- style="vertical-align: top;"
| '''Brakets'''
: don't forget in python the empty brakets for routines without parameters, or it will rise cryptical errors.
| '''No brakets'''
: <code>FSActLayer; { no parameters: brakets not needed }</code>
: <code>MySubroutine;</code>
| '''Brakets'''
: <code>vs.FSActLayer()</code>
: <code>MySubroutine()</code>
Error Message: - none! be careful! -
 
|- style="vertical-align: top;"
| '''FOR statements'''
| '''Runs including last value:'''
<code lang="pas">
{ runs 3 times! 1, 2 and 3 }
FOR i := 1 TO 3 DO
    AlrtDialog(Concat(i));
</code>
| '''Runs excluding last value:'''
<code lang="py">
# runs 2 times! 1 and 2
for i in range(1, 3):
    vs.AlrtDialog(str(i))
</code>
 
 
|- style="vertical-align: top;"
| '''Concatenate text'''
| '''Supports implicit conversion:'''
: 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>Message(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:'''
: 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 = vs.Concat(10, ' fingers') # OK</code>
: <code>vs.Message(10, ' fingers') # OK</code>
: <code>vs.AlrtDialog(str(10) + ' fingers') # OK</code>
: <code>vs.AlrtDialog(10 + ' fingers') # error </code>
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>
|}
 
 
=== Variables ===
 
Perhaps the largest source of error while transitioning to python are the deep differences in variables.
 
{| class="wikitable"
! style="width:20%"| Description
! style="width:40%"| Vectorscript Pascal
! style="width:40%"| Vectorscript Python


Error Message: AttributeError: 'module' object has no attribute 'vs.ALRTDIALOG'


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Empty brakes for functions'''
| '''Returning Values'''
: don't forget in python the empty brakets for routines without parameters, this rises errors that are so tricky to find.
| '''Results left, VARs and arguments right'''
| '''Can be without brakets'''
 
* <code>FSActLayer; { no paramters: brakets not needed }</code>
<code lang="pas">
* <code>MySubroutine;</code>
FUNCTION ActiveClass : STRING;
| '''Can't be without brakets'''
 
* <code>vs.FSActLayer()</code>
classN := ActiveClass;
* <code>MySubroutine()</code>
</code>
 
<code lang="pas">
FUNCTION GetCustomObjectInfo(
    VAR objectName  :STRING;
    VAR objectHand  :HANDLE;
    VAR recordHand  :HANDLE;
    VAR wallHand    :HANDLE) : BOOLEAN;
 
ok := GetCustomObjectInfo( objectName, objectHand, recordHand, wallHand );
</code>
 
<code lang="pas">
PROCEDURE HMove(
    objectHand :HANDLE;
    xOffset  :REAL;
    yOffset  :REAL);
 
HMove( objectHand, xOffset, yOffset );
</code>
 
| '''Results left, VARs left, arguments right'''
 
<code lang="py">
def vs.ActiveClass(): 
    return STRING
 
classN = vs.ActiveClass()
</code>
 
<code lang="py">
def vs.GetCustomObjectInfo():
    return (BOOLEAN, objectName, objectHand, recordHand, wallHand)
 
ok, objectName, objectHand, recordHand, wallHand = vs.GetCustomObjectInfo( )
</code>
 
<code lang="py">
def vs.HMove(objectHand, xOffset, yOffset):
    return None


Error Message: - none! be careful! -
vs.HMove(objectHand, xOffset, yOffset)
</code>


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Empty handles'''
| '''Variable Attributes'''
: do pay attention also to the variable scope (see below).
discovered by P. Winkler 2017
|
 
* <code>h <> NIL</code>
| no
|
 
* <code>h != None</code>
| '''built in in VW'''
* <code>h != vs.Handle() # not inited instance of a handle, how cryptic</code>
h = vs.FSActLayer()
print(h.type)
print(h.locked)
print(h.name)
print(h.selected)
print(h.prev)
print(h.next)
print(h.parent)
 


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Variable scope'''
| '''Variable scope'''
: perhaps the largest source of error for the vectorscripter transitioning to python
|  
|  
'''Global wins over local:'''
'''Global wins over local:'''
* Variables must be declared
* Variables must be declared
* Subroutines "see" their own variables and those of any parent function/procedure where they are contained.
* Subroutines "see" their own variables and those of any parent function/procedure where they are contained.
<code lang="pas">
<code lang="pas">
{ GLOBAL ACCESS }
{ GLOBAL ACCESS }
{ parent of subroutine "Increment" }
 
PROCEDURE Main;
PROCEDURE Main;
     VAR
     VAR
         { good praxis: label globals with "g" }
         { good praxis: label globals with "g" }
         gIndex, gNum : INTEGER;  
         gIndex, gNum : INTEGER;
        { gNum is valid starting from here }
      
      
    { subroutine }
     PROCEDURE Increment;
     PROCEDURE Increment;
         BEGIN
         BEGIN
             { gNum is not defined in this subroutine  
             { 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 118: Line 271:
          
          
     AlrtDialog(Concat(gNum));
     AlrtDialog(Concat(gNum));
{ returns 20 }
{ returns 20 }
END;
END;
Run(Main);
Run(Main);
</code>
</code>
|  
 
'''Local wins over global:'''
| '''Local wins over global:'''
* Variables must NOT be declared
* Variables must NOT be declared
* Subroutines create automatically a local instance of any used variable.
* Subroutines create automatically a local instance of any used variable.
Line 129: Line 283:
<code lang="py">
<code lang="py">
# LOCAL ACCESS
# LOCAL ACCESS
# subroutine
 
def Increment():
def Increment():
     # gNum is not defined in this subroutine
     # gNum is not defined in this subroutine
Line 138: Line 292:
gNum = 10 # init
gNum = 10 # init
for gIndex in range(1, 10):
for gIndex in range(1, 10):
     Increment  
     Increment
    # increments the variable gNum
    # but only inside Increment!
      
      
vs.AlrtDialog(str(gNum))
vs.AlrtDialog(str(gNum))
Line 148: Line 300:
<code lang="py">
<code lang="py">
# GLOBAL ACCESS: CORRECT
# GLOBAL ACCESS: CORRECT
# subroutine
 
def Increment():
def Increment():
     # gNum is not defined in this subroutine
     global gNum # <-----
    # tell the parser that you want to edit gNum global!
    global gNum
     gNum +=1
     gNum +=1
     vs.SysBeep()
     vs.SysBeep()
   
   
gNum = 10 # init
gNum = 10 # init
# please observe that the range is NOT 1, 10!
for gIndex in range(0, 10):
for gIndex in range(0, 10):
     Increment()
     Increment()
    # increments the variable gNum
 
    # but only inside Increment!
   
vs.AlrtDialog(str(gNum))
vs.AlrtDialog(str(gNum))
# returns 20
# returns 20
Line 168: Line 315:


<code lang="py">
<code lang="py">
# GLOBAL ACCESS: WRONG
# TRY GLOBAL ACCESS: WRONG
# subroutine
 
def Increment():
def Increment():
    # gNum is not defined in this subroutine
    # tell the parser that you want to edit gNum global!
     global gNum
     global gNum
     gNum +=1
     gNum +=1
Line 180: Line 325:
for gIndex in range(0, 10):
for gIndex in range(0, 10):
     Increment()
     Increment()
    # increments the variable gNum
    # but only inside Increment!
      
      
vs.AlrtDialog(str(gNum))
vs.AlrtDialog(str(gNum))
# rises error
</code>
</code>


  Error Message: NameError: global name 'gNum' is not defined
  Error Message: NameError: global name 'gNum' is not defined


|- style="vertical-align: top;"
| '''FOR statements'''
| '''Runs including last value:'''
<code lang="pas">
{ runs 3 times! 1, 2 and 3 }
FOR i := 1 TO 3 DO
    AlrtDialog(Concat(i));
</code>
| '''Runs excluding last value:'''
<code lang="py">
<code lang="py">
# runs 2 times! 1 and 2
# TRY GLOBAL ACCESS: WRONG
for i in range(1, 3):
 
     vs.AlrtDialog(str(i))
global gNum #  <----- wrong place!
 
def Increment():
    gNum +=1
    vs.SysBeep()
gNum = 10
for gIndex in range(0, 10):
     Increment()
 
vs.AlrtDialog(str(gNum))
</code>
</code>
Error Message: UnboundLocalError: local variable 'gNum' referenced before assignment
|- style="vertical-align: top;"
|
; NIL handles:
|
h <> NIL
|
; Recommended:
h is None
h is not None
; The "==" or "!=" form is not recommended:
h != None
h != vs.Handle() # not inited instance of a handle, how cryptic
See: www.python.org/dev/peps/pep-0008/#programming-recommendations
|- style="vertical-align: top;"
| '''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)
|- style="vertical-align: top;"
| '''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)
|- style="vertical-align: top;"
| '''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!


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Colors'''
| '''Colors'''
|
|
* Color Index:
; Color Index:
*: <code>SetPenFore(h, RGBToColorIndex(65535, 0, 0));</code>
: <code>SetPenFore(h, RGBToColorIndex(65535, 0, 0));</code>
*: <code>PenFore(RGBToColorIndex(65535, 0, 0));</code>
: <code>PenFore(RGBToColorIndex(65535, 0, 0));</code>
* RGB:
; RGB:
*: <code>SetPenFore(h, 65535, 0, 0);</code>
: <code>SetPenFore(h, 65535, 0, 0);</code>
|  
|  
* Color Index:
; Color Index:
*: <code>vs.SetPenFore(h, vs.RGBToColorIndex(65535, 0, 0)) </code>
: <code>vs.SetPenFore(h, vs.RGBToColorIndex(65535, 0, 0)) </code>
* RGB in Tuple:
 
*: <code>vs.SetPenFore(h, (65535, 0, 0)) </code>
; RGB in Tuple:
* Hex in Tuple:
: <code>vs.SetPenFore(h, (65535, 0, 0)) </code>
*: <code>vs.SetPenFore(h, (0xFFFF, 0, 0))</code>


Warning: don't forget the brakets:
; Hex in Tuple:
* <code>vs.PenFore((65535, 0, 0)) </code> correct
: <code>vs.SetPenFore(h, (0xFFFF, 0, 0))</code>
* <code>vs.PenFore(65535, 0, 0) </code> fails
 
; Warning: don't forget the brakets:
: <code>vs.PenFore((65535, 0, 0)) </code> correct
: <code>vs.PenFore(65535, 0, 0) </code> fails
  Error Message: - none! be careful! -
  Error Message: - none! be careful! -
|}
=== Includes / Imports ===
{| class="wikitable"
! style="width:20%"| Description
! style="width:40%"| Vectorscript Pascal
! style="width:40%"| Vectorscript Python


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Concatenate text'''
| '''Supports implicit conversion:'''
: 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>Message(10, ' fingers'); { OK }</code>
* <code>AlrtDialog(Concat(10, ' fingers')); { OK }</code>
|  '''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.
* <code>t = 10 + ' fingers' # error</code>
* <code>t = vs.Concat(10, ' fingers') # OK</code>
* <code>vs.Message(10, ' fingers') # OK</code>
* <code>vs.AlrtDialog(str(10) + ' fingers') # OK</code>
* <code>vs.AlrtDialog(10 + ' fingers') # error </code>


  Error Message: TypeError: unsupported operand type(s) for +: 'int' and 'str'
| '''Includes/Import'''
Load libraries of code
|
; Custom (own) Libraries:
:* use the term ''$INCLUDE''
:* pass the appropriate path to your code and/or code libraries
 
  $INCLUDE libraryFolder\libraryFile.vss
$INCLUDE libraryFolder\libraryFile.px
 
; Note: the path to the vss/px file is relative to the running .vso/.vst/.vsm plug-in file.
[[File:IncludeVS.png| 500px]]
 
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.
 
 
; External libraries: not possible
 
|
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.
; Custom (own) Libraries: Set up in the Script Options (see below) one or more environmental paths from which all active plug-ins will resolve imports
:* choose a library folder with your code and/or libraries in the Plug-Ins ''Script Options''
:* use the term ''Import''
 
import main
# import the script main.py, in this case it resides in the same folder as the running .vso/.vsm/.vst plug-in
 
; Other Libraries:
import vs
import math
from subprocess import Popen, PIPE
 
: But not
import vs, math # not recommended
 
: Absolute path
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
 
: Relative path
from . import sibling
from .sibling import example
 
See more in: www.python.org/dev/peps/pep-0008/#imports
 
; Note: the paths listed through the ''Script Options'' dialog are valid for ''all'' plug-ins. One might be mislead into thinking that the list affected only one plug-in.
[[File:IncludePy.png| 500px]]
 
; External library: include + name of the chosen library
<code lang="py">import sys
ver = sys.version_info
vs.Message(repr(ver))
</code>
 
; 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.
* link to the API using
import vs
 
[[File:IncludePyAPI.png| 500px]]
 
Intellisense from Visual Studio Code:
 
[[File:IncludePyIntellisense.png| 500px]]
 


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Encryption'''
| '''Encryption'''
| Whatever .vs or .px file is linked through your includes, will be encrypted upon running the encrypt command. More infos about standard Vectorscript (Pascal) encryptions are [[VS:Include_Files_and_Encryption| here]].
See [[VS:Include_Files_and_Encryption| encryption]] for more infos.
| Create list of your included files in an xml file.
|
; vss: -
; px: encrypt upon launching the Encryption command (in the Plug-in Manager dialog, click on the button ''Edit Script...'' while pressing shift + caps lock + alt + cmd)
 
{$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).


For encryption in Python there are difficulties. 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).


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''Python version'''
| '''Python Version'''
|  
 
| -
| <code>import sys
| <code>import sys
ver = sys.version_info</br>
ver = sys.version_info</br>
vs.Message(repr(ver))</br>
vs.Message(repr(ver))</br>
</code>
</code>


|- style="vertical-align: top;"
|- style="vertical-align: top;"
| '''"import vs"'''
|
| <code>import vs</code> # do I need this?


|- style="vertical-align: top;"
|  '''Python Caching'''
|  '''Caching'''
: some caching prevents your script to reflect changes:
: some caching prevents your script to reflect changes:
|  
|  
| varPersistentPythonEngine = 412 { Boolean }
| varPersistentPythonEngine = 412 { Boolean }
In the SDK starting from VW 2014 we can read:
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.''
''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)
|}
|}


Line 289: Line 607:


== Errors ==
== Errors ==
Python Error Messages:  
Python Error Messages:  



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:
AlrtDialog('test 1')
AlrtDialog('test 2') { error }
AlrtDialog('test 1'); AlrtDialog('test 2'); { OK }
AlrtDialog('test 1') AlrtDialog('test 2') { error }
Only needed for multiple statements on one line:
vs.AlrtDialog('test 1')
vs.AlrtDialog('test 2') # OK
vs.AlrtDialog('test 1'); vs.AlrtDialog('test 2') # OK
vs.AlrtDialog('test 1') vs.AlrtDialog('test 2') # error
Error Message: SyntaxError: invalid syntax


Case sensitivity Not case sensitive:
AlrtDialog('test'); { OK }
alrtDialog('test'); { OK }
alrtdialog('test'); { OK }
ALRTDIALOG('test'); { OK }
Case sensitive:
vs.AlrtDialog('test') # OK
vs.alrtDialog('test') # error
vs.alrtdialog('test') # error
vs.ALRTDIALOG('test') # error
Error Message: AttributeError: 'module' object has no attribute 'vs.ALRTDIALOG'


Brakets
don't forget in python the empty brakets for routines without parameters, or it will rise cryptical errors.
No brakets
FSActLayer; { no parameters: brakets not needed }
MySubroutine;
Brakets
vs.FSActLayer()
MySubroutine()
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:
Both Concat and Message support multiple variable types and convert them into string.
t := Concat(10, ' fingers'); { OK }
Message(10, ' fingers'); { OK }
AlrtDialog(Concat(10, ' fingers')); { OK }
v.x := 1; v.y := 0; { vector v }
AlrtDialog(Concat(v)); { returns 1, 0 }
Doesn't support implicit conversion:
For example an integer won't automatically be converted into string. Wrap it in vs.Concat or vs.Message, alternatively perform the needed conversion.
t = 10 + ' fingers' # error
t = vs.Concat(10, ' fingers') # OK
vs.Message(10, ' fingers') # OK
vs.AlrtDialog(str(10) + ' fingers') # OK
vs.AlrtDialog(10 + ' fingers') # error
Error Message: TypeError: unsupported operand type(s) for +: 'int' and 'str'
Vectors are tuples and won't resolve with vs.Concat():
vs.AlrtDialog(str( v )) # OK: returns tuple v as v[0], v[1]
vs.AlrtDialog(vs.Concat( v )) # returns an empty string
vs.AlrtDialog(vs.Concat(str( v )) # OK: returns the values in the tuple v


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:

  • Variables must be declared
  • Subroutines "see" their own variables and those of any parent function/procedure where they are contained.
{ 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:
  • Variables must NOT be declared
  • Subroutines create automatically a local instance of any used variable.
# 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


NIL handles
h <> NIL
Recommended
h is None
h is not None
The "==" or "!=" form is not recommended
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
Color Index
SetPenFore(h, RGBToColorIndex(65535, 0, 0));
PenFore(RGBToColorIndex(65535, 0, 0));
RGB
SetPenFore(h, 65535, 0, 0);
Color Index
vs.SetPenFore(h, vs.RGBToColorIndex(65535, 0, 0))
RGB in Tuple
vs.SetPenFore(h, (65535, 0, 0))
Hex in Tuple
vs.SetPenFore(h, (0xFFFF, 0, 0))
Warning
don't forget the brakets:
vs.PenFore((65535, 0, 0)) correct
vs.PenFore(65535, 0, 0) fails
Error Message: - none! be careful! -

Includes / Imports

Description Vectorscript Pascal Vectorscript Python
Includes/Import

Load libraries of code

Custom (own) Libraries
  • use the term $INCLUDE
  • pass the appropriate path to your code and/or code libraries
$INCLUDE libraryFolder\libraryFile.vss
$INCLUDE libraryFolder\libraryFile.px
Note
the path to the vss/px file is relative to the running .vso/.vst/.vsm plug-in file.

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.


External libraries
not possible

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.

Custom (own) Libraries
Set up in the Script Options (see below) one or more environmental paths from which all active plug-ins will resolve imports
  • choose a library folder with your code and/or libraries in the Plug-Ins Script Options
  • use the term Import
import main 
# import the script main.py, in this case it resides in the same folder as the running .vso/.vsm/.vst plug-in
Other Libraries
import vs
import math
from subprocess import Popen, PIPE
But not
import vs, math # not recommended
Absolute path
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
Relative path
from . import sibling
from .sibling import example

See more in: www.python.org/dev/peps/pep-0008/#imports

Note
the paths listed through the Script Options dialog are valid for all plug-ins. One might be mislead into thinking that the list affected only one plug-in.

External library
include + name of the chosen library
import sys
ver = sys.version_info
vs.Message(repr(ver))
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.
  • link to the API using
import vs

Intellisense from Visual Studio Code:


Encryption

See encryption for more infos.

vss
-
px
encrypt upon launching the Encryption command (in the Plug-in Manager dialog, click on the button Edit Script... while pressing shift + caps lock + alt + cmd)
{$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

ver = sys.version_info
vs.Message(repr(ver))


Python Caching
some caching prevents your script to reflect changes:
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.

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

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