Skip to content

Latest commit

 

History

History
260 lines (231 loc) · 19.3 KB

File metadata and controls

260 lines (231 loc) · 19.3 KB

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 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, available at python.org. 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), search for 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 the path to the above-mentioned script library folder (_VS).
    • Python > Auto Complete Extra Paths > Edit Settings Json: enter the path to the script library folder, it will look roughly like this:
"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 (Intellisense) 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.

IncludePyAPI

Intellisense from Visual Studio Code:

IncludePyIntellisense

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. PythonIndent1 Error Message: unindent does not match any outer indentation level. You added an extra indent. PythonIndent2 Error Message: unexpected indent
Semicolon Always needed:
AlrtDialog('test 1'); AlrtDialog('test 2');
Only needed for multiple statements on one line:
vs.AlrtDialog('test 1')
vs.AlrtDialog('test 2')
Case sensitivity Not case sensitive Case sensitive:
vs.AlrtDialog('test') # OK
vs.alrtDialog('test') # error
Brackets No brackets
FSActLayer;
Brackets required
vs.FSActLayer()
FOR statements Runs including last value:
FOR i := 1 TO 3 DO
Runs excluding last value:
for i in range(1, 3):
Concatenate text Implicit conversion supported No implicit conversion:
vs.AlrtDialog(str(10) + ' fingers') # OK
Returning Values Results left, VARs and arguments right Results left, VARs left, arguments right
Variable Attributes no built-in in VW
h = vs.FSActLayer()
print(h.type)
Variable scope Global wins over local Local wins over global
NIL handles h <> NIL Recommended:
h is None
Fetching Plug-in Parameters Direct, not case sensitive Prefixed with vs, case sensitive
Using units Yes Needs conversion
Vectors, Points Can be manipulated in place Is Tuple
Colors Color Index:
SetPenFore(h, 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))

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;<br>

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<br><br>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;

PROCEDURE Increment;
BEGIN
gNum := gNum +1;
SysBeep;
END;

BEGIN
gNum := 10;
FOR gIndex := 1 TO 10 DO
Increment;

AlrtDialog(Concat(gNum));
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 +=1
vs.SysBeep

gNum = 10
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
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

Not recommended:
h != None
h != vs.Handle()

See: PEP 8
Fetching Plug-in Parameters Direct, not case sensitive:
MoveTo(PCONTROLPOINT01X, PCONTROLPOINT01Y);
LineTo(PCONTROLPOINT02X, PCONTROLPOINT02Y);
Prefixed with 'vs', case sensitive:
Correct:
vs.MoveTo(vs.PControlPoint01X, vs.PControlPoint01Y)
vs.LineTo(vs.PControlPoint02X, vs.PControlPoint02Y)

Wrong:
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);
Needs conversion:
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:
vs.PenFore((65535, 0, 0)) correct
vs.PenFore(65535, 0, 0) fails (no error message!)

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.

    Include VS

    In the example in the 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 the 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: PEP 8 Imports

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

Include Py

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 API 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: Vectorworks 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
Include Py API

Intellisense from Visual Studio Code:

Include Py Intellisense
Encryption
See encryption for more information.
  • 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 from reflecting 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 threads 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 is 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 are some fascinating list 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