简体   繁体   English

Qt中的QThread在Python上

[英]QThread in Qt on Python

I employed QThread using a worker object after reading extended discussions on how QThread should be used and overwriting its run method via subclassing it is not the proper method. 我使用的QThread使用阅读的QThread应如何使用扩展的讨论和通过继承是不正确的方法重写它的run方法后,一个工人对象。 However, in the method I intend to use I need to pass an additional function argument which is not available at the time the thread is initiated and the worker is pushed to the thread using moveToThread . 但是,在我打算使用的方法中,我需要传递一个额外的函数参数,该参数在启动线程时不可用,并且使用moveToThread将worker推送到线程。 This information (argument) is available at the time a button is pressed and conveys the information on which object to move. 按下按钮时可以使用此信息(参数),并传达有关要移动的对象的信息。

In the full version of my code there are three separate controllers for three separate objects and you can find a minimal working example below demonstrating what I tried to pass the argument. 在我的代码的完整版本中,有三个独立的控制器用于三个独立的对象,您可以在下面找到一个最小的工作示例,演示我尝试传递参数的内容。 The code is also available on pastebin and the line numbers of interest are 10-28, 46-50 and 133-135. 代码也可以在pastebin上获得,感兴趣的行号是10-28,46-50和133-135。

So far I have tried using a lambda constructor in the line connecting to the actual function in the worker. 到目前为止,我已尝试在连接到worker中的实际函数的行中使用lambda构造函数。 That is the line self.thread.started.connect(self.obj.moveLeftIncrement) then tried to use slots , but I did not understand them quite well. 那就是self.thread.started.connect(self.obj.moveLeftIncrement)这一行然后尝试使用插槽 ,但我对它们并不了解。 Furthermore, despite using QThread sometimes the GUI hangs and there are errors following program exit one of which is as follows: 此外,尽管使用QThread有时GUI挂起并且程序退出后出现错误,其中一个如下:

Process finished with exit code -1073740791 (0xC0000409) 进程以退出代码-1073740791(0xC0000409)结束

My questions are below: 我的问题如下:

  1. How can I pass the parameter at runtime and/or use slots? 如何在运行时传递参数和/或使用插槽?
  2. How can I prevent program errors upon exit? 如何在退出时阻止程序错误?
  3. Why does subclassing QThread directly works in this case although it is not recommended? 为什么子类化QThread直接适用于这种情况,尽管不建议这样做?

from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
import sys
import time

class Worker(QObject):
    finished = Signal(int)

    @Slot(str)
    def moveLeftIncrement(self, controller_name):
        # controller_name = "Controller 1"
        print("Controller name is ", controller_name)
        if controller_name == "Controller 1":
            print("Starting controller 1")
            time.sleep(2)
            print("Finishing sleep")
        elif controller_name == "Controller 2":
            print("Starting controller 2")
            time.sleep(2)
            print("Finishing sleep")
        elif controller_name == "Controller 3":
            print("Starting controller 3")
            time.sleep(2)
            print("Finishing sleep")
        else:
            raise Exception("No such controller found!")
        self.finished.emit(0)


class Window(QWidget):
    """ Inherits from QWidget """
    def closeEvent(self, *args, **kwargs):
        print("\nClosing")

    def __init__(self):
        super().__init__()
        self.CONTINUOUS_MOVE_SWITCH = False
        self.title = 'Control Controllers'
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 100
        self.AxesMapping = [0, 1, 2, 3]
        self.initUI()
        self.thread = QThread()
        self.obj = Worker()
        self.obj.moveToThread(self.thread)
        self.thread.started.connect(self.obj.moveLeftIncrement)
        self.obj.finished.connect(self.thread.quit)

    def initUI(self):
        """ Initializes the GUI either using the grid layout or the absolute position layout"""
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        Comp1 = self.createGridLayout("Controller 2")
        windowLayout = QGridLayout()

        windowLayout.addWidget(Comp1, 0, 0)
        self.setLayout(windowLayout)
        self.show()

    def createGridLayout(self, controller):
        """Creates a grid layout for the buttons"""
        box_size = QSize(640, 440)
        HGroupBox = QGroupBox(controller)
        layout = QGridLayout()
        layout.addWidget(self.createButton("left", controller), 2, 1)
        layout.addWidget(self.createButton("right", controller), 2, 3)
        layout.addWidget(self.createButton("forward", controller), 1, 2)
        layout.addWidget(self.createButton("backward", controller), 3, 2)
        HGroupBox.setLayout(layout)
        HGroupBox.setFixedSize(box_size)
        return HGroupBox

    def createButton(self, name, controller):
        """Creates a button with the specified size"""
        button_size = QSize(100, 40)
        icon_size = 40
        button = QPushButton()
        button.Name = name
        button.Controller = controller
        button.Moving = 0
        button.clicked.connect(lambda: self.buttonPresssed(button))
        button.setFixedSize(button_size)
        return button

    def moveLeftIncrement(self, controller, button):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            raise Exception("No such controller found!")

    def moveRightIncrement(self, controller, button):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            raise Exception("No such controller found!")

    def moveForwardIncrement(self, controller, button):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            raise Exception("No such controller found!")

    def moveBackwardIncrement(self, controller, button):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            raise Exception("No such controller found!")

    def buttonPresssed(self, button):
        name = button.Name
        if hasattr(button, 'Controller'):
            controller = button.Controller
            print("The controller selected is", controller)
        if name == 'left':
            self.thread.start()
        elif name == 'right':
            print("Moved controller right for a single step")
            self.moveRightIncrement(controller, button)
        elif name == 'forward':
            self.moveForwardIncrement(controller, button)
            print("Moved controller forward for a single step")
        elif name == 'backward':
            self.moveBackwardIncrement(controller, button)
            print("Moved controller backward for a single step")
        elif name == "up":
            print("Moving controller up for a single step")
            self.moveUpIncrement(controller, button)
        elif name == "down":
            print("Moving controller down for a single step")
            self.moveDownIncrement(controller, button)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Window()
    sys.exit(app.exec_())

- How can I pass the parameter at runtime and/or use slots? - 如何在运行时传递参数和/或使用插槽?

If you want to invoke a slot of an object that is in another thread you must use signals since it is thread-safe. 如果要调用另一个线程中的对象的插槽,则必须使用信号,因为它是线程安全的。

- How can I prevent program errors upon exit? - 如何在退出时防止程序错误?

In your case the error is caused because you have a thread running and you have not stopped it, a possible option is to use the closeEvent to stop it. 在您的情况下,错误是由于您有一个线程正在运行并且您没有停止它,可能的选择是使用closeEvent来停止它。

- Why does subclassing QThread directly works in this case although it is not recommended? - 为什么子类化QThread直接适用于这种情况,尽管不建议这样做?

Not that it is not recommended, but it is very limited, there are better options like using a worker that lives in another thread so we can have different methods and not just do a task in the run method. 并不是不推荐它,但它非常有限,有更好的选择,比如使用一个生活在另一个线程中的worker,所以我们可以有不同的方法而不只是在run方法中执行任务。 Also with the option of the worker you can have several objects living in a thread. 此外,通过工作人员的选项,您可以在线程中生成多个对象。


In the case of the worker, the methodology is as follows: 就工人而言,方法如下:

  • Create a QThread and start the thread that handles. 创建一个QThread并启动处理的线程。
  • Create an object that will live in the other thread and move the other thread 创建一个将存在于另一个线程中的对象并移动另一个线程
  • Connect the signals that invoke the slots of the object, and also connect the signals that will send the information of the object to the GUI. 连接调用对象插槽的信号,并连接将对象信息发送到GUI的信号。
  • Emit the signals they will invoke when necessary. 发出必要时会调用的信号。

Considering the above, the solution is: 考虑到上述情况,解决方案是:

from PySide2 import QtCore, QtGui, QtWidgets
import time

class Worker(QtCore.QObject):
    error = QtCore.Signal()

    @QtCore.Slot(str)
    def moveLeftIncrement(self, controller):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            self.error.emit("No such controller found!")

    @QtCore.Slot(str)
    def moveRightIncrement(self, controller):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            self.error.emit("No such controller found!")

    @QtCore.Slot(str)
    def moveForwardIncrement(self, controller, button):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            self.error.emit("No such controller found!")

    @QtCore.Slot(str)
    def moveBackwardIncrement(self, controller, button):
        if controller == "Controller 1":
            time.sleep(2)
        elif controller == "Controller 2":
            time.sleep(2)
        elif controller == "Controller 3":
            time.sleep(2)
        else:
            self.error.emit("No such controller found!")

class Window(QtWidgets.QWidget):
    leftClicked = QtCore.Signal(str)
    rightClicked = QtCore.Signal(str)
    forwardClicked = QtCore.Signal(str)
    backwardClicked = QtCore.Signal(str)

    def __init__(self):
        super().__init__()
        self.CONTINUOUS_MOVE_SWITCH = False
        self.title = 'Control Controllers'
        self.left, self.top, self.width, self.height = 10, 10, 320, 100
        self.AxesMapping = [0, 1, 2, 3]

        self.initUI()

        self.thread = QtCore.QThread(self)
        self.thread.start()
        self.obj = Worker()
        self.obj.moveToThread(self.thread)
        self.leftClicked.connect(self.obj.moveLeftIncrement)
        self.rightClicked.connect(self.obj.moveRightIncrement)
        self.forwardClicked.connect(self.obj.moveForwardIncrement)
        self.backwardClicked.connect(self.obj.moveBackwardIncrement)
        self.obj.error.connect(self.on_error)

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        Comp1 = self.createGridLayout("Controller 2")
        windowLayout = QtWidgets.QGridLayout(self)
        windowLayout.addWidget(Comp1, 0, 0)

    def createGridLayout(self, controller):
        """Creates a grid layout for the buttons"""
        box_size = QtCore.QSize(640, 440)
        HGroupBox = QtWidgets.QGroupBox(controller)
        layout = QtWidgets.QGridLayout()
        layout.addWidget(self.createButton("left", controller), 2, 1)
        layout.addWidget(self.createButton("right", controller), 2, 3)
        layout.addWidget(self.createButton("forward", controller), 1, 2)
        layout.addWidget(self.createButton("backward", controller), 3, 2)
        HGroupBox.setLayout(layout)
        HGroupBox.setFixedSize(box_size)
        return HGroupBox

    def createButton(self, name, controller):
        button_size = QtCore.QSize(100, 40)
        icon_size = 40
        button = QtWidgets.QPushButton()
        button.Name = name
        button.Controller = controller
        button.Moving = 0
        button.clicked.connect(self.buttonPresssed)
        button.setFixedSize(button_size)
        return button

    @QtCore.Slot()
    def buttonPresssed(self):
        button = self.sender()
        name = button.Name
        if hasattr(button, 'Controller'):
            controller = button.Controller
            print("The controller selected is", controller)
            if name == 'left':
                self.leftClicked.emit(controller)
            elif name == 'right':
                print("Moved controller right for a single step")
                self.rightClicked.emit(controller)
            elif name == 'forward':
                print("Moved controller forward for a single step")
                self.forwardClicked.emit(controller)
            elif name == 'backward':
                print("Moved controller backward for a single step")
                self.backwardClicked.emit(controller)

    @QtCore.Slot(str)
    def on_error(self, error):
        print(error)

    def closeEvent(self, event):
        self.thread.quit()
        self.thread.wait()
        super(Window, self).closeEvent(event)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ex = Window()
    ex.show()
    sys.exit(app.exec_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM