Starting a Qthread on a button click in PyQt5 [duplicate] - python

This question already has answers here:
QThread: Destroyed while thread is still running
(2 answers)
Closed 4 years ago.
I'm currently trying to make an API call when my button is clicked without pausing my GUI for the duration of the call. I was using threads to set the text of my Qlable to the response of the API call. This worked however it was unsafe as I was accessing GUI elements from a separate thread.
Currently, I'm attempting to use QThreads to make the API call and then emit the response to the GUI thread however, when I create the Qthread object my program finishes with exit code 3. I've simplified the problem for more clarity.
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("TestWindow")
self.setFixedSize(300,75)
self.main_layout = QtWidgets.QGridLayout()
self.setLayout(self.main_layout)
self.txt_0 = QtWidgets.QLabel()
self.btn_0 = QtWidgets.QPushButton('Press Me')
self.btn_0.clicked.connect(self.btn_0_clicked)
self.main_layout.addWidget(self.txt_0, 0, 0)
self.main_layout.addWidget(self.btn_0, 1, 0)
self.show()
def btn_0_clicked(self):
temp_thread = StringThread("name")
temp_thread.start()
class StringThread(QtCore.QThread):
str_signal = QtCore.pyqtSignal(str)
_name = ''
def __init__(self, name):
QtCore.QThread.__init__(self)
self._name = name
print("Thread Created")
def run(self):
self.str_signal.emit('Emitted message from StringThread. Name = ' + self._name)
print("Done run")
My intention is to set the text of my Qlable to the message emitted from the pyqtSignal in the StringThread class however, as soon as I click the button my program finishes with exit code 3.
edit:
I made the following changes to the btn_0_clicked method
def btn_0_clicked(self):
self.temp_thread = StringThread("hello")
self.temp_thread.str_signal.connect(self.txt_0.setText)
self.temp_thread.start()
It's working now.

You should probably read up on the Qt C++ documentation to better understand what is happening:
http://doc.qt.io/qt-5/qobject.html
http://doc.qt.io/qt-5/qthread.html
https://wiki.qt.io/QThreads_general_usage
In general you have two problems (one of which is worth listing twice, to examine it from different angles):
Your QThread is owned/associated by/with the thread that created it for the purpose of emitting events. So all code trying to emit events is actually trying to access the event loop of your main thread (UI). Read up on QObject's thread() and moveToThread() methods to understand this more fully.
You overrode the run() method of QThread which is what is invoked on the actual thread represented by QThread (i.e. the code that winds up getting called eventually once you call start() on the thread). So you are effectively trying to access the main event loop in an unsafe manner from a different thread.
You overrode the run() method of QThread. The default implementation of QThread sets up a QEventLoop, runs it and that is what gives QObject's associated/owned with/by that thread the ability to use signal/slots freely. You probably did not intend to lose that default implementation.
Basically what you want to do instead is this:
Do not subclass QThread. Instead write a custom QObject worker subclass with appropriate events
Construct a QThread with an eventloop (the default IIRC, but check the docs)
Construct an instance of your custom worker object.
Move your worker object to the newly created QThread with moveToThread().
Wire up the signals and slots, make sure you are using the QueuedConnection type (the default is DirectConnection which is not suitable for communicating across thread boundaries).
Start your thread which should just run its own QEventLoop.
Emit the signal to kickstart your worker object logic.
Receive notification of the results via signals on your main thread (eventloop), where it is safe to update the UI.
Figure out how you want to terminate the QThread and clean up associated resources. Typically you would fire a custom event wired up to the destroyLater() slot to get rid of your worker object. You may want to cache/reuse the QThread for subsequent button clicks or you may want to tear it down as well once your worker object has been cleaned up.

Related

pyqt - How do I ensure that all my calculations are running in a separate Qthread?

I have been reading around about how to "properly" use QThreads and I've become a bit confused. I've been reading around and a lot of people have been saying to, create a QThread, but also create a worker which is a QObject. Then use movetoThread to move the Qobject to the Qthread.
I'm just a bit confused since all the examples I've seen on stackoverflow don't really do that.
If this is my code:
class worker(QThread):
def __init__(self):
Qthread.__init__(self)
def run(self):
do_work()
a = 3
def do_work(self):
b = 2
self.emit(SIGNAL("work_done"),b)
class MyApp(QtGui.QMainWindow):
QtGui.QMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.worker_thread = worker()
self.connect(self.worker,SIGNAL('work_done'),self.done)
self.worker_thread.start()
def done(self,b)
print "the value from the worker is: " + str(b)
Will this code make the assignment of variables a and b done in another thread SEPARATE from the main thread? Or is that not true as my worker_thread object LIVES INSIDE the main thread?
A better method for running some code in another thread is not to subclass QThread, but to move the object implementing that other code into a separate thread.
In your case, your worker object would just inherit directly from QObject, so that you can communicate with it across threads via signals and slots. Then create a standard, out-of-the-box QThread and move the worker to it. Use signals/slots to send the worker data, if needed, and to retrieve its results when they're ready.
I think your confusion stems from this idea: the QThread object manages a separate thread, but the object itself lives in whatever thread created the object in the first place. For example, creating it in the main thread means the QThread is also in the main thread, but your worker object would be in the thread managed by the QThread object (after you move it there, of course).
Check out this post for more details and explanations.

Qthread is still working when i close gui on python pyqt

my code has thread, but when i close the gui, it still works on background. how can i stop threads? is there something stop(), close()?
i dont use signal, slots? Must i use this?
from PyQt4 import QtGui, QtCore
import sys
import time
import threading
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.kac_ders=QtGui.QComboBox()
self.bilgi_cek=QtGui.QPushButton("Save")
self.text=QtGui.QLineEdit()
self.widgetlayout=QtGui.QFormLayout()
self.widgetlar=QtGui.QWidget()
self.widgetlar.setLayout(self.widgetlayout)
self.bilgiler=QtGui.QTextBrowser()
self.bilgi_cek.clicked.connect(self.on_testLoop)
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.widgetlar)
self.analayout=QtGui.QVBoxLayout()
self.analayout.addWidget(self.text)
self.analayout.addWidget(self.bilgi_cek)
self.analayout.addWidget(self.bilgiler)
self.centralWidget=QtGui.QWidget()
self.centralWidget.setLayout(self.analayout)
self.setCentralWidget(self.centralWidget)
def on_testLoop(self):
self.c_thread=threading.Thread(target=self.kontenjan_ara)
self.c_thread.start()
def kontenjan_ara(self):
while(1):
self.bilgiler.append(self.text.text())
time.sleep(10)
app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()
A few things:
You shouldn't be calling GUI code from outside the main thread. GUI elements are not thread-safe. self.kontenjan_ara updates and reads from GUI elements, it shouldn't be the target of your thread.
In almost all cases, you should use QThreads instead of python threads. They integrate nicely with the event and signal system in Qt.
If you just want to run something every few seconds, you can use a QTimer
def __init__(self, parent=None):
...
self.timer = QTimer(self)
self.timer.timeout.connect(self.kontenjan_ara)
self.timer.start(10000)
def kontenjan_ara(self):
self.bilgiler.append(self.text.text())
If your thread operations are more computationally complex you can create a worker thread and pass data between the worker thread and the main GUI thread using signals.
class Worker(QObject):
work_finished = QtCore.pyqtSignal(object)
#QtCore.pyqtSlot()
def do_work(self):
data = 'Text'
while True:
# Do something with data and pass back to main thread
data = data + 'text'
self.work_finished.emit(data)
time.sleep(10)
class MyWidget(QtGui.QWidget):
def __init__(self, ...)
...
self.worker = Worker()
self.thread = QtCore.QThread(self)
self.worker.work_finished.connect(self.on_finished)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.do_work)
self.thread.start()
#QtCore.pyqtSlot(object)
def on_finished(self, data):
self.bilgiler.append(data)
...
Qt will automatically kill all the subthreads when the main thread exits the event loop.
I chose to rewrite a bit this answer, because I had failed to properly look at the problem's context. As the other answers and comments tell, you code lacks thread-safety.
The best way to fix this is to try to really think "in threads", to restrict yourself to only use objects living in the same thread, or functions that are known as "threadsafe".
Throwing in some signals and slots will help, but maybe you want to think back a bit to your original problem. In your current code, each time a button is pressed, a new thread in launched, that will, every 10 seconds, do 2 things :
- Read some text from self.text
- Append it to self.bilgiler
Both of these operations are non-threadsafe, and must be called from the thread that owns these objects (the main thread). You want to make the worker threads "schedule & wait" the read & append oeprations, instead of simply "executing" them.
I recommend using the other answer (the thread halting problem is automatically fixed by using proper QThreads that integrate well with Qt's event loop), which would make you use a cleaner approach, more integrated with Qt.
You may also want to rethink your problem, because maybe there is a simpler approach to your problem, for example : not spawning threads each time bilgi_cek is clicked, or using Queue objects so that your worker is completely agnostic of your GUI, and only interact with it using threadsafe objects.
Good luck, sorry if I caused any confusion. My original answer is still available here. I think it would be wise to mark the other answer as the valid answer for this question.

Redirecting stdout and stderr to a PyQt4 QTextEdit from a secondary thread

Stack overflow. Once again, I come to you in a time of dire need, teetering precariously on the brink of insanity. This question - as may be evident from the title - is an amalgamation of several other questions I have seen answered here.
I have a PyQt application, and I want to re-route the stdout and stderr streams to a QTextEdit that is in my GUI without delay.
Initially, I found the following stack overflow answer: https://stackoverflow.com/a/17145093/629404
This works perfectly, but with one caveat: If stdout or stderr are updated multiple times while the CPU is processing a relatively longer method, all of the updates show up simultaneously when the main thread returns to the application loop. Unfortunately, I have a few methods which take up to 20 seconds to complete (networking related), and so the application becomes unresponsive - and the QTextEdit does not update - until they are finished.
In order to fix this problem, I delegated all of the GUI processing to the main thread, and I have been spawning off a second thread to handle the longer networking operations, using pyqtSignals to notify the main thread of when the work is finished and pass back results. Immediately when I began testing the code written this way, the python interpreter began crashing without any warning.
This is where it gets very frusterating: Python is crashing because - using the class from the included link above - I have assigned the sys.stdout/err streams to the QTextEdit widget; PyQt widgets cannot be modified from any thread other then the application thread, and since the updates to stdout and stderr are coming from the secondary worker thread that I created, they are violating this rule. I have commented out the section of code where I redirect the output streams, and sure enough, the program runs without error.
This brings me back to square one, and leaves me in a confusing situation; Assuming I continue to handle GUI related operations in the main thread and deal with computation and longer operations in a secondary thread (which I have come to understand is the best way to keep the application from blocking when the user triggers events), how can I redirect Stdout and Stderr from both threads to the QTextEdit widget? The class in the link above works just fine for the main thread, but kills python - for the reason described above - when updates come from the second thread.
Firstly, +1 for realising how thread-unsafe many of the examples on stack overflow are!
The solution is to use a thread-safe object (like a Python Queue.Queue) to mediate the transfer of information. I've attached some sample code below which redirects stdout to a Python Queue. This Queue is read by a QThread, which emits the contents to the main thread through Qt's signal/slot mechanism (emitting signals is thread-safe). The main thread then writes the text to a text edit.
Hope that is clear, feel free to ask questions if it is not!
EDIT: Note that the code example provided doesn't clean up QThreads nicely, so you'll get warnings printed when you quit. I'll leave it to you to extend to your use case and clean up the thread(s)
import sys
from Queue import Queue
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# The new Stream Object which replaces the default stream associated with sys.stdout
# This object just puts data in a queue!
class WriteStream(object):
def __init__(self,queue):
self.queue = queue
def write(self, text):
self.queue.put(text)
# A QObject (to be run in a QThread) which sits waiting for data to come through a Queue.Queue().
# It blocks until data is available, and one it has got something from the queue, it sends
# it to the "MainThread" by emitting a Qt Signal
class MyReceiver(QObject):
mysignal = pyqtSignal(str)
def __init__(self,queue,*args,**kwargs):
QObject.__init__(self,*args,**kwargs)
self.queue = queue
#pyqtSlot()
def run(self):
while True:
text = self.queue.get()
self.mysignal.emit(text)
# An example QObject (to be run in a QThread) which outputs information with print
class LongRunningThing(QObject):
#pyqtSlot()
def run(self):
for i in range(1000):
print i
# An Example application QWidget containing the textedit to redirect stdout to
class MyApp(QWidget):
def __init__(self,*args,**kwargs):
QWidget.__init__(self,*args,**kwargs)
self.layout = QVBoxLayout(self)
self.textedit = QTextEdit()
self.button = QPushButton('start long running thread')
self.button.clicked.connect(self.start_thread)
self.layout.addWidget(self.textedit)
self.layout.addWidget(self.button)
#pyqtSlot(str)
def append_text(self,text):
self.textedit.moveCursor(QTextCursor.End)
self.textedit.insertPlainText( text )
#pyqtSlot()
def start_thread(self):
self.thread = QThread()
self.long_running_thing = LongRunningThing()
self.long_running_thing.moveToThread(self.thread)
self.thread.started.connect(self.long_running_thing.run)
self.thread.start()
# Create Queue and redirect sys.stdout to this queue
queue = Queue()
sys.stdout = WriteStream(queue)
# Create QApplication and QWidget
qapp = QApplication(sys.argv)
app = MyApp()
app.show()
# Create thread that will listen on the other end of the queue, and send the text to the textedit in our application
thread = QThread()
my_receiver = MyReceiver(queue)
my_receiver.mysignal.connect(app.append_text)
my_receiver.moveToThread(thread)
thread.started.connect(my_receiver.run)
thread.start()
qapp.exec_()

wxPython: Threading GUI --> Using Custom Event Handler

I am trying to learn how to run a thread off the main GUI app to do my serial port sending/receiving while keeping my GUI alive. My best Googling attempts have landed me at the wxpython wiki on: http://wiki.wxpython.org/LongRunningTasks which provides several examples. I have settled on learning the first example, involving starting a worker thread when the particular button is selected.
I am having trouble understanding the custom-event-definition:
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
Primarily the
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
I think EVT_RESULT is placed outside the classes so as to make it call-able by both classes (making it global?)
And.. the main GUI app monitors the thread's progress via:
# Set up event handler for any worker thread results
EVT_RESULT(self,self.OnResult)
I also notice that in a lot of examples, when the writer uses
from wx import *
they simply bind things by
EVT_SOME_NEW_EVENT(self, self.handler)
as opposed to
wx.Bind(EVT_SOME_NEW_EVENT, self.handler)
Which doesn't help me understand it any faster.
Thanks,
That's the old style of defining custom events. See the migration guide for more information.
Taken from the migration guide:
If you create your own custom event
types and EVT_* functions, and you
want to be able to use them with the
Bind method above then you should
change your EVT_* to be an instance of wx.PyEventBinder instead of a
function. For example, if you used to
have something like this:
myCustomEventType = wxNewEventType()
def EVT_MY_CUSTOM_EVENT(win, id, func):
win.Connect(id, -1, myCustomEventType, func)
Change it like so:
myCustomEventType = wx.NewEventType()
EVT_MY_CUSTOM_EVENT = wx.PyEventBinder(myCustomEventType, 1)
Here is another post that I made with a couple of example programs that do exactly what you are looking for.
You can define events like this:
from wx.lib.newevent import NewEvent
ResultEvent, EVT_RESULT = NewEvent()
You post the event like this:
wx.PostEvent(handler, ResultEvent(data=data))
Bind it like this:
def OnResult(event):
event.data
handler.Bind(EVT_RESULT, OnResult)
But if you just need to make a call from a non-main thread in the main thread you can use wx.CallAfter, here is an example.
Custom events are useful when you don't want to hard code who is responsible for what (see the observer design pattern). For example, lets say you have a main window and a couple of child windows. Suppose that some of the child windows need to be refreshed when a certain change occurs in the main window. The main window could directly refresh those child windows in such a case but a more elegant approach would be to define a custom event and have the main window post it to itself (and not bother who needs to react to it). Then the children that need to react to that event can do it them selves by binding to it (and if there is more than one it is important that they call event.Skip() so that all of the bound methods get called).
You may want to use Python threads and queues and not custom events. I have a wxPython program (OpenSTV) that loads large files that caused the gui to freeze during the loading. To prevent the freezing, I dispatch a thread to load the file and use a queue to communicate between the gui and the thread (e.g., to communicate an exception to the GUI).
def loadBallots(self):
self.dirtyBallots = Ballots()
self.dirtyBallots.exceptionQueue = Queue(1)
loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
loadThread.start()
# Display a progress dialog
dlg = wx.ProgressDialog(\
"Loading ballots",
"Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots),
parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
)
while loadThread.isAlive():
sleep(0.1)
dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots))
dlg.Destroy()
if not self.dirtyBallots.exceptionQueue.empty():
raise RuntimeError(self.dirtyBallots.exceptionQueue.get())

Thread Finished Event in Python

I have a PyQt program, in this program I start a new thread for drawing a complicated image.
I want to know when the thread has finished so I can print the image on the form.
The only obstacle I'm facing is that I need to invoke the method of drawing from inside the GUI thread, so I want a way to tell the GUI thread to do something from inside the drawing thread.
I could do it using one thread but the program halts.
I used to do it in C# using a BackgroundWorker which had an event for finishing.
Is there a way to do such thing in Python? or should I hack into the main loop of PyQt application and change it a bit?
In the samples with PyQt-Py2.6-gpl-4.4.4-2.exe, there's the Mandelbrot app. In my install, the source is in C:\Python26\Lib\site-packages\PyQt4\examples\threads\mandelbrot.pyw. It uses a thread to render the pixmap and a signal (search the code for QtCore.SIGNAL) to tell the GUI thread its time to draw. Looks like what you want.
I had a similar issue with one of my projects, and used signals to tell my main GUI thread when to display results from the worker and update a progress bar.
Note that there are several examples to connect objects and signals in the PyQt reference guide. Not all of which apply to python (took me a while to realize this).
Here are the examples you want to look at for connecting a python signal to a python function.
QtCore.QObject.connect(a, QtCore.SIGNAL("PySig"), pyFunction)
a.emit(QtCore.SIGNAL("pySig"), "Hello", "World")
Also, don't forget to add __pyqtSignals__ = ( "PySig", ) to your worker class.
Here's a stripped down version of what I did:
class MyGui(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.worker = None
def makeWorker(self):
#create new thread
self.worker = Worker(work_to_do)
#connect thread to GUI function
QtCore.QObject.connect(self.worker, QtCore.SIGNAL('progressUpdated'), self.updateWorkerProgress)
QtCore.QObject.connect(self.worker, QtCore.SIGNAL('resultsReady'), self.updateResults)
#start thread
self.worker.start()
def updateResults(self):
results = self.worker.results
#display results in the GUI
def updateWorkerProgress(self, msg)
progress = self.worker.progress
#update progress bar and display msg in status bar
class Worker(QtCore.QThread):
__pyqtSignals__ = ( "resultsReady",
"progressUpdated" )
def __init__(self, work_queue):
self.progress = 0
self.results = []
self.work_queue = work_queue
QtCore.QThread.__init__(self, None)
def run(self):
#do whatever work
num_work_items = len(self.work_queue)
for i, work_item in enumerate(self.work_queue):
new_progress = int((float(i)/num_work_items)*100)
#emit signal only if progress has changed
if self.progress != new_progress:
self.progress = new_progress
self.emit(QtCore.SIGNAL("progressUpdated"), 'Working...')
#process work item and update results
result = processWorkItem(work_item)
self.results.append(result)
self.emit(QtCore.SIGNAL("resultsReady"))
I believe that your drawing thread can send an event to the main thread using QApplication.postEvent. You just need to pick some object as the receiver of the event. More info
Expanding on Jeff's answer: the Qt documentation on thread support states that it's possible to make event handlers (slots in Qt parlance) execute in the thread that "owns" an object.
So in your case, you'd define a slot printImage(QImage) on the form, and a doneDrawing(QImage) signal on whatever is creating the image, and just connect them using a queued or auto connection.

Categories