SwingWorker Examples in Jython

DocumentationAndEducation


The SwingExamples page shows some good examples of using Swing components from Jython.

However, all of the examples perform the majority of their processing on the Swing Event Dispatch Thread and this is not a good technique to use in a real world application which needs to do any significant processing.

Very briefly, a Swing program normally comprises of three different types of thread:

Only the Event Dispatch Thread should access Swing components (with certain exceptions). Swing components should not even be created by other threads -- including the Initial Threads.

A good description of these threads is given in the Concurrency in Swing lesson in the Swing Tutorial.

The SwingWorker class described in the above lesson is only available in Java 6. However, it has been backported to earlier versions see https://swingworker.dev.java.net/

The remainder of this section shows a translation of two of the examples from the Swing Tutorial to Jython.

   1 # Flipper.py
   2 
   3 from java.lang import Runnable
   4 from java.util import Random
   5 from java.util.concurrent import ExecutionException
   6 from javax.swing import SwingWorker, SwingUtilities
   7 from javax.swing import BorderFactory
   8 from javax.swing import JButton, JFrame, JTextField
   9 from java.awt import GridBagLayout as awtGridBagLayout
  10 from java.awt import GridBagConstraints as awtGridBagConstraints
  11 from java.awt import Insets as awtInsets
  12 
  13 
  14 ###########################################################################
  15 class FlipTask(SwingWorker):
  16     "Class implementing long running task as a SwingWorker thread"
  17 
  18     #######################################################################
  19     def __init__(self, gui):
  20         self.gui = gui
  21         SwingWorker.__init__(self)
  22 
  23 
  24     def doInBackground(self):
  25         heads = 0
  26         total = 0
  27         random = Random()
  28         while not self.isCancelled():
  29             total += 1
  30             if random.nextBoolean():
  31                 heads += 1
  32             #publish the number of heads and total tosses
  33             self.super__publish([(heads, total)])
  34 
  35 
  36     def process(self, pairs):
  37         pair = pairs[len(pairs)-1]
  38         self.gui.headsText.text = "%d" % pair[0]
  39         self.gui.totalText.text = "%d" % pair[1]
  40         self.gui.devText.text = "%f" % ((1.0*pair[0]/pair[1]) - 0.5)
  41 
  42 
  43     def done(self):
  44         try:
  45             self.get()  #raise exception if abnormal completion
  46         except ExecutionException, e:
  47             raise SystemExit, e.getCause()
  48 
  49 ###########################################################################
  50 class Flipper(JFrame):
  51 
  52     def __init__(self):
  53         JFrame.__init__(self, "Flipper",
  54                     defaultCloseOperation=JFrame.EXIT_ON_CLOSE,
  55                     layout=awtGridBagLayout(),
  56                     )
  57 
  58         self.constraints = awtGridBagConstraints(
  59                     insets=awtInsets(3, 10, 3, 10)
  60                     )
  61 
  62         self.border = BorderFactory.createLoweredBevelBorder()
  63 
  64         #Make text boxes
  65         self.headsText = self.makeText()
  66         self.totalText = self.makeText()
  67         self.devText = self.makeText()
  68 
  69         #Make buttons
  70         self.startButton = self.makeButton("Start", self.startPressed)
  71         self.stopButton = self.makeButton("Stop", self.stopPressed)
  72         self.stopButton.enabled = False
  73 
  74         #Display the window.
  75         self.pack()
  76         self.visible = True
  77 
  78 
  79     def makeText(self):
  80         t = JTextField(
  81                 20,
  82                 editable=False,
  83                 horizontalAlignment=JTextField.RIGHT,
  84                 border = self.border,
  85                 )
  86         self.add(t, self.constraints)
  87         return t
  88 
  89 
  90     def makeButton(self, caption, action):
  91         b = JButton(caption, actionPerformed=action)
  92         self.add(b, self.constraints)
  93         return b
  94 
  95 
  96     def _setButtonStates(self, started):
  97         self.stopButton.enabled = started
  98         self.startButton.enabled = not started
  99 
 100 
 101     def stopPressed(self, e):
 102         self._setButtonStates(started=False)
 103         self.flipTask.cancel(True)
 104         self.flipTask = None
 105 
 106 
 107     def startPressed(self, e):
 108         self._setButtonStates(started=True)
 109         self.flipTask = FlipTask(self)
 110         self.flipTask.execute()
 111         
 112 
 113 ###########################################################################
 114 class Runnable(Runnable):
 115     def __init__(self, runFunction):
 116         self._runFunction = runFunction
 117 
 118 
 119     def run(self):
 120         self._runFunction()
 121 
 122 ###########################################################################
 123 if __name__ == '__main__':
 124     SwingUtilities.invokeLater(Runnable(Flipper))

   1 # ProgressBarDemo.py
   2 
   3 from java.lang import InterruptedException, Runnable, Thread
   4 from java.beans import PropertyChangeListener
   5 from java.util import Random
   6 from java.util.concurrent import ExecutionException
   7 from javax.swing import SwingWorker, SwingUtilities
   8 from javax.swing import BorderFactory
   9 from javax.swing import JButton, JFrame, JPanel, JProgressBar, \
  10                     JScrollPane, JTextArea
  11 from java.awt import Toolkit as awtToolkit
  12 from java.awt import BorderLayout as awtBorderLayout
  13 from java.awt import Cursor as awtCursor
  14 from java.awt import Insets as awtInsets
  15 
  16 ###########################################################################
  17 class Task(SwingWorker):
  18 
  19     def __init__(self, gui):
  20         self.gui = gui
  21         SwingWorker.__init__(self)
  22 
  23     def doInBackground(self):
  24         random = Random()
  25         progress = 0
  26         #Initialize progress property.
  27         self.super__setProgress(progress)
  28         while progress < 100:
  29             #Sleep for up to one second.
  30             try:
  31                 Thread.sleep(random.nextInt(1000))
  32             except InterruptedException, e:
  33                 pass
  34 
  35             #Make random progress.
  36             progress += random.nextInt(10)
  37             self.super__setProgress(min(progress, 100))
  38 
  39 
  40     def done(self):
  41         try:
  42             self.get()  #raise exception if abnormal completion
  43             awtToolkit.getDefaultToolkit().beep()
  44             self.gui.taskOutput.append("Done!\n")
  45             self.gui.startButton.enabled = True
  46             self.gui.cursor = None  #turn off the wait cursor
  47         except ExecutionException, e:
  48             raise SystemExit, e.getCause()
  49 
  50 
  51 
  52 ###########################################################################
  53 class ProgressBarDemo(JPanel, PropertyChangeListener):
  54     def __init__(self):
  55         JPanel.__init__(self, awtBorderLayout(),
  56                         border = BorderFactory.createEmptyBorder(20, 20, 20, 20)
  57                         )
  58 
  59         #Create the demo's UI.
  60         self.startButton = JButton("Start", actionPerformed=self.startPressed)
  61 
  62         self.progressBar = JProgressBar(0, 100, value=0, stringPainted=True)
  63 
  64         self.taskOutput = JTextArea(5, 20,
  65                             margin=awtInsets(5,5,5,5),
  66                             editable=True
  67                             )
  68 
  69         panel = JPanel()
  70         panel.add(self.startButton)
  71         panel.add(self.progressBar)
  72 
  73         self.add(panel, awtBorderLayout.PAGE_START)
  74         self.add(JScrollPane(self.taskOutput), awtBorderLayout.CENTER)
  75 
  76 
  77     def startPressed(self, e):
  78         "Invoked when the user presses the start button"
  79         self.startButton.enabled = False
  80         self.cursor = awtCursor.getPredefinedCursor(awtCursor.WAIT_CURSOR)
  81         #Instances of javax.swing.SwingWorker are not reusuable, so
  82         #we create new instances as needed.
  83         task = Task(self)
  84         task.addPropertyChangeListener(self)
  85         task.execute()
  86 
  87 
  88     def propertyChange(self, e):
  89         # Invoked when task's progress property changes.
  90         if e.propertyName == "progress":
  91             self.progressBar.value = e.newValue
  92             self.taskOutput.append("Completed %d%% of task\n"% e.newValue)
  93 
  94 
  95 def createAndShowGUI():
  96     # Create the GUI and show it. As with all GUI code, this must run
  97     # on the event-dispatching thread.
  98     frame = JFrame("ProgressBarDemo",
  99                     defaultCloseOperation = JFrame.EXIT_ON_CLOSE,
 100                     )
 101     #Create and set up the content pane.
 102     newContentPane = ProgressBarDemo()
 103     newContentPane.setOpaque(True)      #content panes must be opaque
 104     frame.contentPane = newContentPane
 105 
 106     #Display the window.
 107     frame.pack()
 108     frame.visible = True
 109 
 110 ###########################################################################
 111 class Runnable(Runnable):
 112     def __init__(self, runFunction):
 113         self._runFunction = runFunction
 114 
 115 
 116     def run(self):
 117         self._runFunction()
 118 
 119 ###########################################################################
 120 if __name__ == '__main__':
 121     SwingUtilities.invokeLater(Runnable(createAndShowGUI))

SwingWorker (last edited 2008-11-15 09:15:59 by localhost)