← Back to team overview

sikuli-driver team mailing list archive

Re: [Question #293847]: How to gracefully interrupt running script when using SwingWorker and JFrame?

 

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 thread get mangled (use the Python string formatting ""%() to assure that - see sample)

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)
      
    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())
   
    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):
    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.