python - How can I stop a long-running function when it is called multiple times? -
below have example program. when button pressed, takes second before can calculate value show. if user presses button in rapid succession end waiting long time see last answer, answer care about. in code, can see _datacruncher
function needs know self._count
, self._count
not depend on output of _datacruncher
.
my question, therefore, how can interrupt normal execution of _datacruncher
on subsequent calls in order keep gui free other stuff, , not waste processing time when not needed? realize need use thread run _datacruncher
, sort of queue appropriate val
display, not understand how put together.
from pyqt4 import qtgui, qtcore import sys import time import random import random class mainwindow(qtgui.qmainwindow): def __init__(self): self.app = qtgui.qapplication(sys.argv) super(mainwindow, self).__init__() self.count = 0 self.initui() def initui(self): # layouts central = qtgui.qwidget() layout = qtgui.qvboxlayout() self.button = qtgui.qpushbutton('press me') self.text = qtgui.qlabel('?') layout.addwidget(self.button) layout.addwidget(self.text) central.setlayout(layout) self.setcentralwidget(central) self.button.clicked.connect(self._buttonclicked) def _datacruncher(self, val): time.sleep(1) # takes long time process data using val return val * random.randint(1,10) def _buttonclicked(self): self.count += 1 val = self._datacruncher(self.count) self.text.settext('value {}'.format(val)) def startup(self): self.show() result = self.app.exec_() sys.exit(result) if __name__ == '__main__': random.seed() mywindow = mainwindow() mywindow.startup()
so, finding answer more complicated thought. @mtset mentions in 1 of comments, python not offer means cancel execution of thread. so, did create 'threadhandler' class that, well, handles thread. keeps track of last thread created , offers means result execution of last thread.
i posting modified version of test code original post threadhandler code in full in case has use it.
file 1 here
# tester.py, run file pyqt4 import qtgui, qtcore import random, sys, time threadhandler import mythreadhandler class mymodel(object): def datacruncher(self, val): delay = random.randint(1,5) print('{} sleeping {}'.format(val, delay)) time.sleep(delay) # takes long time process data using val print('{} done sleeping'.format(val)) return val class mainwindow(qtgui.qmainwindow): def __init__(self, threadhandler): self.app = qtgui.qapplication(sys.argv) super(mainwindow, self).__init__() self.count = 0 self.initui() self.button_clicked_events = event() self.threadhandler = threadhandler def initui(self): # layouts central = qtgui.qwidget() layout = qtgui.qvboxlayout() self.button = qtgui.qpushbutton('press me') self.text = qtgui.qlabel('?') layout.addwidget(self.button) layout.addwidget(self.text) central.setlayout(layout) self.setcentralwidget(central) self.button.clicked.connect(self._buttonclicked) def _buttonclicked(self): self.count += 1 self.button_clicked_events(self.count) def setlabel(self, val): self.text.settext(str(val)) def startup(self): self.show() result = self.app.exec_() return result class event(list): """event subscription. list of callable objects. calling instance of cause call each item in list in ascending order index. example usage: >>> def f(x): ... print 'f(%s)' % x >>> def g(x): ... print 'g(%s)' % x >>> e = event() >>> e() >>> e.append(f) >>> e(123) f(123) >>> e.remove(f) >>> e() >>> e += (f, g) >>> e(10) f(10) g(10) >>> del e[0] >>> e(2) g(2) """ def __init__(self): self.output = {} def __call__(self, *args, **kwargs): f,key in self: output = f(*args, **kwargs) self.output[key] = output return self.output def __repr__(self): return "event({})".format(list.__repr__(self)) if __name__ == '__main__': def checker(handler, window): if handler.islastdone(): val = handler.getlastresult() window.setlabel(val) else: window.setlabel('calculating...') random.seed() model = mymodel() threadhandler = mythreadhandler() mywindow = mainwindow(threadhandler) threadhandler.createtimer(1, checker, threadhandler, mywindow) def getdata(count): threadhandler.createoneshot(model.datacruncher, count) mywindow.button_clicked_events.append((getdata, 'dt')) result = mywindow.startup() print('ending') threadhandler.end() print('ended') sys.exit(result)
file 2 below
#threadhandler.py, save file in same folder tester.py import threading, time class mythreadhandler(object): def __init__(self): self.oneshots = [] self.timers = [] self.oldoneshots = [] self.latest = none self.cleaning = false self._startcleaner() def _startcleaner(self): print('-'*20+'starting cleaner'+'-'*20) self.cleaner = self.createtimer(1, self._cleanupthreads) def _stopcleaner(self): print('-'*20+'stopping cleaner'+'-'*20) self.cleaner.stop() def getnumthreads(self): return len(self.oneshots) def getnumoldthreads(self): return len(self.oldoneshots) def end(self): i,timer in enumerate(self.timers): timer.stop() self.timers.pop(i) def createtimer(self, interval, func, *args, **kwargs): timer = mytimer(interval, func, args, kwargs) self.timers.append(timer) return timer def createoneshot(self, func, *args, **kwargs): oneshot = myoneshot(func, args, kwargs) self.oneshots.append(oneshot) self.latest = oneshot def islastdone(self): if not self.latest none: return not self.latest.running() else: return none def getlastresult(self): if self.latest none: raise valueerror('there have not been oneshots created.') while self.latest.running(): pass result = self.latest.getresult() if len(self.oneshots) > 0: self.oldoneshots.append(myoneshot(self._cleanall, (self.oneshots,))) self.oneshots = [] return result def _cleanall(self, toclean): # loop through toclean , pop that's done. lock while len(toclean) > 0: toclean = self._cleanup(toclean) def _cleanup(self, tocleanup): while not self.cleaning: self.cleaning = true i, thread in enumerate(tocleanup): if not thread.running(): tocleanup.pop(i) self.cleaning = false return tocleanup def _cleanupthreads(self): # check each of these lists , pop out threads done. # not lock. function should called # cleaner, set in __init__ self.oneshots = self._cleanup(self.oneshots) self.timers = self._cleanup(self.timers) self.oldoneshots = self._cleanup(self.oldoneshots) class mytimer(object): def __init__(self, delay, func, args=tuple(), kwargs={}): self.delay = delay self.func = func self.loop = true self.args = args self.kwargs = kwargs self.thread = threading.thread(target=self.run, daemon=true) self.thread.start() self.output = none def run(self): while self.loop: self.output = self.func(*self.args, **self.kwargs) if self.delay > 0.1: count = 0 while count <= self.delay: count += 0.1 time.sleep(0.1) else: time.sleep(self.delay) def stop(self): self.loop = false def running(self): return self.loop def getresult(self): return self.output class myoneshot(object): def __init__(self, func, args=tuple(), kwargs={}): self.func = func self.args = args self.kwargs = kwargs self.thread = threading.thread(target=self.run, daemon=true) self.thread.start() self.output = none def run(self): self.output = self.func(*self.args, **self.kwargs) def running(self): return self.thread.is_alive() def getresult(self): return self.output if __name__ == '__main__': import random random.seed() def longfunc(num): delay = random.randint(5,8) if num in (3, 6): delay = 2 print('-'*30+'func {} has sleep {}'.format(num, delay)) time.sleep(delay) print('-'*30+'func {} done'.format(num)) return num def checker(handler): if handler.islastdone(): return handler.getlastresult() else: return none myhandler = mythreadhandler() # 'checker' function simulates in program uses # data generated 'longfunc'. waits until there no more threads # in threadhandler, indicate user done # switching back-and-forth between different values checktimer = myhandler.createtimer(1, checker, myhandler) # create 10 one-shot threads take 'long' time. delay keep # them in order, loop meant simulate user switching between # items using keyboard or mouse, imagine couldn't # faster every 1/10th of second start = time.time() in range(4): myhandler.createoneshot(longfunc, i) time.sleep(0.1) # wait until there no more threads executing last = myhandler.getlastresult() print('result last = {}'.format(last)) in range(4, 7): myhandler.createoneshot(longfunc, i) time.sleep(0.1) last = myhandler.getlastresult() print('result last = {}'.format(last)) while myhandler.getnumoldthreads() >0 or myhandler.getnumthreads() > 0: pass myhandler.end() print('done ending')
Comments
Post a Comment