← Back to team overview

sikuli-driver team mailing list archive

Re: [Question #293847]: How to gracefully interrupt running script when using SwingWorker and JFrame? --- not possible with the thread model

 

Question #293847 on Sikuli changed:
https://answers.launchpad.net/sikuli/+question/293847

Description changed to:
*************** 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()

-- 
You received this question notification because your team Sikuli Drivers
is an answer contact for Sikuli.