How to gracefully interrupt running script when using SwingWorker and JFrame? --- not possible with the thread model

Asked by Mike

*************** a revised version of the provided sample code by RaiMan *************

*** comments:
- with such a solution it is not possible to kill the running SwingWorker thread (the python interpreter simply ignores the bgThread.cancel(True) request. (the problems with the wait() are fixed in the next 1.1.1 - see revised sample)
- to use a stop button, to end a script only works, if the script does not make continuous use of the mouse (you will not get a chance to click the button). a good alternative is a stop hotkey (see revised sample)
- to add a global kill option for the SwingWorker thread, you either have to run the script via subprocess module or implement something like that hack:
https://web.archive.org/web/20130503082442/http://mail.python.org/pipermail/python-list/2004-May/281943.html
- another option is to make the script aware of a stop request (as done in the revised sample below)
- the triggered script should not run into an infinite loop or wait, since this can only be stopped by aborting the wrapping script or even the IDE
- the script should be tested before adding it into such a wrapper, because most errors simply do not produce any helpful output (an option is to make the script run standalone and then use runScript() in the wrapper)
- print statements should only have one parameter, otherwise printouts from the different threads get mangled (use the Python string formatting ""%() to assure that - see sample)
- the start button should only be activated again by code, that is triggered by the thread on termination (in the revised example I use the done() callback for that (otherwise it might be possible to trigger a new run, while one is still running)

from javax.swing import JButton, JFrame, JTextField, JPanel
from java.lang import Runnable, InterruptedException
from java.util.concurrent import ExecutionException
from javax.swing import SwingWorker, SwingUtilities
from java.lang import *

from java.awt.event import ActionListener, WindowAdapter
import threading

class BgTask(SwingWorker):
    def __init__(self, gui):
        self.gui = gui
        SwingWorker.__init__(self)
        self.stopRun = False

    def shouldStop(self, event=None):
      self.stopRun = True

    def someInit(self):
      print "**** started"
      Env.addHotkey("x", KeyModifier.CTRL+KeyModifier.ALT, self.shouldStop)

    # @Override
    def doInBackground(self):
      self.someInit()
      for i in range(10):
        if self.stopRun: break
        # for 1.1.x before 2016-05-21
        #App.pause(1)
        wait(1)
        self.showState("%d ** running - State:" % (i))

    def showState(self, msg):
      if self.stopRun:
        print "%s - shouldStop reveived" % (msg)
      else:
        print "%s - %s" % (msg, self.getState())

    # @Override
    def done(self):
      self.gui.hasEnded()
      print "**** ended"

class Example:
    def __init__(self):
        frame = JFrame("Automated Testing")
        frame.setSize(200, 150)
        frame.addWindowListener(Adapter())
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
        frame.setAlwaysOnTop(True)
        pnl=JPanel()
        frame.add(pnl)
        self.startBtn = JButton('Start Automation',
            actionPerformed=self.startRun)
        self.stopBtn = JButton('Stop',
            actionPerformed=self.stopRun)
        pnl.add(self.startBtn)
        pnl.add(self.stopBtn)
        self.stopBtn.enabled = False
        frame.pack()
        frame.setVisible(True)

    def startRun(self,event):
        self._setButtonStates(True)
        self.bgTask = BgTask(self)
        self.bgTask.execute()

    def stopRun(self, event):
         #self.bgTask.cancel(True) # generally no effect
         self.bgTask.shouldStop()
         self.bgTask = None

    def _setButtonStates(self, activated):
        self.stopBtn.enabled = activated
        self.startBtn.enabled = not activated

    def hasEnded(self):
        self._setButtonStates(False)

class Adapter(WindowAdapter):
    # @Override
    def windowClosing(self, event):
        endCondition.acquire()
        endCondition.notifyAll()
        endCondition.release()

class Runnable(Runnable):
    def __init__(self, runFunction):
        self._runFunction = runFunction
    def run(self):
        self._runFunction()

SwingUtilities.invokeLater(Runnable(Example))
endCondition = threading.Condition()
endCondition.acquire()
endCondition.wait()
endCondition.release()

----------------------------------------------- end of revised sample --------------------------------------

Thanks to the help provided here I've created a JFrame to allow me more advanced user interaction and a custom control GUI. I only have one issue at this point, once the items runing in doInBackground (BgTask SwingWorker), you can not gracefully stop stop the script. I get the following error: java.lang.InterruptedException: sleep interrupted which I assume happens due to me trying to interrupt the 'wait' command.

I tried to add a try/except to catch the exception, but it doesn't seem to work. Also when I try to use the CTRL+ALT+c hot key it stops the program and makes my JFrame and also the IDE unresponsive.

I'm using Windows 7 64bit, I'm using SikulixIDE 1.1.0

CODE:

from javax.swing import JButton, JFrame, JTextField, JPanel
from java.lang import Runnable, InterruptedException
from java.util.concurrent import ExecutionException
from javax.swing import SwingWorker, SwingUtilities
from java.lang import *

from java.awt.event import ActionListener, WindowAdapter
import threading

class BgTask(SwingWorker):
    def __init__(self, gui):
        self.gui = gui
        SwingWorker.__init__(self)

    def doInBackground(self):
        try:
            click("1463505602802.png") #Random icon on my desktop that is visible

            wait(5)

            click("1463514867811.png") #Random icon on my desktop that is visible
        except InterruptedException:
            print "***Automation Stuff Should End***"

    def done(self):
        try:
            self.get()
        except ExecutionException, e:
            raise SystemExit, e.getCause()
# print e.getCause()

class Example:
    def _setButtonStates(self, started):
        self.stopBtn.enabled = started
        self.startBtn.enabled = not started
    def stopSomething(self, event):
         self.bgTask.cancel(True)
         self.bgTask = None
         self._setButtonStates(started=False)

    def doSomething(self,event):
        self.bgTask = BgTask(self)
        self.bgTask.execute()
        self._setButtonStates(started=True)

    def __init__(self):
        frame = JFrame("Gamma Automated Testing")
        frame.setSize(200, 150)
        frame.addWindowListener(Adapter())
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
        pnl=JPanel()
        frame.add(pnl)
        self.textfield1 = JTextField('', 15)
        pnl.add(self.textfield1)
        self.textfield2 = JTextField('', 15)
        pnl.add(self.textfield2)
        self.startBtn = JButton('Start Automation', actionPerformed=self.doSomething)
        self.stopBtn = JButton('Stop', actionPerformed=self.stopSomething)
        pnl.add(self.startBtn)
        pnl.add(self.stopBtn)
        self.stopBtn.enabled = False
        frame.pack()
        frame.setVisible(True)

endCondition = threading.Condition()
class Adapter(WindowAdapter):
    def windowClosing(self, event):
        endCondition.acquire()
        endCondition.notifyAll()
        endCondition.release()

class Runnable(Runnable):
    def __init__(self, runFunction):
        self._runFunction = runFunction

    def run(self):
        self._runFunction()

if __name__ == '__main__':
    SwingUtilities.invokeLater(Runnable(Example))
    endCondition.acquire()
# Example()

    endCondition.wait()
    endCondition.release()

Question information

Language:
English Edit question
Status:
Answered
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Karl (k-d) said :
#1

You can try:
      finally:
            print "***Automation Stuff Should End***"

instead of:
      except InterruptedException:
            print "***Automation Stuff Should End***"

Revision history for this message
Mike (mike-spicer99) said :
#2

That seems to have helped a bit, but I'm still getting some sort of crash:

***Automation Stuff Should End***

Exception in thread "AWT-EventQueue-0" java.util.concurrent.CancellationException
at java.util.concurrent.FutureTask.report(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at javax.swing.SwingWorker.get(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
at org.python.core.PyObject.__call__(PyObject.java:461)
at org.python.core.PyObject.__call__(PyObject.java:465)
at org.python.core.PyMethod.__call__(PyMethod.java:126)
at org.python.pycode._pyx459.done$4(C:\Users\mspicer\Documents\sikulistuff\gamma_test_screen.sikuli\gamma_test_screen.py:30)
at org.python.pycode._pyx459.call_function(C:\Users\mspicer\D

ocuments\sikulistuff\gamma_test_screen.sikuli\gamma_test_screen.py)
at org.python.core.PyTableCode.call(PyTableCode.java:167)
at org.python.core.PyBaseCode.call(PyBaseCode.java:307)
at org.python.core.PyBaseCode.call(PyBaseCode.java:198)
at org.python.core.PyFunction.__call__(PyFunction.java:482)
at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:237)
at org.python.core.PyMethod.__call__(PyMethod.java:228)
at org.python.core.PyMethod.__call__(PyMethod.java:218)
at org.python.core.PyMethod.__call__(PyMethod.java:213)
at org.python.core.PyObject._jcallexc(PyObject.java:3626)
at org.python.core.PyObject._jcall(PyObject.java:3658)
at org.python.proxies.__main__$BgTask$6.done(Unknown Source)
at javax.swing.SwingWorker$5.run(Unknown Source)
at javax.swing.SwingWorker.doneEDT(Unknown Source)
at javax.swing.SwingWorker.access$100(Unknown Source)
at javax.swing.SwingWorker$2.done(Unknown Source)
at java.util.concurrent.FutureTask.finishCompletion(Unknown Source)
at java

.util.concurrent.FutureTask.cancel(Unknown Source)
at javax.swing.SwingWorker.cancel(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
at org.python.core.PyObject.__call__(PyObject.java:478)
at org.python.core.PyObject.__call__(PyObject.java:482)
at org.python.core.PyMethod.__call__(PyMethod.java:141)
at org.python.pycode._pyx459.stopSomething$8(C:\Users\mspicer\Documents\sikulistuff\gamma_test_screen.sikuli\gamma_test_screen.py:42)
at org.python.pycode._pyx459.call_function(C:\Users\mspicer\Documents\sikulistuff\gamma_test_screen.sikuli\gamma_test_screen.py)
at org.python.core.PyTableCode.call(PyTableCode.java:167)
at org

.python.core.PyBaseCode.call(PyBaseCode.java:307)

at org.python.core.PyBaseCode.call(PyBaseCode.java:198)
at org.python.core.PyFunction.__call__(PyFunction.java:482)
at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:237)
at org.python.core.PyMethod.__call__(PyMethod.java:228)
at org.python.core.PyMethod.__call__(PyMethod.java:223)
at org.python.core.PyCompoundCallable.__call__(PyCompoundCallable.java:26)
at org.python.core.PyObject.__call__(PyObject.java:431)
at org.python.core.PyObject._jcallexc(PyObject.java:3626)
at org.python.core.PyObject._jcall(PyObject.java:3658)
at org.python.proxies.java.awt.event.ActionListener.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.p

rocessMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.

security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Revision history for this message
RaiMan (raimund-hocke) said :
#3

thanks for the challenging idea (see my update in the question top) ;-)

Can you help with this problem?

Provide an answer of your own, or ask Mike for more information if necessary.

To post a message you must log in.