JMRI: Scripting Examples
The JMRI distributions come with a "jython" directory that contains a few examples. As of the time of writing these are:
- SampleSound.py
- A straight-forward set of statements to load and play a sound. There are some comments on how to add various options.
- SetTurnouts.py
- Sets a couple of turnouts to specific positions, waiting between each operation. Because we want to have the rest of the program keep running, there are a few more statements involved than just the commands to throw the turnouts. There's also a separate page that talks more about this.
- ListenerExample.py
- Example of listing to a Turnout so that the script can do something every time the Turnout's state changes.
- Sensor-sound.py
- An example of using a "listener" to play a sound every time a particular sensor changes.
- AutomatonExample.py
- A simple automation example, this runs a train back and forth between two blocks.
- OpsProgExample.py
- A more complicated automation example. When a given sensor changes, this script will use ops-mode programming (programming on the main) to change the momentum value for a particular locomotive.
- SigletExample.py
- An example of driving a signal from a script.
- JButtonExample.py
- This is an example of adding an object to the GUI, in this case a button. When clicked, it doesn't really do anything except print a message, but it is an example after all!
The rest of this page contains examples of specific techniques.
Invoking another script file from a script
execfile("filename.py");
Loading a panel file from within a script
jmri.InstanceManager.configureManagerInstance().load(java.io.File("filename.xml"))
Communicating between scripts
def printStatus() : print "x is", x print "y is", y print "z is", z return x = 0 y = 0 z = 0Once that file has been executed, any later script can invoke the
printStatus() routine whenever needed.
x,
y, and z variables are available to
anybody.
This can lead to obscure bugs if two different routines
are using a variable of the same name, without realizing that
they are sharing data with each other. Putting your code
into "classes" is a way to avoid that.
Can a script wait for more than one thing to change?
self.waitChange([self.sensorA, self.sensorB, self.sensorC])
where you've previously defined each of those "self.sensorA" things
via a line like:
self.sensorA = sensors.provideSensor("21")
You can then check for various combinations like:
if self.sensorA.knownState == ACTIVE : print "The plane! The plane!" elif self.sensorB.knownState == INACTIVE : print "Would you believe a really fast bird?" else print "Nothing to see here, move along..."(I haven't actually typed these into a script & run it, so there might be typos, sorry)
Listening to more than one Turnout
A single routine can listen to more than one Turnout, Sensor, etc.
If you retain a reference to your listener object, you can attach it to more than one object:
m = MyListener()
turnouts.provideTurnout("12").addPropertyChangeListener(m)
turnouts.provideTurnout("13").addPropertyChangeListener(m)
turnouts.provideTurnout("14").addPropertyChangeListener(m)
But how does the listener know what changed?
A listener routine looks like this:
class MyListener(java.beans.PropertyChangeListener):
def propertyChange(self, event):
print "change",event.propertyName
print "from", event.oldValue, "to", event.newValue
print "source systemName", event.source.systemName
print "source userName", event.source.userName
When the listener is called, it's given an object (called event above) that contains the name of the property that changed, plus the old and new values of that property.
You can also get a reference to the original object that changed via "name", and then do whatever you'd like through that. In the example above, you can retrieve the systemName, userName (or even other types of status).
Playing a sound
Note that if more than one sound is playing at a time, the program combines them as best it can. Generally, it does a pretty good job.
You can combine the play() call with other logic to play a sound when a Sensor changes, etc. Ron McKinnon provided an example of doing this. It plays a railroad crossing bell when the sensor goes active.
# It listens for changes to a sensor,
# and then plays a sound file when sensor active
import jarray
import jmri
# create the sound object by loading a file
snd = jmri.jmrit.Sound("resources/sounds/Crossing.wav")
class SensndExample(jmri.jmrit.automat.Siglet) :
# Modify this to define all of your turnouts, sensors and
# signal heads.
def defineIO(self):
# get the sensor
self.Sen1Sensor = sensors.provideSensor("473")
# Register the inputs so setOutput will be called when needed.
self.setInputs(jarray.array([self.Sen1Sensor], jmri.NamedBean))
return
# setOutput is called when one of the inputs changes, and is
# responsible for setting the correct output
#
# Modify this to do your calculation.
def setOutput(self):
if self.Sen1Sensor.knownState==ACTIVE:
snd.play()
return
# end of class definition
# start one of these up
SensndExample().start()