Как структурировать большой графический интерфейс pyqt5 без создания подкласса QThread и использования QPushButtons для выполнения долгосрочных задач - PullRequest
0 голосов
/ 26 сентября 2019

Я смотрю на создание программы с PyQt5 GUI.Программа начнется с пользовательского интерфейса с многочисленными кнопками.Эти кнопки будут использоваться для открытия других программ / завершенных длительных задач.Я знаю, что мне нужно использовать QThread, но я не уверен, как структурировать программы так, чтобы они правильно масштабировались.

Я занимался этим целую вечность и прочитал множество постов / руководств.Большинство склоняется по маршруту подклассов.Раньше мне удавалось создать рабочую программу с подклассами QThread, но с тех пор я читал, что эта методология не является предпочтительной.

У меня такое чувство, что я должен создать универсальный рабочий и передать функцию с* args и ** kwargs, но этого пока нет в моем наборе навыков.

Я изначально создал поток для каждой кнопки во время инициализации GUI, но казалось, что он скоро выйдет из-под контроля.

В настоящее время я нахожусь на стадии создания потока под слотом, подключенным к сигналу button.clicked.Я не уверен, должен ли я тогда иметь работника для каждой кнопки, или я могу / должен сделать общего работника и передать функцию.Примечание: я пытался сделать это, но не смог этого сделать.

#Import standard modules
import sys

#Import third-party modles
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QVBoxLayout, QWidget

class Worker(QObject):

    #Custom signals?? or built-in QThread signals?
    started = pyqtSignal()    
    finished = pyqtSignal()

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

    @pyqtSlot()
    def do_something(self):
            for _ in range(3):
                print('Threading...')
                QThread.sleep(1)
            self.finished.emit()

class Window(QMainWindow):

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

    def initUi(self):
        #Create GUI
        self.centralWidget  = QWidget()
        self.setCentralWidget(self.centralWidget )  
        self.vertical_layout = QVBoxLayout(self.centralWidget)
        self.setWindowTitle('QThread Test')
        self.setGeometry(300, 300, 300, 50)
        self.button1=QPushButton("Task 1", self, clicked=self._task1_clicked)
        self.button2=QPushButton("Task 2", self, clicked=self._task2_clicked)
        self.vertical_layout.addWidget(self.button1)
        self.vertical_layout.addWidget(self.button2)
        self.vertical_layout.addStretch()

    def _task1_clicked(self):
        print('task1 clicked')

        #Create the worker
        self.my_worker = Worker()          

        #Create thread; needs to be done before connecting signals/slots
        self.task1_thread = QThread()

        #Move the worker to the thread
        self.my_worker.moveToThread(self.task1_thread)        

        #Connect worker and thread signals to slots
        self.task1_thread.started.connect(self._thread_started)
        self.task1_thread.started.connect(self.my_worker.do_something)
        self.my_worker.finished.connect(self._thread_finished) 

        #Start thread
        self.task1_thread.start() 

    def _task2_clicked(self):
        print('task2 clicked')

    def _thread_started(self):
        print('thread started')

    def _thread_finished(self):
        print('thread finished')
        self.my_worker.isRunning = False
        self.task1_thread.quit()
        self.task1_thread.wait()
        print('The thread is running: ' + str(self.task1_thread.isRunning()))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    form = Window()
    form.show()
    app.exec_()

Выше, кажется, работает, но я чувствую, что наткнулся на это, и это не правильный способделая это.Я не хочу, чтобы это был мой метод перехода, если он полностью неверен.Я хотел бы иметь возможность генерировать более сложные программы (выполняющие большее количество кнопок) по сравнению с программой «одна кнопка / одна задача».

Кроме того, я не могу получить сигналы запуска и завершения QThreadстрелять, не делая их в основном на заказ сигналов.Это одна из причин, по которой я думаю, что поступаю неправильно.

1 Ответ

0 голосов
/ 26 сентября 2019
from PyQt5 import QtCore

class AsyncTask(QtCore.QThread):
    taskDone = QtCore.pyqtSignal(dict)
    def __init__(self, *, task, callback=None, parent = None):
        super().__init__(parent)
        self.task = task
        if callback != None:
            self.taskDone.connect(callback)
        if callback == None:
            callback = self.callback
        self.start()

    def run(self):
        try:
            result = self.task()
            print(result)
            self.taskDone.emit(result)
        except Exception as ex:
            print(ex)

    def callback(self):
        print('callback')

Пожалуйста, попробуйте код выше, позвоните так: AsyncTask (task = yourTaskFunction, callback = yourCallbackFunction)

...