This question already has answers here:
Is there any way to kill a Thread?
(31 answers)
Closed 3 years ago.
I have this class which is my thread. By creating instance of the class I create my thread, so by calling its stop method it should be terminated or killed but it wouldn't. I don't know why? I appreciate if someone can help me.
class MyThread(threading.Thread):
# Thread class with a _stop() method.
# The thread itself has to check
# regularly for the stopped() condition.
def __init__(self, last_time=0, start_time=0, label="", root="", speed_mode=""):
super(MyThread, self).__init__()
self.label = label
self.last_time = last_time
self.start_time = start_time
self.root = root
self.speed_mode = speed_mode
self._stop_event = threading.Event()
# function using _stop function
def start_counter(self):
self.start_time += 1
self.label['text'] = self.start_time
# self.start_time = start_time
if self.start_time < self.last_time:
self.label.after(DIC_TIME[self.speed_mode], self.start_counter)
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.isSet()
def run(self):
self.start_counter()
while True:
if self.stopped():
return
I expect that by calling stop method of MyThread class my label in tinter canvas restart and stop running(label is a second counter)
Try this: Is there any way to kill a Thread? hopefully it will be what you`re looking for, hopefully it's what you're looking for
Related
This question already has answers here:
Background thread with QThread in PyQt
(7 answers)
Example of the right way to use QThread in PyQt?
(3 answers)
PyQt5: start, stop and pause a thread with progress updates
(1 answer)
Closed 3 days ago.
Save New Duplicate & Edit Just Text Twitter
# importing libraries
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import sys
import time
class Worker(QObject):
finished = pyqtSignal()
_stop = False
def run(self):
i=0
print('Started')
for i in range(5):
if self._stop:
print('Breaking')
break
time.sleep(1)
i+=1
self.finished.emit()
print('Done')
def stop(self):
self._stop = True
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
try:
self.worker.stop()
self.thread.wait()
except (RuntimeError,AttributeError):
pass
print('Here')
self.createThread()
self.thread.started.connect(self.worker.run)
self.worker.quit = False
self.thread.finished.connect(lambda:print('Stopped'))
self.thread.start()
self.btn = QPushButton(self)
self.btn.move(40, 80)
self.btn.setText('Stop')
self.btn.clicked.connect(self.initUI)
self.setWindowTitle("Python")
self.show()
def createThread(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
if __name__ == '__main__':
App = QApplication(sys.argv)
window = Example()
sys.exit(App.exec())
I wrote an MVCE version of a problem I have been having.
The initUI function creates a window with a button in it. It also creates a thread that waits for 5 loops with a 1 second sleep each loop. When I click the button, it calls initUI again. This time, if the thread is still running, it calls the worker.stop function to stop the thread in between. When I do this, although the worker.run function finishes execution, the worker.finished signal is not emitted. However, the signal is emitted when the loop finishes on its own without pressing the button (i.e. waiting 5 seconds). Any explanation?
So i created a class that runs a big calculation in a seperet thread.
This is what i expected to see:
And this is what i do see:
This is the frame class
class ThreadFrame(wx.Frame):
def __init__(self,parent=None):
wx.Frame.__init__(self,parent=parent)
self.frame = wx.Frame(None, title='Bitte Warten',style=wx.FRAME_NO_TASKBAR)
self.frame.SetSize(500,100)
self.panel=wx.Panel(self.frame)
self.parent=parent
self.WaitLbl=wx.StaticText(self.panel,-1)
self.WaitLbl.SetLabel('Geotags werden ausgelesen und abgerufen.')
self.progress = wx.Gauge(self.panel,size=(500,30), range=self.parent.list_ctrl.GetItemCount())
self.btn = wx.Button(self.panel,label='Abbrechen')
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.Sizer=wx.BoxSizer(wx.VERTICAL)
#Add Widgets LeftSizer
self.Sizer.Add(self.WaitLbl,0,wx.ALL|wx.CENTER,5)
self.Sizer.Add(self.progress,0,wx.ALL,5)
self.Sizer.Add(self.btn,0,wx.ALL|wx.CENTER,5)
self.panel.SetSizer(self.Sizer)
self.Sizer.Fit(self.panel)
self.panel.Layout()
self.Centre()
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
#self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self.frame, self.parent)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
print('Thread lebt noch')
self.mythread.terminate() # Shutdown the thread
print('Thread wird beendet')
self.mythread.join() # Wait for it to finish
self.Close()
And this is the thread where the calculation is running
class TestThread(Thread):
def __init__(self,parent_target,toplevel):
Thread.__init__(self)
self.parent = toplevel
self.ownparent=parent_target
self.stopthread = False
self.start() # start the thread
def run(self):
print('Thread gestartet')
i=0
while self.stopthread == False:
#if calculation is not finished:
#do calculation and count i one up
evt = progress_event(count=i)
#Send back current count for the progress bar
try:
wx.PostEvent(self.ownparent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
i=i+1
else:
print('Thread Terminated')
self.terminate()
def terminate(self):
self.stopthread = True
And this is how i call the class from my main programm:
frame=ThreadFrame(self)
The main program also has a frame open. So this is a frame which is opend and then starts a thread which does calculation and then stops.
I think thats all that is to know. I replaced the caluclation with speudo code because my brain hurts and i cant come up with a plachold right now. But i feel like between all the fiting and sizers and panels and frames i went somewhere wrong. And i totaly dont look through all of this stuff at the moment.
The code you show doesn't seem to correspond to the screenshots. Whatever problem with the layout you might have, you should still have "Bitte warten" in the frame title, but your screenshot doesn't even show this. Either you're not executing the code you show at all, or you create some other frame elsewhere in your code which you see here. Or, of course, you've uploaded a wrong screenshot or code version. But something just doesn't fit here.
So im not sure what caused the problem. I started from scratch and now it works. This time i didnt use a new frame because the class itself is allready a frame.
class GeoThreadFrame(wx.Frame):
def __init__(self, radsteuer):
wx.Frame.__init__(self,parent=radsteuer.frame,style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP|wx.FRAME_NO_TASKBAR)
panel = wx.Panel(self)
self.SetWindowStyle(wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP)
self.progress = wx.Gauge(panel,size=(300,30), pos=(10,50), range=radsteuer.list_ctrl.GetItemCount())
self.btn = wx.Button(panel,label='Abbrechen', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
panel.Center()
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Center()
self.Show()
self.mythread = GeoLocationThread(self, radsteuer)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
#time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
#self.mythread.join(3) # Wait for it to finish
self.Destroy()
class GeoLocationThread(Thread):
def __init__(self,parent_target,mainparent):
Thread.__init__(self)
self.parent = parent_target
self.mainparent=mainparent
self.stopthread = False
self.start() # start the thread
def run(self):
# A loop that will run for 5 minutes then terminate
i=0
while self.stopthread == False:
if i < self.mainparent.list_ctrl.GetItemCount():
self.calculation(i)
evt = progress_event(count=i)
i=i+1
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
def calculate(self,i):
#your calculation here
I'm trying to use the pyqt qthread for multithreading program.
In the code, there are two different workers instance. I try to use signal and slot for data share.
but it seemed that the signal is blocked until one of the qthread is finished.
this is the code.
from PyQt5.QtCore import QCoreApplication,QThread, QObject, pyqtSignal, pyqtSlot
import time
import sys
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
#pyqtSlot()
def work(self): # A slot takes no params
for i in range(1, 10):
time.sleep(1)
self.intReady.emit(i)
self.finished.emit()
class Worker2(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
#pyqtSlot()
def work(self): # A slot takes no params
for i in range(1, 10):
time.sleep(1)
self.intReady.emit(i)
self.finished.emit()
#pyqtSlot(int)
def revsignal(self, val):
print("hello rev a signal"+str(val))
def updateLabel(val):
print("updateLable "+str(val))
app = QCoreApplication(sys.argv)
# 1 - create Worker and Thread inside the Form
worker = Worker() # no parent!
thread = QThread() # no parent!
worker2 = Worker2()
thread2 = QThread()
worker.intReady.connect(updateLabel)
worker.intReady.connect(worker2.revsignal)
worker.moveToThread(thread)
worker.finished.connect(thread.quit)
thread.started.connect(worker.work)
# self.thread.finished.connect(app.exit)
worker2.moveToThread(thread2)
worker2.finished.connect(thread2.quit)
thread2.started.connect(worker2.work)
thread2.finished.connect(app.exit)
thread2.start()
print("after thread2 start")
thread.start()
print("after thread1 start.")
sys.exit(app.exec_())
and the output is here
after thread2 start
after thread1 start.
updateLable 1
updateLable 2
updateLable 3
updateLable 4
updateLable 5
updateLable 6
updateLable 7
updateLable 8
updateLable 9
hello rev a signal1
hello rev a signal2
hello rev a signal3
hello rev a signal4
hello rev a signal5
hello rev a signal6
hello rev a signal7
hello rev a signal8
hello rev a signal9
Process finished with exit code 0
i don't know why the worker2 thread can't receive the signal as soon as the work1 emit the signal. until the work1 thread ended.
Any help is greatly appreciated!
First thing is that all slots should be members of a class which inherits from QObject. So make a class like this
class Manager(QObject):
#pyqtSlot(int)
def updateLabel(self, val):
print("updateLable "+str(val))
manager = Manager()
Next, as per the docs here, there is an additional argument which can be passed to connect to control the behavior of cross-thread signal processing. You need to change three lines.
from PyQt5.QtCore import QCoreApplication, QThread, QObject, pyqtSignal, pyqtSlot, Qt
to get the Qt namespace, and both of
worker.intReady.connect(manager.updateLabel, type=Qt.DirectConnection)
worker.intReady.connect(worker2.revsignal, type=Qt.DirectConnection)
to set the connection type.
You're misunderstanding how threads and signals/slots work.
Signal and Slot handling
When a signal is emitted, slots that it's connected to should be run. When using a DirectConnection, that means directly calling the slot function, but that's only the default when objects live on the same thread. When objects live on different threads (or you've used a QueuedConnection), the signal enqueues an event to run the slot in the appropriate thread's event queue. This is what you want.
Thread event loops
Each thread is a separate thread of executing, running an event loop. The event loop calls the slots that have received a signal, but it does so sequentially, that is, it calls one slot, then when that function returns, it calls the next, and so on.
What you're doing wrong
It's a combination of 2 things.
First, you're doing your entire logic loop in the slot, meaning no further events will be handled until your loop is finished.
Second, you're calling time.sleep in the slot, which causes that entire thread to sleep for 1 second.
Combined, this means that both threads will first call the work slot, be 'busy' for 10 seconds until that slot is done, and then do the rest of the work.
Solutions
Direct Connection
You can use a direct connection, as suggested by buck54321, but this just means that the work function of worker1 (running on thread1) will call worker2.revsignal directly, still on thread1.
Manually process events
You can call QCoreApplication.processEvents() to run (an iteration of) the event loop by hand. Sometimes this can be the right solution, but it's messy,
and you're still going to cause the thread to sleep for a second at a time.
Use timed events
Instead of looping, you can use timers to periodically call your slots and do work then. This means you don't have to loop until you're done. I've included an example using QTimer.singleShot.
Code
Cleaned up
Here is a cleaned up version that still behaves roughly the same:
from PyQt5.QtCore import QCoreApplication,QThread, QObject, pyqtSignal, pyqtSlot
import time
import sys
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int, int)
def __init__(self, number):
super().__init__()
self.number = number
#pyqtSlot()
def work(self):
for i in range(1, 10):
time.sleep(1)
self.intReady.emit(self.number, i)
self.finished.emit()
#pyqtSlot(int)
def revsignal(self, val):
print("hello rev a signal"+str(val))
class Label(QObject):
#pyqtSlot(int, int)
def updateLabel(self, source, val):
print(f"updateLabel ({source}): {val}")
class Exiter(QObject):
done = pyqtSignal()
def __init__(self, num_threads):
super().__init__()
self.live_threads = num_threads
#pyqtSlot()
def finished(self):
self.live_threads -= 1
if self.live_threads <= 0:
self.done.emit()
app = QCoreApplication(sys.argv)
# 1 - create Worker and Thread inside the Form
worker1 = Worker(1) # no parent!
thread1 = QThread() # no parent!
worker2 = Worker(2)
thread2 = QThread()
label = Label()
exiter = Exiter(2) # Waiting for 2 threads
worker1.intReady.connect(label.updateLabel)
worker1.intReady.connect(worker2.revsignal)
worker1.moveToThread(thread1)
worker1.finished.connect(thread1.quit)
thread1.started.connect(worker1.work)
thread1.finished.connect(exiter.finished)
worker2.intReady.connect(label.updateLabel)
worker2.moveToThread(thread2)
worker2.finished.connect(thread2.quit)
thread2.started.connect(worker2.work)
thread2.finished.connect(exiter.finished)
exiter.done.connect(app.exit)
thread2.start()
print("after thread2 start")
thread1.start()
print("after thread1 start.")
sys.exit(app.exec_())
Hack
This version forces event processing before sleeping, which at least handles other events, but still causes the thread to sleep.
from PyQt5.QtCore import QCoreApplication,QThread, QObject, pyqtSignal, pyqtSlot
import time
import sys
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int, int)
def __init__(self, number):
super().__init__()
self.number = number
#pyqtSlot()
def work(self):
for i in range(1, 10):
QCoreApplication.processEvents()
time.sleep(1)
self.intReady.emit(self.number, i)
self.finished.emit()
#pyqtSlot(int)
def revsignal(self, val):
print("hello rev a signal"+str(val))
class Label(QObject):
#pyqtSlot(int, int)
def updateLabel(self, source, val):
print(f"updateLabel ({source}): {val}")
class Exiter(QObject):
done = pyqtSignal()
def __init__(self, num_threads):
super().__init__()
self.live_threads = num_threads
#pyqtSlot()
def finished(self):
self.live_threads -= 1
if self.live_threads <= 0:
self.done.emit()
app = QCoreApplication(sys.argv)
# 1 - create Worker and Thread inside the Form
worker1 = Worker(1) # no parent!
thread1 = QThread() # no parent!
worker2 = Worker(2)
thread2 = QThread()
label = Label()
exiter = Exiter(2) # Waiting for 2 threads
worker1.intReady.connect(label.updateLabel)
worker1.intReady.connect(worker2.revsignal)
worker1.moveToThread(thread1)
worker1.finished.connect(thread1.quit)
thread1.started.connect(worker1.work)
thread1.finished.connect(exiter.finished)
worker2.intReady.connect(label.updateLabel)
worker2.moveToThread(thread2)
worker2.finished.connect(thread2.quit)
thread2.started.connect(worker2.work)
thread2.finished.connect(exiter.finished)
exiter.done.connect(app.exit)
thread2.start()
print("after thread2 start")
thread1.start()
print("after thread1 start.")
sys.exit(app.exec_())
Timed events
Here the logic has been rewritten to use a timed event, instead of sleeping.
from PyQt5.QtCore import QCoreApplication, QThread, QTimer, QObject, pyqtSignal, pyqtSlot
import sys
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int, int)
def __init__(self, number):
super().__init__()
self.number = number
self.to_emit = iter(range(1, 10))
#pyqtSlot()
def work(self):
try:
value = next(self.to_emit)
self.intReady.emit(self.number, value)
QTimer.singleShot(1000, self.work)
except StopIteration:
self.finished.emit()
#pyqtSlot(int)
def revsignal(self, val):
print("hello rev a signal"+str(val))
class Label(QObject):
#pyqtSlot(int, int)
def updateLabel(self, source, val):
print(f"updateLabel ({source}): {val}")
class Exiter(QObject):
done = pyqtSignal()
def __init__(self, num_threads):
super().__init__()
self.live_threads = num_threads
#pyqtSlot()
def finished(self):
self.live_threads -= 1
if self.live_threads <= 0:
self.done.emit()
app = QCoreApplication(sys.argv)
# 1 - create Worker and Thread inside the Form
worker1 = Worker(1) # no parent!
thread1 = QThread() # no parent!
worker2 = Worker(2)
thread2 = QThread()
label = Label()
exiter = Exiter(2) # Waiting for 2 threads
worker1.intReady.connect(label.updateLabel)
worker1.intReady.connect(worker2.revsignal)
worker1.moveToThread(thread1)
worker1.finished.connect(thread1.quit)
thread1.started.connect(worker1.work)
thread1.finished.connect(exiter.finished)
worker2.intReady.connect(label.updateLabel)
worker2.moveToThread(thread2)
worker2.finished.connect(thread2.quit)
thread2.started.connect(worker2.work)
thread2.finished.connect(exiter.finished)
exiter.done.connect(app.exit)
thread2.start()
print("after thread2 start")
thread1.start()
print("after thread1 start.")
sys.exit(app.exec_())
This question already has an answer here:
Tkinter, error maximum recursion depth exceeded
(1 answer)
Closed 5 years ago.
I'm new to Python and wanted to build a sleeptimer for training purposes.
I'm using Python 3.6.3 on Ubuntu.
I wanted a label which shows how many time is left till the Computer wents to sleep.
I'm using after() but every time I call the update Method it calls itself very often until I reach the maximum recursion depth with the Error:
RecursionError: maximum recursion depth exceeded while calling a Python object
I think that, for some reason the after(1000, self.update) command doesn't wait 1 second before calling the method again. Also I don't want a recursion, but call the method each second...
Can you tell me what I'm doing wrong?
class Application(Frame):
def startTimer(self):
self.start = time.time()
self.waiting = self.entry.get()*60000
self.update()
self.after(self.waiting, self.shutdown)
def update(self):
string = str(int(int(self.waiting/6000) + self.start - time.time())/10)
print(string)
self.label.config(text = string + " Minuts left")
self.after(1000, self.update())
def shutdown(self):
print("PSSST!")
def createWidgets(self):
self.entry = Scale(self, from_=5, to=120, orient=HORIZONTAL, resolution=5)
self.label = Label(self,text="time remaining")
self.start = Button(self, text="Start", fg="black",
command=self.startTimer)
self.quit = Button(self, text="QUIT", fg="red",
command=root.quit)
self.entry.pack(side="top")
self.label.pack(side="left")
self.start.pack(side="right")
self.quit.pack(side="bottom")
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
root = Tk()
app = Application(master=root)
app.mainloop()`
In this line:
self.after(1000, self.update())
You are not passing the self.update function to self.after, you are calling self.update() immediately and passing the result to self.after. This causes infinite recursion.
I tried following the solution laid out in this question, but nothing seems to happen once the signal is supposed to be emitted.
This isn't all of the code, but it should be enough to demonstrate what I'm trying to do:
class StartQT5(QMainWindow):
def __init__(self):
super().__init__()
[...]
self.getFileProperties(path, batch)
def getFileProperties(self, path, batch):
self.thread = QThread()
self.w = probeThread(self)
self.w.moveToThread(self.thread)
self.w.finished[dict].connect(self.setProbeOutput)
self.thread.started.connect(self.w.run)
self.thread.start()
#pyqtSlot(dict)
def setProbeOutput(self, data):
print("gotOutput")
self.probeOutput = data
print(data)
class probeThread(QObject):
finished = pyqtSignal(dict)
def __init__(self, parent):
super().__init__()
self.parent = parent
self.output = {}
def run(self):
self.output = json.loads(subprocess.check_output('%s -v 0 -print_format json -show_format -show_streams -count_frames "%s"' % (self.parent.ffprobePath, self.parent.file)).decode("utf-8"))
print("finished")
self.finished.emit(self.output)
The thread runs fine, but setProbeOutput never gets called. Sorry if this is something trivial, but I just can't seem to get it to work, thanks! :)
What about simply doing:
self.w.finished.connect(self.setProbeOutput)
You are defining a new signal and attaching it to a slot, you already declare the parameters types both in the signal and the slot declaration.