Проблема PyQt / PySide2 с реализацией QThread, когда я пытаюсь передать метод другого класса конструктору класса потока - PullRequest
0 голосов
/ 05 мая 2020

Итак, вот я, новичок ie, который пытается заново изобрести колесо, а также правильно распределять потоки в PySide2. Моя проблема в том, что мой поток, кажется, запускается нормально, но я не получаю вывода от функции, которую он должен выполнять, и я не могу понять почему.

Вот краткое объяснение того, как я установил все это и минимальный воспроизводимый пример. У меня есть файл, скажем, он называется «so_helpers» для демонстрации. Внутри есть 2 класса, один - это QThread под названием «Внешний», который я пытаюсь создать и запустить разумным способом (очевидно, нет, поскольку он не работает), а другой - Updater, метод которого я пытаюсь перейти к External для выполнения. Вот код моего «so_helpers»:

from PySide2.QtCore import QThread, Signal

class Updater: 
    def __init__(self):
        print("connection to db established")

    def process(self):
        print("started")
        print("doing the update")
        print("finished")

class External(QThread):
    finished = Signal()

    def __init__(self, func):
        super().__init__()
        self.func = func

    def run(self):
        self.func()
        self.finished.emit()

И это мой пользовательский интерфейс, в данном примере назовем его «for_SO_ui». Он был автоматически сгенерирован pyuic5 из файла .ui:

from PySide2 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(644, 187)
        MainWindow.setMinimumSize(QtCore.QSize(644, 187))
        MainWindow.setMaximumSize(QtCore.QSize(644, 187))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(80, 30, 491, 111))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "THREADING IS AWESOME!"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

И вот main.py, в котором все должно собраться вместе (но это не так, и это меня огорчает):

from so_helpers import *
from for_SO_ui import *
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import QThread, Signal
import sys

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    startThread = Signal()
    threadDone = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.connectSignals()

    def connectSignals(self):
        self.pushButton.clicked.connect(self.onButtonClick)
        self.startThread.connect(self.onThreadStart)
        self.threadDone.connect(self.onThreadDone)

    def onButtonClick(self):
        self.startThread.emit()

    def onThreadStart(self):
        self.thread_object = External(self.threadFunction)
        self.thread = QThread()
        self.thread_object.moveToThread(self.thread)
        self.thread_object.finished.connect(self.onThreadDone)
        self.thread.start()

    def onThreadDone(self):
        self.thread.start()

    def threadFunction(self):
        func = Updater.process
        return func

    def closeEvent(self, event):
        self.thread.quit()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ui = MainWindow()
    ui.show()
    sys.exit(app.exec_())

Любая помощь, объяснения, рекомендации приветствуются!

1 Ответ

1 голос
/ 05 мая 2020

Есть несколько проблем с вашим кодом.

Прежде всего, External уже является подклассом QThread, и нет необходимости создавать новый поток для его размещения. Даже в этом случае, всякий раз, когда вы перемещаете QObject в другой поток, вы должны подключить сигнал started потока к фактической функции, которая должна быть выполнена (в вашем случае, run).

Вызов self.func() будет не запускать функцию process, а вместо этого threadFunction, которая просто возвращает Updater.process.

Тогда вам нужен доступ к функции, но вы ' re возвращает вместо этого несвязанный метод класса (Updater.process), а не метод экземпляра, поэтому его вызов вызовет исключение, поскольку process требует как минимум аргумента (self).
Вы можете использовать @staticmethod (и удалите self) или создайте экземпляр класса.

Наконец, QThread уже имеет сигнал finished(), вы не должны его перезаписывать или использовать свой собственный сигнал для перезапуска поток: когда вы испускаете свой собственный сигнал, поток все равно будет работать, и в результате он может не запуститься снова, как описано в документации о QThread.start():

[...] Если три ead уже запущен, эта функция ничего не делает.

Итак, вы либо используете исходный сигнал QThread finished, либо QThread.wait(), чтобы дождаться завершения перед его запуском снова.

Это возможное исправление для вашего кода:

class External(QThread):
    def __init__(self, func):
        super().__init__()
        self.func = func

    def run(self):
        self.func()


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):

    startThread = pyqtSignal()
    threadDone = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.connectSignals()

    def connectSignals(self):
        self.pushButton.clicked.connect(self.onButtonClick)
        self.startThread.connect(self.onThreadStart)
        self.threadDone.connect(self.onThreadDone)

    def onButtonClick(self):
        self.startThread.emit()

    def onThreadStart(self):
        self.thread = External(self.threadFunction())
        self.thread.finished.connect(self.onThreadDone)
        self.thread.start()

    def onThreadDone(self):
        self.thread.start()

    def threadFunction(self):
        self.updater = Updater()
        return self.updater.process

    def closeEvent(self, event):
        self.thread.finished.disconnect(self.onThreadDone)
        self.thread.quit()

PS: Для справки в будущем: если строки документации / комментарии не требуются для лучшего понимания вашего кода, удалите им.

...