Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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_()
like image 386
Tony Stark Avatar asked Oct 31 '25 04:10

Tony Stark


2 Answers

A few things:

  1. 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.

  2. 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.

like image 149
Brendan Abel Avatar answered Nov 02 '25 16:11

Brendan Abel


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.

like image 35
pistache Avatar answered Nov 02 '25 17:11

pistache



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!