# This script runs a loco back and forth between blocks 1 and 3 of a # three block (1,2,3) section. # The frame contains two JTextFields, a scroll field and two buttons. # The start button is inactive until data has been entered. # A AbstractAutomaton is used to get a throttle and run the loco. # A PropertyChangeListener is used to report block information. It could have been # with AbstractAutomaton but this allows for independent action with a block change. # A WindowListener is used to report when the window is closing and # allows for the removeal of the PropertyChangeListener and stopping the AbstractAutomaton. # # Portions of this script are taken from JButtonComplexExample.py by Bob Jacobsen # Author: Bill Robinson with help from Bob Jacobsen # $Revision: 1.0 $ 11/4/06 # $Revision: 1.1 $ 11/28/06 Added variable 'num1Way' # Added variable 'beenInBlk2' which is used to solve problem # of multiple direction changes in end blocks. # $Revision: 1.2 $ 12/18/06 Revised the user interface # $Revision: 1.3 $ 1/8/07 Replace the use of StringBuffer because it did not work on a PC import java import javax.swing import java.awt # initialize variables addressChanged = False commandChanged = False block1 = False block2 = False block3 = False blkChange = False beenInBlk2 = True num1Way = 0 # set up a throttle with the loco address class LocoThrot(jmri.jmrit.automat.AbstractAutomaton) : def init(self): scrollArea.setText(scrollArea.getText()+"Getting throttle - ") #add text to scroll field number = int(address.text) if (number > 100) : long = True else : long = False self.throttle = self.getThrottle(number, long) if (self.throttle == None) : scrollArea.setText(scrollArea.getText()+"Couldn't assign throttle! - Run stopped\n") a.stop() else : scrollArea.setText(scrollArea.getText()+"got throttle\n") self.throttle.setIsForward(True) speedUp() return def handle(self): global block1, block2, block3, blkChange, beenInBlk2, num1Way if blkChange == True: # block change has occured # print block1, block2, block3, beenInBlk2 self.waitMsec(2000) # wait while loco crosses block gap if (block2 == False) & (beenInBlk2 == True): # Must be in one of the end blocks and previously in block 2. # This keeps multiple directions changes from happening # do to end blocks becoming active/inactive perhaps due beenInBlk2 = False # to dirty track. slowDown() if self.throttle.getIsForward(): # reverse direction self.throttle.setIsForward(False) else : self.throttle.setIsForward(True) num1Way = num1Way + 1 stNum = java.lang.Integer.toString(num1Way) scrollArea.setText(scrollArea.getText()+"Number of one way trips is ") scrollArea.setText(scrollArea.getText()+stNum+"\n") speedUp() blkChange = False return 1 #continue def slowDown(): a.waitMsec(2000) a.throttle.speedSetting = 0.1 a.waitMsec(2000) a.throttle.speedSetting = 0.0 return def speedUp(): a.waitMsec(2000) a.throttle.speedSetting = 0.1 a.waitMsec(3000) a.throttle.speedSetting = int(command.text) * .01 return # have the text field enable the button when OK def whenAddressChanged(event) : global addressChanged, commandChanged # keep track of whether both fields have been changed if (address.text != "") : # address only changed if a value was entered addressChanged = True # True means the field has changed if (commandChanged and addressChanged) : # if both have been changed enterButton.setEnabled(True) return # have the 2nd text field enable the button when OK also def whenCommandChanged(event) : global addressChanged, commandChanged if (command.text != "") : commandChanged = True if (commandChanged and addressChanged) : enterButton.setEnabled(True) return # define what button does when clicked and attach that routine to the button def whenEnterButtonClicked(event) : global num1Way scrollArea.setText(scrollArea.getText()+"Run started\n") a.start() stopButton.setEnabled(True) enterButton.setEnabled(False) num1Way = 0 return def whenStopButtonClicked(event): scrollArea.setText(scrollArea.getText()+"Slow loco to stop\n") slowDown() a.stop() scrollArea.setText(scrollArea.getText()+"*** Run stopped ***\n") stopButton.setEnabled(False) enterButton.setEnabled(True) return # WindowListener is a interface class and therefore all of it's # methods should be implemented even if not used to avoid AttributeErrors class WinListener(java.awt.event.WindowListener): def windowClosing(self,event): #print"window closing" s1.removePropertyChangeListener(m) # remove the listener s2.removePropertyChangeListener(m) s3.removePropertyChangeListener(m) fr.dispose() # close the pane (window) a.stop() # stop the AbstractAutomaton def windowActivated(self,event): return def windowDeactivated(self,event): return def windowOpened(self,event): return def windowClosed(self,event): return #To detect block status, first define the listener. class MyListener(java.beans.PropertyChangeListener): def propertyChange(self, event): global block1, block2, block3, blkChange, beenInBlk2 blkChange = True if(event.source.systemName == "LS1"): # if sensor 1 changed if(s1.knownState == ACTIVE): #and it went active scrollArea.setText(scrollArea.getText()+"Block 1 active\n") #then block1 = True else : block1 = False elif(event.source.systemName == "LS2"): # if sensor 2 changed if(s2.knownState == ACTIVE): scrollArea.setText(scrollArea.getText()+"Block 2 active\n") block2 = True beenInBlk2 = True else : block2 = False elif(event.source.systemName == "LS3"): # if sensor 3 changed if(s3.knownState == ACTIVE): scrollArea.setText(scrollArea.getText()+"Block 3 active\n") block3 = True else : block3 = False # Second, attach that listener to a particular sensor. The variable m # is used to remember the listener so we can remove it later. m = MyListener() s1 = sensors.provideSensor("1") s2 = sensors.provideSensor("2") s3 = sensors.provideSensor("3") # set listeners s1.addPropertyChangeListener(m) s2.addPropertyChangeListener(m) s3.addPropertyChangeListener(m) # start to initialise the GUI # create buttons and define action enterButton = javax.swing.JButton("Start the Run") enterButton.setEnabled(False) # button starts as grayed out (disabled) enterButton.actionPerformed = whenEnterButtonClicked stopButton = javax.swing.JButton("Stop") stopButton.setEnabled(False) # button starts as grayed out (disabled) stopButton.setToolTipText("Stops the run - there is a delay as the loco slows") stopButton.actionPerformed = whenStopButtonClicked address = javax.swing.JTextField(5) # sized to hold 5 characters, initially empty address.actionPerformed = whenAddressChanged # if user hit return or enter address.focusLost = whenAddressChanged # if user tabs away address.requestFocusInWindow() # create the second text field similarly command = javax.swing.JTextField(5) # sized to hold 5 characters command.setToolTipText("Max speed is a number from 1 to 50") command.actionPerformed = whenCommandChanged command.focusLost = whenCommandChanged # create a text area scrollArea = javax.swing.JTextArea(10, 30) # define a text area with it's size scrollArea.setText("Put the loco in block 2\nEnter the loco number and max speed\n") srcollField = javax.swing.JScrollPane(scrollArea) # put text area in scroll field # create a panel to put the scroll area in # a borderlayout causes the scroll area to fill the space as the window is resized midPanel = javax.swing.JPanel() # midPanel.setBorder(javax.swing.BorderFactory.createMatteBorder(1,8,1,8, java.awt.Color.white)) midPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1,8,1,8)) midPanel.setLayout(java.awt.BorderLayout()) midPanel.add(srcollField) # create a frame to hold the buttons and fields # also create a window listener. This is used mainly to remove the property change listener # when the window is closed by clicking on the window close button w = WinListener() fr = javax.swing.JFrame("Run Loco") # argument is the frames title fr.contentPane.setLayout(javax.swing.BoxLayout(fr.contentPane, javax.swing.BoxLayout.Y_AXIS)) fr.addWindowListener(w) addressPanel = javax.swing.JPanel() addressPanel.setLayout(java.awt.FlowLayout(2)) # 2 is right align for FlowLayout addressPanel.add(javax.swing.JLabel("Address")) addressPanel.add(address) commandPanel = javax.swing.JPanel() commandPanel.setLayout(java.awt.FlowLayout(2)) # 2 is right align for FlowLayout commandPanel.add(javax.swing.JLabel("Max Speed")) commandPanel.add(command) temppanel1 = javax.swing.JPanel() temppanel1.setLayout(javax.swing.BoxLayout(temppanel1, javax.swing.BoxLayout.PAGE_AXIS)) temppanel1.add(addressPanel) temppanel1.add(commandPanel) butPanel = javax.swing.JPanel() butPanel.setLayout(javax.swing.BoxLayout(butPanel, javax.swing.BoxLayout.PAGE_AXIS)) butPanel.add(enterButton) butPanel.add(stopButton) buttonPanel = javax.swing.JPanel() buttonPanel.add(butPanel) blankPanel = javax.swing.JPanel() blankPanel.setLayout(java.awt.BorderLayout()) entryPanel = javax.swing.JPanel() entryPanel.setLayout(javax.swing.BoxLayout(entryPanel, javax.swing.BoxLayout.LINE_AXIS)) entryPanel.add(temppanel1) entryPanel.add(buttonPanel) entryPanel.add(blankPanel) # create the top panel # it is a 1,1 GridLayout used to keep all controls stationary when the window is resized topPanel = javax.swing.JPanel() topPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1,8,8,8)) topPanel.setLayout(java.awt.GridLayout(1,1)) topPanel.add(entryPanel) # create a ppanel to give some space under the scrolling field bottomPanel = javax.swing.JPanel() # bottomPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1,8,1,8)) # Put contents in frame and display fr.contentPane.add(topPanel) fr.contentPane.add(midPanel) fr.contentPane.add(bottomPanel) fr.pack() fr.show() # create one of these a = LocoThrot()