Writing Python Scripts

Basics

Python scripts executed on Choreonoid run on the Python interpreter embedded in Choreonoid. There they can directly access the objects of the running Choreonoid, such as items and views.

In a script, first import the modules of the cnoid package corresponding to the functions to use.

from cnoid.Util import *
from cnoid.Base import *
from cnoid.Body import *
from cnoid.BodyPlugin import *

For the overview of each module, refer to “Python Bindings” in Overview of Python Scripting. When a script is executed on Choreonoid, the path of the cnoid package is set in the module search path in advance, so you can import the modules as they are.

The classes and methods of the Python bindings mostly correspond to those of the C++ libraries, and they are basically available with the same names as in C++. Also, some of the accessors written as getX / setX in C++ can be referenced and assigned as properties in Python (e.g. body.numJoints, item.name).

Manipulating Items

The basis of automating Choreonoid operations is the manipulation of items.

The root item at the top of the item tree can be obtained as follows.

from cnoid.Base import *

rootItem = RootItem.instance

To create a new item and place it in the tree, create an object of the item class and add it to the parent item with addChildItem.

from cnoid.BodyPlugin import *

worldItem = WorldItem()
RootItem.instance.addChildItem(worldItem)

To obtain an existing item, use findItem. You can search by the name of the item (or by the path when tracing the hierarchy), or by the type of the item.

# search by name / path
robotItem = RootItem.instance.findItem("World/SR1")

# search by type (the first item found is returned)
simulatorItem = RootItem.instance.findItem(SimulatorItem)

The selection and check states of items can also be manipulated as follows.

simulatorItem.setSelected(True)
robotItem.setChecked(True)

For items corresponding to files, you can load a file with load. Path variables such as ${SHARE} (the share directory of Choreonoid), ${HOME} (the home directory of the user), and ${PROJECT_DIR} (the directory of the project file) can be used in file paths.

robotItem = BodyItem()
robotItem.load("${SHARE}/model/SR1/SR1.body")

Example of Constructing a Project

The following is an example that constructs the project of a walking simulation of the SR1 model with a script and starts the simulation. This is the content of the script sample/python/SR1Walk.py included in the Choreonoid samples.

from cnoid.Util import *
from cnoid.Base import *
from cnoid.Body import *
from cnoid.BodyPlugin import *
import math

worldItem = WorldItem()
RootItem.instance.addChildItem(worldItem)

robotItem = BodyItem()
robotItem.load("${SHARE}/model/SR1/SR1.body")

robot = robotItem.body
robot.rootLink.setTranslation([0.0, 0.0, 0.7135])

q = [  0.0, -2.1, 0.0,   4.5, -2.4, 0.0,
      10.0, -0.2, 0.0, -90.0,  0.0, 0.0, 0.0,
       0.0, -2.1, 0.0,   4.5, -2.4, 0.0,
      10.0, -0.2, 0.0, -90.0,  0.0, 0.0, 0.0,
       0.0,  0.0, 0.0  ]

for i in range(robot.numJoints):
    robot.joint(i).q = math.radians(q[i])

robot.calcForwardKinematics()
robotItem.storeInitialState()

controllerItem = SimpleControllerItem()
controllerItem.setController("SR1WalkPatternController")
robotItem.addChildItem(controllerItem)
robotItem.setChecked(True)
worldItem.addChildItem(robotItem)

floorItem = BodyItem()
floorItem.load("${SHARE}/model/misc/floor.body")
worldItem.addChildItem(floorItem)

simulatorItem = AISTSimulatorItem()
simulatorItem.setTimeStep(0.002)
simulatorItem.setActiveControlTimeRangeMode(True)
worldItem.addChildItem(simulatorItem)
simulatorItem.setSelected(True)

simulatorItem.startSimulation()

This script performs the following processes.

  1. Create a world item and place it under the root item

  2. Create a body item and load the model file of SR1

  3. Obtain the body model (Body object) from the body item, set the position of the root link and the angles of the joints, and update the whole body posture with the forward kinematics calculation ( calcForwardKinematics )

  4. Store the current posture as the initial state of the simulation with storeInitialState

  5. Place a controller item to which the simple controller that plays the walking pattern is set, as a child item of the robot

  6. Place the floor model and the AIST simulator item

  7. Start the simulation with startSimulation

In this way, most of the project construction operations performed on the GUI can also be written in scripts.

Working with Simulations

You can also control the execution of simulations for a loaded project. The following is a script that starts a simulation and outputs a summary of the result when the simulation finishes.

from cnoid.Base import *
from cnoid.BodyPlugin import *

def onSimulationFinished(isForced):
    print("The simulation finished at %f [s]." % simulatorItem.currentTime)

simulatorItem = RootItem.instance.findItem(SimulatorItem)
simulatorItem.sigSimulationFinished.connect(onSimulationFinished)
simulatorItem.setRealtimeSyncMode(SimulatorItem.NonRealtimeSync)
simulatorItem.setSelected()
simulatorItem.startSimulation()

Here, the process at the end of the simulation is written by connecting a function to the signal sigSimulationFinished of the simulator item. In this way, as with the C++ API, you can write event-driven processes by connecting functions to signals.

When you wait for the completion of time-consuming processing such as a simulation, always receive it with a signal in this way. If you write a loop that waits for the completion, the event processing of Choreonoid does not proceed and the simulation itself is not processed correctly. See also Batch Execution and Control of the Termination for this.

By combining this script with the --batch option described in Executing Scripts from the Command Line, it becomes possible to execute simulations in a batch manner. In this case Choreonoid also exits automatically when the simulation finishes, so no description for the termination is necessary in the script. Also, if you want to execute scripts in conjunction with the start and end of a simulation, the Python Simulation Script is available as well.

Outputting Messages

This section describes how to inform the user of the execution status and errors of a script.

Where the Output of print Goes

The output of the Python print function is put to the message view. This is because the Python plugin replaces sys.stdout and sys.stderr with the objects for the message view when the interpreter is initialized, so the output does not go directly to the terminal. sys.stderr is treated in the same way, so the traceback of an exception is also put to the message view.

In addition, when the non-interactive mode is enabled, the contents of the message view are also put to the standard output. Hence the final destination of the output is as follows.

Execution form

Destination of the output of print

Ordinary GUI execution

The message view only. The output does not go to the terminal.

With --non-interactive

The message view and the standard output

With --batch

Same as above (because it includes --non-interactive)

With --headless

Same as above. Since there is no window, the output is practically put to the standard output only.

Refer to Executing Scripts from the Command Line for these options.

Note that the output of the code entered in the Python Console is treated differently. See that page for the details.

Outputting Messages with MessageOut

print is easy to use, but it cannot specify the type of the output. When you want to use different types, use the MessageOut class, which is also used in Choreonoid itself. The standard destination of the output is obtained with MessageOut.master.

from cnoid.Base import *
from cnoid.Util import *

mout = MessageOut.master

mout.putln("The processing has started.")
mout.putHighlightedln("An important notice")
mout.putWarningln("The setting is not valid, so the default value is used.")
mout.putErrorln("The file could not be read.")

The functions are used as follows.

Function

Purpose

putln

An ordinary message. It is used to inform the progress of the processing and so on.

putHighlightedln

An ordinary message that you especially want to stand out.

putWarningln

A warning. It informs that the processing could be continued but there is something to note. The prefix “Warning: “ is added.

putErrorln

An error. It informs that the processing failed and its purpose was not achieved. The prefix “Error: “ is added.

In the message view, all the messages other than the ordinary ones are shown in red. In other words, the highlighted messages, the warnings and the errors are all shown in red, and the warnings and the errors additionally have their prefixes.

The type of the output can also be specified with an argument.

mout.putln("An error message", MessageOut.MessageType.Error)

Note

When an error is output, the return code of Choreonoid becomes 1. Warnings and highlighted messages do not affect the return code. This difference matters when the success or failure of a batch execution is determined from a shell script. See Batch Execution and Control of the Termination for the details.

Reflecting the Messages on the Screen

An output message is not necessarily shown on the screen at that moment. The operation that actually reflects the accumulated messages on the message view is called “flush”.

The functions with the “ln” suffix introduced above output a line feed and also perform a flush. Hence the messages are shown each time if you use them, which is usually what you want.

On the other hand, the functions without the “ln” suffix, namely put, putHighlighted, putWarning and putError, are also available, and they perform neither a line feed nor a flush. They are used when a message of one line is composed and output in several steps. In that case, call flush when the output of the line has finished.

mout.put("Processing")
mout.put(" ... ")
mout.put("done.")
mout.flush()

This especially matters when you want to show the progress of time-consuming processing. The screen of Choreonoid is not updated while a script continues its processing. By using putln or calling flush explicitly, the messages up to that point are shown.

Note

A flush involves the update processing of the screen, so repeating it at a very short interval decreases the processing speed.

When you output a message of several lines, repeating putln for each line performs a flush that many times. To avoid it, include the line feed characters in the messages, output them with put, and flush only once at the end.

mout.put("Line 1\n")
mout.put("Line 2\n")
mout.put("Line 3\n")
mout.flush()

If the messages to output are known in advance, composing them into a single string is the simplest way.

mout.putln("Line 1\nLine 2\nLine 3")

When you output a message in every iteration of a loop, similarly consider combining the outputs or reducing the frequency of the output.

Note that the output of print is automatically flushed each time, so you do not have to be aware of this point for it.

Not only the messages but the whole GUI is not updated while a script is processing. See the next section, Updating the GUI while a Script Is Processing, for this.

Updating the GUI while a Script Is Processing

The GUI of Choreonoid is updated while Choreonoid itself has the control. The display of the GUI is not updated while a script continues its processing.

For example, the following script changes the angles of all the joints of a robot and notifies the change to the item.

from cnoid.Base import *
from cnoid.BodyPlugin import *
import math, time

bodyItem = RootItem.instance.findItem(BodyItem)
body = bodyItem.body

for i in range(body.numJoints):
    body.joint(i).q = math.radians(40.0)
body.calcForwardKinematics()
bodyItem.notifyKinematicStateChange()

time.sleep(20)   # instead of time-consuming processing

Although the change of the posture is notified by notifyKinematicStateChange, the posture of the robot on the scene view does not change while the script continues its processing afterwards. The display is updated after the script has finished its processing and the control has returned to Choreonoid.

To update the display in the middle of the processing, call App.updateGui.

from cnoid.Base import *

bodyItem.notifyKinematicStateChange()
App.updateGui()      # the display is updated here

With this, the changes made so far are reflected on the screen. When you want to animate the posture of a robot by changing it little by little with a script, you have to call App.updateGui for each change.

The flush of the messages described in the previous section is a kind of this screen update. putln and flush update the display of the message view, so use App.updateGui when you want to update the whole GUI including the scene view.

Note

App.updateGui performs the event processing of the GUI, so the user operations and the processes such as timers may be executed during it. Calling it frequently also decreases the processing speed.

Note

When you perform time-consuming processing, it is preferable to divide the processing using signals or timers and proceed while returning the control to Choreonoid, rather than keeping the control on the script side. See also Batch Execution and Control of the Termination.

Using the Qt Classes

With the cnoid.QtCore, cnoid.QtGui, and cnoid.QtWidgets modules, the major classes of Qt can be used from scripts. The following is the content of the sample sample/python/TimerSample.py, which is an example of executing a process every second using QTimer.

from cnoid.QtCore import *

class TimerSample:
    def __init__(self):
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.doSomething)
        self.timer.start()
        self.counter = 0

    def doSomething(self):
        print("do something %d" % self.counter)
        self.counter += 1
        if self.counter == 10:
            self.timer.stop()

timerSample = TimerSample()

In addition, it is also possible to build your own GUI consisting of buttons, dialogs, and so on with scripts using the widget classes of QtWidgets.

How to Investigate Classes and Functions

For the classes and functions that can be used for writing Python scripts, you can import each module on the Python console and check them with the standard Python functions dir() and help().

>>> import cnoid.Body
>>> dir(cnoid.Body)
>>> help(cnoid.Body.Body)

You can also use the input completion (Tab key) of the Python Console to try things out while checking the methods of a class on the spot.

Sample Scripts

Sample Python scripts are stored in the sample/python/ directory of the Choreonoid source. Refer to them when writing your own scripts. The following are some of them.

File

Contents

SR1Walk.py

Constructs the project of a walking simulation of the SR1 model and executes it.

StartSimulation.py

Starts the simulation of a loaded project.

StartSimulationAndQuitWhenFinished.py

Starts a simulation and exits Choreonoid when it finishes. (The same thing can also be done with the --batch option described in Executing Scripts from the Command Line.)

KinematicsTest.py

Performs a test of kinematics calculations on a body model.

BodyShaker.py

Shakes the selected body models. This is also an example of adding a custom toolbar and using signals and timers.

TimerSample.py

An example of periodic processing using QTimer.