Author: Bob Jacobsen
Keywords: JMRI technical code Python Jython scripting

# Jython access to JMRI Tools

This page discusses access to the JMRI libraries from Jython, a Java version of Python. The emphasis is on using JMRI capabilities from a command-line Jython interpreter. This page is not about scripting with Jython inside JMRI. See the scripting pages if you are more interested in Jython scripting within JMRI.

Note that this page is somewhat out of date; version numbers, etc may not be current.

## Introduction

Python is a widely used scripting language that's available on many types of computers. A Java-based varient, called Jython, has been integrated with JMRI to make it easy to control a model railroad from the command line of a computer. Jython and Python bring some simplifications to using JMRI code. The Java member function:

            turnout.setCommandedState(jmri.Turnout.CLOSED);

can also be expressed in Jython as:

            turnout.commandedState = CLOSED

This results in much easier-to-read code.

There are a lot of useful Python books and online tutorials. For more information on the Jython language and it's relations with Java, the best reference is the Jython Essentials book published by O'Reilly. The jython.org web site is also very useful.

## Using Jython directly

This section of the page is a pile of useful information on controlling JMRI from Jython directly.

Note that you don't need most of this when running the PanelPro.py or similar scripts, as they handle starting a complete application.

### Initialization

JMRI uses the LogJ system extensively for logging information during program execution. To configure that when running with Jython, you should do:

      import org
      org.apache.log4j.PropertyConfigurator.configure("default.lcf")

before starting any of the JMRI classes. The "default.lcf" is the name of the logging control file to be used; it can be omitted.

To make JMRI classes available, you have to do:

      import jmri

As currently set up, this does not run any initialization code to start the hardware connections, etc. One way to do that is to load a configuration file, perhaps one you've created earlier via the PanelPro preferences panel:

      import java.io
      configfile = java.io.File(jmri.util.FileUtil.getPreferencesPath()+"PanelProConfig2.xml")
      jmri.InstanceManager.setConfigureManager(jmri.configurexml.ConfigXmlManager())
      jmri.InstanceManager.getDefault(jmri.ConfigureManager.class).load(configfile)

to activate the JMRI classes and connect to your layout hardware. "PanelProConfig2.xml" is the name of the configuration file from the PanelPro program; you can use another name if desired. The configuration file controls the layout connection, and any other options that may have been set when it was created.

Alternately, if you want to start the complete PanelPro application, including the menu bar, spash screen, etc, you can do:

     import apps
     apps.PanelPro.PanelPro.main([])

This will start the program, including its startup configuration, etc.

To simplify this startup even further, you can do:

     execfile("PanelPro.py")

to invoke these commands.

### Access to JMRI

JMRI uses the factory-pattern extensively to get access to objects. In Java this results in verbose code like

       Turnout t2 = InstanceManager.getDefault(jmri.TurnoutManager.class).newTurnout("LT2", "turnout 2");
       t2.setCommandedState(Turnout.THROWN)

Jython simplifies that by allowing us to provide useful variables, and by shortening certain method calls.

To get access to the SignalHead, Sensor and Turnout managers and the CommandStation object, several shortcut variables are defined in the .py scripts listed above:

- sensors
- signals
- turnouts
- lights
- reporters
- memories
- dcc
- audio

These can then be referenced directly in Jython as

       t2 = turnouts.provideTurnout("12");

       dcc.

Note that the variable t2 did not need to be declared.

Juthon provides a shortcut for parameters that have been defined with Java-Bean-like get and set methods:

       t2.setCommandedState(Turnout.THROWN)

can be written as

       t2.commandedState = THROWN

where the assignment is actually invoking the set method. Also note that THROWN was defined when running the Python script at startup; CLOSED, ACTIVE, INACTIVE, RED, YELLOW and GREEN are also defined.

A similar mechanism can be used to check the state of something:

    >>> print sensors.provideSensor("3").knownState == ACTIVE
    1
    >>> print sensors.provideSensor("3").knownState == INACTIVE
    0

Note that Jython uses "1" to indicate true, and "0" to indicate false, so sensor 3 is currently active in this example

You can also directly invoke methods, e.g. to send a DCC packet to the rails you type:

       dcc.sendPacket([0x01, 0x03, 0xbb], 4)

This sends that three-byte packet four times, and then returns to the command line.

To exit, either ^C from the command line, or use the exit command from the menu.

## Using Python for signal logic and automation

The existing JMRI "Automat" classes provide hooks for user layout automation, including signaling. But they require that you write Java code and compile it into .class files, or write Jython code and import it.

### Termination

Once the JMRI main thread (or Swing GUI thread?) has started, ^D is not sufficient to exist the program. You have to select "Quit" from the actual file menu, or ^C the program.

It would be good to understand what's preventing the program from stopping when it gets the `^D`.