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.
Related
I am developing an application with Qt GUI (PyQt5) in python. In the main window I have a QTextEdit that is connected to the logging handler more or less as described below.
class ConsolePanelHandler(logging.Handler):
def __init__(self, parent):
logging.Handler.__init__(self)
self.parent = parent
def emit(self, record):
self.parent.write_log_message(self.format(record))
class MyAppWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.connectSignalsSlot()
def write_log_message(self, s):
self.messagewindow.setFontWeight(QtGui.QFont.Normal)
self.messagewindow.append(s)
if __name__ == "__main__":
# prepare the logging machinery
log = logging.getLogger(__name__)
log.setLevel(level=logging.INFO)
# start the Qt App
app = QApplication(sys.argv)
win = MyAppWindow()
# connect the logging handler to the Qt App
handler = ConsolePanelHandler(win)
handler.setFormatter(logging.Formatter('[%(asctime)s] - %(levelname)s: %(message)s',datefmt='%Y%m%d-%H:%M:%S'))
log.addHandler(handler)
# show the main window
win.show()
# execute the main window and eventually exit when done!
sys.exit(app.exec())
In general everything works fine, but as soon as the application load is becoming high (CPU and/or I/O), then the GUI is becoming unresponsive and the QTextEdit is not updated. From the user point of view, it looks like that the program crashed, but actually it is just busy working.
When the high load task is done, the GUI returns to be responsive and all log entries are displayed all together.
I guess that the solution would be to spawn a new thread where the high load task is done in order to leave the Qt thread almost free.
Do you have a better solution?
Thanks for your help
Update: implemented multithreading
I've tried to implemented a multithread solution, it is to say that when the user click on the button to start the I/O heavy operation, this is executed in a new thread as below.
from threading import Thread
class MyAppWindow(QMainWindow, Ui_MaindWindow):
# skipping all unnnecessary lines of code
def on_mouse_click(self):
self.thread = threading.Thread(target=self.perform_IO_task)
self.thread.start()
# don't put self.thread.join() here
def perform_IO_task(self):
# do the job
# print log messages
# ...
# self.thread.join() FAILS!
This implementation is working, the IO task is executed and the GUI remains workable. Now the question is when should I join the thread?
If I put the join() statement as last line of the on_mouse_click method, the application will 'freeze' the GUI and wait for the thread to finish without showing updates on the log. This is the correct behaviour according to the documentation.
If I put it in end of the target call back, the application fails saying that it is not possible to join the thread.
If I got it right, join() is needed to force a thread to wait for the output of another one before continuing. In my case, it is not needed, so I'm tempted to skip the join() statement... Will this cause any issue?
There are various things you need to think about when using Qt and updating the GUI (updates should be from the main thread). I recommend you look at the logging cookbook which has a working example, and adapt that approach to your needs.
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.
When I try to use one QDialog object from threads I get this error.
Here is the code I'm using:
import threading
import test_qdialog
from PyQt4 import QtGui, QtCore
class MyThread(threading.Thread):
def __init__(self, id, window, mutex):
self.id = id
self.window = window
self.mutex = mutex
super(MyThread, self).__init__()
def run(self):
with self.mutex:
result = self.window.exec_()
if result == QtGui.QDialog.Accepted:
print "Thread %d: %s" % (self.id, self.window.message_input.text())
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
mutex = threading.Lock()
threads = []
window = test_qdialog.MyDialog()
for i in range(5):
thread = MyThread(i, window, mutex)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
sys.exit(app.exec_())
As written in this answer, if I get it right, I can't do it this way. But how can I do it then?
You can only create and use GUI widgets on main thread (every UI library that I know is like that). However, you can easily pass signals from threads to main using QtCore.QtThread. See for example the answer to PyQt threads and signals - how to properly retrieve values (even if the answer is not what the OP was looking for, it is relevant to your situation). May also find this SO post useful.
So instead of creating or accessing the dialog from thread, you would emit a signal from thread, and have your main window connected to it create the dialog when it receives the signal. Qt takes care of transfering data between threads. Will work like a charm.
Definitely take a close look at Qt Threading Basics, if you haven't already (if you have, may want to post questions about parts you don't understand, there is tons of important info there).
The QT Widgets can't be accessed from a thread which is not the main thread. For example, if you call mainwindow.show(), the program will crash.
However, this can be easily addressed using QThread. The principle is that instead of controlling the e.g., mainwindow directly, we can send a signal to the main thread, letting the main thread to call the show() method. A signal can carry anything, such as a String or Integer.
I'd strongly suggest you to watch this video. It will solve your problem in 10 minutes.
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_()
I'm relatively new to wxPython (but not Python itself), so forgive me if I've missed something here.
I'm writing a GUI application, which at a very basic level consists of "Start" and "Stop" buttons that start and stop a thread. This thread is an infinite loop, which only ends when the thread is stopped. The loop generates messages, which at the moment are just output using print.
The GUI class and the infinite loop (using threading.Thread as a subclass) are held in separate files. What is the best way to get the thread to push an update to something like a TextCtrl in the GUI? I've been playing around with PostEvent and Queue, but without much luck.
Here's some bare bones code, with portions removed to keep it concise:
main_frame.py
import wx
from loop import Loop
class MainFrame(wx.Frame):
def __init__(self, parent, title):
# Initialise and show GUI
# Add two buttons, btnStart and btnStop
# Bind the two buttons to the following two methods
self.threads = []
def onStart(self):
x = Loop()
x.start()
self.threads.append(x)
def onStop(self):
for t in self.threads:
t.stop()
loop.py
class Loop(threading.Thread):
def __init__(self):
self._stop = threading.Event()
def run(self):
while not self._stop.isSet():
print datetime.date.today()
def stop(self):
self._stop.set()
I did, at one point, have it working by having the classes in the same file by using wx.lib.newevent.NewEvent() along these lines. If anyone could point me in the right direction, that'd be much appreciated.
The easiest solution would be to use wx.CallAfter
wx.CallAfter(text_control.SetValue, "some_text")
You can call CallAfter from any thread and the function that you pass it to be called will be called from the main thread.