Installation and Usage
This document describes the differences between the Ruby and Python
scripting environments. It assumes that you have some familiarity with
Python and with the Ruby API for scripting Sketchup.
Installation
To use SuPy, you need to put the following items from the 'plugins'
folder of the SuPy distribution into your Sketchup plugins folder. (The
location of the Sketchup plugins folder depends on which version of
Sketchup you have. Consult the Ruby API documentation for your Sketchup
version to find out where it is.)
python.bundle |
|
(for
MacOSX) |
python.so |
|
(for Windows) |
python |
|
folder
and its contents |
supy |
|
folder
and its contents |
supyboot.rb |
|
file |
Locations for Python Modules
The python
folder is where you put your Python scripts. It's placed on the search
path for Python modules, and furthermore, any files there whose names
end in .py
will be
automatically imported when Sketchup starts.
The python
folder contains another folder called lib that is
also placed on the import path. If you have any files that you want to
be able to import but don't want loaded automatically on startup, you
should place them in there.
The
Sketchup Environment as seen from Python
All of the Sketchup-related Ruby modules and classes are available in
the Python builtin namespace, so you can use them from any Python file
without having to import anything.
How
Ruby objects look to Python
For the most part, you can use Ruby classes and objects in a Pythonic
way as you would expect. There are a few things to keep in mind,
however.
Parentheses on method calls
Remember that Ruby has no
notion of accessing an attribute
of an object from outside that object. Things that often look like
attribute accesses in Ruby are actually method calls, and must be
written that way in Python. For example,
model =
Sketchup.active_model() # Parentheses required!
There is one exception to
this, and that's when calling a
method whose name in Ruby ends in '='. SuPy lets you invoke this kind
of method using an attribute assignment, e.g.
my_face.material =
my_material # invokes 'material='
Names ending in ? or !
Some Ruby methods have names
ending in '?' or '!'. When
calling these from Python, as long as there's no ambiguity, you can
just leave off the punctuation, e.g.
my_face.reverse()
# invokes 'reverse!'
If there is more than one
method with the same name but different punctuation, you can use the
following translations.
Ruby |
|
Python | | |
xxx? |
|
is_xxx() | | |
xxx! |
|
xxx_ip() | | for "in-place" |
Calling and Instantiation
Calling a Ruby object will
invoke its call
method if
it has one, otherwise new.
This lets you instantiate most Ruby classes just by calling them, as
you would a Python class:
p = Point3d(1, 2, 3)
Constructors named
something other than 'new' will need to be called explicitly, however.
Passing Blocks
A Ruby method that expects a block argument can be passed a callable Python object by using the keyword body. For example,
def print_ent(ent):
print ent
ents = Sketchup.active_model().active_entities()
ents.each(body = print_ent)
The number of parameters passed to the
Python function depends on what kind of value the Ruby method sends to
it. If it sends a Ruby nil value, the function is called with no
arguments. If it sends a Ruby list, the list is unpacked and passed as
separate arguments. Any other kind of value is passed as a single
argument.
Methods requiring Arrays
Python tuples are converted to Ruby
arrays, so you can pass them directly to methods that require an array.
If you want to pass any other kind of sequence (including a list) you
will have to convert it using the Array() function, which works like its Ruby counterpart.
Booleans
Be careful with boolean
values! Only the Python values True
and False are properly converted into Ruby booleans. Since Ruby regards
almost everything as true, including zeros, empty strings and
empty collections, this can easily get you into trouble. If in doubt,
use
bool()
on values passed to Ruby methods expecting a boolean.
Unit Conversions
The Ruby trick of using things like 5.feet to convert units needs to be converted into a multiplication in Python, e.g. 5 * feet. Suitably-named conversion factors are available as builtins.
Parsing Lengths
There is a to_length() function that parses a string as a length and returns a number. It is equivalent to calling the to_l method of a Ruby number.Implementing Tool Classes in Python
The modules tool and observer contain Python classes that you need to inherit from if you want to implement a custom tool
class or an observer class, for example,
from tool import Tool
class MyTool(Tool):
# Implement tool methods here
Calling
Python from the Ruby Console
There's currently no direct way to enter a Python expression
interactively, but you can access Python modules and their contents
through the Ruby Console.
Firstly, there is a Ruby module called Py that
contains all of the standard Python builtins, e.g. entering
Py.abs(-42)
into the Ruby Console will invoke the Python abs() function.
The Py
module also contains all of the currently imported Python modules. For
example, the example.py
file that comes with the SuPy distribution can be referenced using the Ruby expression
Py.example
The example module defines a function called 'cylinder' which you can
call as follows:
Py.example.cylinder(0, 0, 0,
10, 30)
Python files in the 'lib' subfolder are not automatically
imported, but
you can cause any Python file on the import path to be imported using a
Ruby require command with a name
of the form 'python/modulename'. For example, if you have a
file 'mystuff.py' in the python/lib folder, you can import it using
require 'python/mystuff'
and then reference it as
Py.mystuff
Note that if you're importing a module from a package, the components of the module name should be separated with dots, not slashes, e.g.
require 'python/my_pkg.my_module'
Short expressions and statements can be evaluated using the methods Py.eval and Py.exec:
spams = Py.eval("3 * 'Spam!'")
Py.exec("print 42")
You can also use these features from any Ruby script to access Python
functionality.
Reloading
Python Files
If you make changes to your Python files, you can reload them without
restarting Sketchup by entering the following command at the Ruby
Console:
Py.refresh
This will unload any modules imported from the python or python/lib
folders and then reload any scripts in the
python folder.
You can also attempt to reload a specific module using the normal
Python reload()
function, i.e.
Py.reload(module)
but this is risky, because other modules that have previously imported
from the module being reloaded may not see the new definitions. It's
safer to start with a clean slate and reload everything.
Preserving Data
Sometimes you may want to keep data in a module-level variable that won't get lost when the module is reloaded using Py.refresh. SuPy provides a builtin function keep()
to facilitate this. You call it immediately after the module code that
initialises the variables you want to keep, passing their names as
arguments. For example,my_cache = {}
precious = []
keep('my_cache', 'precious')
When you call keep()
with a particular name for the first time in a Sketchup session, its
current value is stored away in a safe place. The next time you call it
with that name, any value currently in the module under that name is
replaced with the stored value.
The stored names are associated with the name of the module from which the keep() function is called, so that kept variables with the same names in different modules will not be confused.
Bear in mind that if you store any instances of your own classes this way,
and you subsequently change the definition of those classes, the
preserved objects will still be instances of the old classes, and won't
see any of the changes. The same thing applies to any objects that
manage to survive some other way.
If you want to re-initialise a kept variable, you can use the clear_keep()
function to remove the stored value. You pass it the fully-qualified
name of the module (as a string) and a series of variable names to be
cleared. E.g. from the Ruby console,
Py.clear_keep 'my_module', 'precious'
You will then need to reload the module concerned to establish a new initial value.
Python
Standard I/O
Python's standard output and standard error channels are connected to
the Ruby Console.
There is currently no usable standard input for Python. If you try to
read from standard input, your script will probably either hang or raise an exception.
Examples
There is an example script called example.py in the python folder that illustrates how to add a menu item and execute some Sketchup operations in response to it.
There is also a package called more_examples containing some additional example scripts. To use these, either move them into the main python folder, or load them explicitly using
require 'python/more_examples.modulename'