在按钮上启动Qthread在PyQt5中单击

时间:2019-01-09 21:21:49

标签: python multithreading pyqt5

我当前正在尝试单击按钮时进行API调用,而在调用过程中不暂停我的GUI。我使用线程将Qlable的文本设置为API调用的响应。这行得通,但是由于我要从单独的线程访问GUI元素,因此这是不安全的。

当前,我正在尝试使用QThreads进行API调用,然后发出对GUI线程的响应,但是,当我创建Qthread对象时,我的程序以退出代码3结尾。清晰。

class MainWindow(QtWidgets.QWidget):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("TestWindow")
        self.setFixedSize(300,75)

        self.main_layout = QtWidgets.QGridLayout()
        self.setLayout(self.main_layout)
        self.txt_0 = QtWidgets.QLabel()
        self.btn_0 = QtWidgets.QPushButton('Press Me')
        self.btn_0.clicked.connect(self.btn_0_clicked)

        self.main_layout.addWidget(self.txt_0, 0, 0)
        self.main_layout.addWidget(self.btn_0, 1, 0)

        self.show()

    def btn_0_clicked(self):
        temp_thread = StringThread("name")
        temp_thread.start()


class StringThread(QtCore.QThread):

    str_signal = QtCore.pyqtSignal(str)
    _name = ''

    def __init__(self, name):
        QtCore.QThread.__init__(self)
        self._name = name
        print("Thread Created")

    def run(self):
        self.str_signal.emit('Emitted message from StringThread. Name = ' + self._name)
        print("Done run")

我的目的是将Qlable的文本设置为StringThread类中pyqtSignal发出的消息,但是,一旦我单击按钮,程序就会以退出代码3结束。

编辑:

我对btn_0_clicked方法进行了以下更改

def btn_0_clicked(self):
        self.temp_thread = StringThread("hello")
        self.temp_thread.str_signal.connect(self.txt_0.setText)
        self.temp_thread.start()

现在正在工作。

1 个答案:

答案 0 :(得分:0)

You should probably read up on the Qt C++ documentation to better understand what is happening:

  1. http://doc.qt.io/qt-5/qobject.html
  2. http://doc.qt.io/qt-5/qthread.html
  3. https://wiki.qt.io/QThreads_general_usage

In general you have two problems (one of which is worth listing twice, to examine it from different angles):

  1. Your QThread is owned/associated by/with the thread that created it for the purpose of emitting events. So all code trying to emit events is actually trying to access the event loop of your main thread (UI). Read up on QObject's thread() and moveToThread() methods to understand this more fully.
  2. You overrode the run() method of QThread which is what is invoked on the actual thread represented by QThread (i.e. the code that winds up getting called eventually once you call start() on the thread). So you are effectively trying to access the main event loop in an unsafe manner from a different thread.
  3. You overrode the run() method of QThread. The default implementation of QThread sets up a QEventLoop, runs it and that is what gives QObject's associated/owned with/by that thread the ability to use signal/slots freely. You probably did not intend to lose that default implementation.

Basically what you want to do instead is this:

  1. Do not subclass QThread. Instead write a custom QObject worker subclass with appropriate events
  2. Construct a QThread with an eventloop (the default IIRC, but check the docs)
  3. Construct an instance of your custom worker object.
  4. Move your worker object to the newly created QThread with moveToThread().
  5. Wire up the signals and slots, make sure you are using the QueuedConnection type (the default is DirectConnection which is not suitable for communicating across thread boundaries).
  6. Start your thread which should just run its own QEventLoop.
  7. Emit the signal to kickstart your worker object logic.
  8. Receive notification of the results via signals on your main thread (eventloop), where it is safe to update the UI.
  9. Figure out how you want to terminate the QThread and clean up associated resources. Typically you would fire a custom event wired up to the destroyLater() slot to get rid of your worker object. You may want to cache/reuse the QThread for subsequent button clicks or you may want to tear it down as well once your worker object has been cleaned up.