Creating commands/macros with Python

Note: Though my question primarily arises from the lack of RhinoScript in Rhino for Mac, I’ve chosen to post in the Scripting forum because any solutions or discussion should be just as valid on either platform and is intended to be a question about Python, not “wait, how do I get this to work on the Mac”.

The question: What is the most straightforward way of creating multiple new commands/macros in Rhino using only Python?

Here’s the scenario, using Pascal Golay’s Project_Direction as an example.


RhinoScript

Rhino.AddAlias "ProjectDir", "! _NoEcho _-Runscript ProjectDir"
Rhino.AddAlias "ProjectView", "! _NoEcho _-Runscript ProjectView"

Sub ProjectDir()
        Project(0)
End Sub

Sub ProjectView()
        Project(0)
End Sub

Sub Project(pdir)
        ' ...implementation...
End Sub

Python

# In Python we set the alias to launch the script
# because -RunScript doesn't exist in v5 (at least on Mac Rhino).
rs.AddAlias("ProjectDir", "! _NoEcho _-RunPythonScript Project_Direction")
rs.AddAlias("ProjectView", "! _NoEcho _-RunPythonScript Project_Direction")

def ProjectDir():
    project(0)

def ProjectView():
    project(1)

def project(pdir):
    # Implementation

if __name__ == "__main__":
    # If this script contained only one function, we would call it here

My issues are as follows:

  1. There is no [documented] way to pass arguments to a script via RunPythonScript
  2. LastCommandName is the _NoEcho, and something like LastLastCommandName (via CommandHistory) isn’t very nice. Otherwise in the last statement we could write something like if rs.LastCommandName == "ProjectView": ProjectView()
  3. The commands should be independent, i.e. not require a dialog, message, or prompt to take their argument (though I haven’t looked into whether this works on Mac Rhino either).

For immediate use, the simplest solution is probably to split each command up into its own file and then import them into a single module if required. For the sake of discussion though, I’m curious as to whether there are any other options as searching has turned up nil.

Hi,

I’d love to get the answer to this as well. Especially if the answer includes a process to pass arguments to the script!

Like you, I have been forced to create a whole bunch of scripts, one for each alias. I tend to write one large script with multiple functions and a bunch of tiny scripts which do nothing more than import the big one, and call the particular functions with relevant arguments attached. It’s pretty convoluted.

cheers
Peter

If i understand that correctly you want to pass the script options via toolbar buttons so your script somehow need to ask for options and run the according function. The script below will ask for an option value, either “Sphere”, “Point” or “Line”.


import rhinoscriptsyntax as rs

def DoSomething():
    "using custom options callable from buttons"
    myoptions = ["Sphere", "Point", "Line"]
    str = rs.GetString("Choose an option", "Sphere", myoptions)
    if str is not None:
        if str == "Sphere":
            DoSphere()
        elif str == "Point":
            DoPoint()
        elif str == "Line":
            DoLine()
        else:
            return None

def DoSphere(): 
    rs.AddSphere( (0.0, 0.0, 0.0), 10.0)

def DoPoint():
    rs.AddPoint( (1.0, 2.0, 3.0) )

def DoLine():
    rs.AddLine( (1.0, 2.0, 3.0), (4.0, 5.0, 6.0) )

if __name__=="__main__":
    DoSomething()

To run the script and choose to create eg. a sphere you could use this button command:

! _-RunPythonScript "C:\YourPathToTheScript\CustomButtonOptions.py" Sphere

To create a point instead all you need is to change the option value which is written after the script call:

! _-RunPythonScript "C:\YourPathToTheScript\CustomButtonOptions.py" Point

To get rid of the path, you could define a custom search path for all your python scripts. Then the call inside the button would be something like this:

! _-RunPythonScript "CustomButtonOptions.py" Point

does that work ?

c.

My initial thought was that there should be a better way than asking for the value but I see it’s actually a fairly compact way of going about it (and it gets you the dialog for free if users wish to call the script in that way. I’m satisfied with this; it seems to work with all of my scripts which I’ve tested so far. Thanks!

For anyone else who might come across this and wonder, successive calls to rs.Get--- (GetString, GetBoolean, …) appear to take the next space-delimited value in the command string.