【问题标题】:How to use Threading with variables inside a function? PyQt5如何在函数内使用带变量的线程? PyQt5
【发布时间】:2020-06-03 21:07:16
【问题描述】:

我有一个很大的函数可以冻结我的 PyQt5 程序,我尝试为它使用不同的线程(为此我使用 QThread)。问题是我的函数需要一些变量才能正常工作。 如何使这个工作?我展示了我的所作所为。

原码:

class AnalysisWindow(QtWidgets.QMainWindow):

    def __init__(self, firstWindow):
        super(AnalysisWindow, self).__init__()       
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(self.letsgo)

    def letsgo(self):
        #here some code , not big
        #and at some point i have a heavy one which make it freeze until it's over:
          self.x1, self.x2, self.y1,self.y2, self.z, = self.analyze(self.i1, self.i2, self.i3)

    def analyze(self,i1,i2,i3):
        #big function
        return(x1,x2,y1,y2,z)

我尝试了什么:

from PyQt5.QtCore import Qt, QThread, pyqtSignal


class AnalysisWindow(QtWidgets.QMainWindow):

    class MyThread(QThread):         

        _signal =pyqtSignal()
        def __init__(self):
            super().__init__()

        def run(self,i1,i2,i3):  # here I obviously can't put variables

            #I copied here my analyze function
            return(x1,x2,y1,y2,z)         

            self._signal.emit()

    def __init__(self, firstWindow):
        super(AnalysisWindow, self).__init__()       
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(self.letsgo)

    def letsgo(self):
        self.thread = MyThread()           
        self.thread.start()
        #here I dont see how to send the variables self.i1, self.i2, self.i3 and how to get the result: x1,x2,y1,y2,z

我在 QMainWindow 类中创建了线程类,因为我需要将一些变量(self.i1、self.i2、self.i3)从 QMainWindow 传递给将使用新线程的函数。也许这很糟糕,但它无论如何都不起作用。谢谢大家。

【问题讨论】:

    标签: python pyqt5 qthread


    【解决方案1】:

    这是一个最小的工作示例,您可以将其调整为您的代码。 有几点需要注意:

    • 你不应该从QThread继承。相反,您应该创建一个工作线程并将其移动到您的线程中。
    • 在 worker 中,而不是尝试 return 一个结果,emit 保存结果的信号并在您的应用程序中处理该信号。
    • 类似地,与其尝试正常呼叫您的工人,不如通过其插槽通过QtCore.QMetaObject.invokeMethod 与它通信。线程启动后,您可以根据需要调用此方法。

    更多信息请参考this答案

    import sys
    import random
    from PyQt5.QtCore import QThread, pyqtSignal, QObject, pyqtSlot, Qt
    from PyQt5 import QtWidgets
    from PyQt5 import QtCore
    
    
    class Analyzer(QObject):
        analyze_completed = pyqtSignal(bool)
        analyze_result = pyqtSignal(list, int)
    
        @pyqtSlot(str, list)
        def analyze(self, foo, analyze_args):
            print(foo, analyze_args)
            self.analyze_completed.emit(False)
    
            # do your heavy calculations
            for i in range(10000000):
                x = i ** 0.5
    
            result = sum(analyze_args)
            self.analyze_result.emit(analyze_args, result)
            self.analyze_completed.emit(True)
    
    
    class AnalysisWindow(QtWidgets.QWidget):
    
        def __init__(self):
            super().__init__()
            self.label = QtWidgets.QLabel("")
            self.i = 0
            self.label_i = QtWidgets.QLabel("Value of i: {}".format(self.i))
            self.increment_button = QtWidgets.QPushButton("increment i")
            self.pushbutton = QtWidgets.QPushButton("Analyze")
            super(AnalysisWindow, self).__init__()
            self.analyze_args = []
            self.analyzer = Analyzer()
            self.thread = QThread()
            self.analyzer.analyze_result.connect(self.on_analyze_result_ready)
            self.analyzer.analyze_completed.connect(self.on_analyze_completed)
            self.analyzer.moveToThread(self.thread)
            self.thread.start()
            self.init_UI()
    
        def init_UI(self):
            grid = QtWidgets.QGridLayout()
            grid.addWidget(self.label, 0, 0)
            grid.addWidget(self.pushbutton)
            grid.addWidget(self.label_i)
            grid.addWidget(self.increment_button)
            self.increment_button.clicked.connect(self.increment_i)
            self.pushbutton.clicked.connect(self.start_analyze)
            self.setLayout(grid)
            self.move(300, 150)
            self.setMinimumSize(300, 100)
            self.setWindowTitle('Thread Test')
            self.show()
    
        def start_analyze(self):
            self.analyze_args.clear()
            self.analyze_args.extend(random.choices(range(100), k=5))
            QtCore.QMetaObject.invokeMethod(self.analyzer, 'analyze', Qt.QueuedConnection,
                                            QtCore.Q_ARG(str, "Hello World!"),
                                            QtCore.Q_ARG(list, self.analyze_args))
    
        def increment_i(self):
            self.i += 1
            self.label_i.setText("Value of i: {}".format(self.i))
    
        def on_analyze_result_ready(self, args, result):
            t = "+".join(str(i) for i in args)
            self.label.setText(f"{t} = {result}")
    
        def on_analyze_completed(self, completed):
            if completed:
                self.label.setStyleSheet('color: blue')
            else:
                self.label.setText(
                    "Analyzing... {}".format(", ".join(str(i) for i in self.analyze_args)))
                self.label.setStyleSheet('color: yellow')
    
    
    app = QtWidgets.QApplication(sys.argv)
    
    widget = AnalysisWindow()
    
    sys.exit(app.exec_())
    
    
    

    希望这会有所帮助!

    【讨论】:

    • 非常感谢您的帮助,但是在这里当我运行它时,窗口一直冻结...直到计算或睡眠完成。我认为我们使用相同的线程(self.thread)而不是另一个线程,但不确定......我是一个非常初学者!再次感谢。
    • 哎呀,你是对的!我应该做错什么了。让我检查一下……
    • 我编辑了我的答案。事实证明,您需要在计算的每次迭代中调用QtCore.QCoreApplication.processEvents()。如果您没有任何循环,请尝试尽可能多地调用它。
    • 我忘了给你加标签,我不确定你是否仍然收到通知,但你去@DarkWarrior
    • 嘿,是的,我明白了。我试图实现这一点,问题是如果我使用 QcoreApplication.processEvents(),它确实有效,因为窗口不再冻结,但函数需要 20% 到 25% 的时间......是的,正如你所说的我的循环是原因...我仍在尝试解决此问题...!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多