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

From Vectorworks Developer
Jump to navigation Jump to search
No edit summary
m (expand)
 
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 500: Line 517:
</code>
</code>


; VS API: you must always include the VS API (list of Vectorscript routines, plug-in parameters, anything prefixed with ''vs.'') or you won't be able to use it. This file will also allow for intelligent hints while using an Editor capable of it:
; 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
* go to 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  

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