@RussianSuburban

Как правильно работать с QThread?

Python 3.4, PyQt 4.8, Windows 10

Есть основной поток, в котором работает сама программа. Внутри этого потока мне надо запустить новый поток в фоновом режиме, чтоб основной поток не замораживался (Этот основной поток отвечает за работу с GUI). Из фонового потока нужно менять некоторые элементы GUI. Делал всё следующим образом:

В основной форме:
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 замораживается и программа 
           #  подвисает, пока работает фоновый поток


Что я делаю не так? AThread наследуется от QThread, в нем переписан метод run(). Выглядит оно вот так:
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()



В основной программе на сигнал progressed повешен обработчик, который работает с GUI.

Проблема в том, что программа крашится, если не использовать runnable.wait() в основном потоке. А если использовать, то GUI подвисает.
  • Вопрос задан
  • 3015 просмотров
Пригласить эксперта
Ответы на вопрос 3
Avernial
@Avernial
Разрабатываю научное ПО на языке Python.
Можете привести весь код? Проверьте работает ли простой код с обновлением прогресса.

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()
Ответ написан
MAKAPOH
@MAKAPOH
многостаночник
Я не спец во внутреней архитектуре Qt но как я понял для правильного взаимодействия сигналов и слотов из разных потоков оба должны иметь работающий событийный цикл и соединение должно осуществлятся через некую внутренюю очередь а не напрямую (для этого соединяемые объекты должны принадлежать разным потокам). У вас объект runnable находится в GUI потоке. Возможно из за этого проблемы. Попробуйте все ваши манипуляции вынести в отдельный объект вычислитель, наследник QObject, в конструктор AThread передать всё что нужно для связи с "внешним миром", а в методе run создать объект вычислитель соединив его сигналами и слотами с объектами из GUI потока вызвав после этого exec(). Здесь можно посмотреть более подробную статью с примером.
Ответ написан
@RussianSuburban Автор вопроса
Да, этот код работает.

За эти несколько дней мне удалось локализовать проблему: я делаю последовательную (в цикле) цепочку http запросов через requests в SomeThread.run(). Там-то и происходит что-то потоконебезопасное. Поскольку я делаю эти запросы последовательно, и поскольку я каждый раз после очередного запроса жду его результатов, прежде чем делать новый, мне совершенно непонятно, что там может быть не так. Эта же цепочка запросов в основном треде работает отлично.

Обязательно ли делать disconnect сигналов в основном треде?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
Dialog Москва
от 100 000 ₽
OnederX Москва
от 100 000 до 120 000 ₽
NatsON Москва
от 220 000 до 350 000 ₽
15 авг. 2020, в 05:42
50000 руб./за проект
15 авг. 2020, в 05:23
5000 руб./за проект
15 авг. 2020, в 04:49
5000 руб./за проект