R
R
RussianSuburban2016-03-19 13:55:52
Python
RussianSuburban, 2016-03-19 13:55:52

How to work with QThread correctly?

Python 3.4, PyQt 4.8, Windows 10
There is a main thread in which the program itself runs. Inside this thread, I need to start a new thread in the background so that the main thread does not freeze (This main thread is responsible for working with the GUI). From a background thread, some GUI elements need to be changed. I did everything as follows:
In the main form:

self.connect(self.pushButton_4,QtCore.SIGNAL("clicked()"),self.addAllAccounts)
...
...
def addAllAccounts(self):
      print("pushButton_4 нажата")
        login,password,name=[],[],[]
        #Здесь пропущен для удобства кусок, в котором я заполняю login, password, name
        #Если массив логинов не пустой, запускаю отдельный тред
        if login:
            self.pushButton_4.setEnabled(False) #Блокирую кнопку
            self.runnable = AThread(login,password,name)
            self.runnable.progressed.connect(self.progressBarHandler)
            self.runnable.finished.connect(app.exit)
            self.runnable.start()
            self.label_43.setText("Подождите, добавление займет некоторое время...")
            self.progressBar.setMinimum(0)    
            self.progressBar.setMaximum(len(name))
            self.progressBar.setValue(0)
            self.progressBar.show()
            self.label_43.show()
           #Проблема вот в этой строчке
            self.runnable.wait() #Если её не использовать, программа падает в случайном месте
           #не выдавая ошибок; Если её использовать,  GUI замораживается и программа 
           #  подвисает, пока работает фоновый поток

What am I doing wrong? AThread inherits from QThread and rewrites the run() method. It looks like this:
class AThread(QtCore.QThread):
    progressed = pyqtSignal()

    def __init__(self,logins,passwords,names):
        super(AThread,self).__init__()
        self.logins=logins
        self.passwords=passwords
        self.names=names
        
    def run(self):
        print(self.logins)
        print(self.passwords)
        print(self.names)
        for i in range(0,len(self.logins)):
            login = self.logins[i]
            password = self.passwords[i]
            name = self.names[i]
            checkResult = vk.checkIfValid(login,password)
           self.progressed.emit()

In the main program, a handler is attached to the progressed signal, which works with the GUI.
The problem is that the program crashes if you don't use runnable.wait() on the main thread. And if you use it, then the GUI freezes.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Alexander, 2016-03-21
@Avernial

Can you provide the whole code? Check if simple code works with progress update.

from PyQt4.QtGui import QWidget, QVBoxLayout, QPushButton, QProgressBar, QApplication
from PyQt4.QtCore import QThread, pyqtSignal
import time


class SomeThread(QThread):
    progressed = pyqtSignal(int)

    def __init__(self):
        super().__init__()

    def run(self):
        for i in range(1, 11):
            self.progressed.emit(i)
            time.sleep(0.5)


class App(QWidget):

    def __init__(self):
        super().__init__()
        vbox = QVBoxLayout()
        self.pBar = QProgressBar()
        self.pBar.setMaximum(10)
        vbox.addWidget(self.pBar)
        self.button = QPushButton("Start")
        vbox.addWidget(self.button)
        self.thread = None
        self.setLayout(vbox)
        self.button.clicked.connect(self.on_button)

    def on_button(self):
        if not self.thread:
            self.thread = SomeThread()
            self.thread.progressed.connect(self.on_progress)
            self.thread.finished.connect(self.on_finished)
            self.thread.start()

    def on_progress(self, value):
        self.pBar.setValue(value)

    def on_finished(self):
        self.thread.progressed.disconnect(self.on_progress)
        self.thread.finished.disconnect(self.on_finished)
        self.thread = None

if __name__ == '__main__':
    qApp = QApplication([])
    app = App()
    app.show()
    qApp.exec()

A
Alexey, 2016-03-19
@MAKAPOH

I am not a specialist in the internal architecture of Qt, but as I understand it, for the correct interaction of signals and slots from different threads, both must have a working event loop and the connection must be made through some kind of internal queue and not directly (for this, the connected objects must belong to different threads). You have a runnable object on the GUI thread. Perhaps because of this problem. Try to transfer all your manipulations to a separate object of the calculator, the successor of QObject, to the AThread constructor to pass everything you need to communicate with the "outside world", and in the run method create a calculator object by connecting it with signals and slots with objects from the thread's GUI and then calling exec( ) . Here you can see a more detailed article with an example.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question