在PyQt5中点击按钮启动Qthread



当我的按钮被点击时,我正在尝试进行API调用,而在调用期间不暂停GUI。我使用线程将Qable的文本设置为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")

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

编辑:

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

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

它现在起作用了。

您可能应该阅读Qt C++文档,以更好地了解正在发生的事情:

  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

通常情况下,您会遇到两个问题(其中一个值得列出两次,从不同角度进行检查):

  1. 您的QThread由创建它以发出事件的线程拥有/关联。因此,所有试图发出事件的代码实际上都是试图访问主线程(UI)的事件循环。阅读QObjectthread()moveToThread()方法可以更全面地理解这一点
  2. 您覆盖了QThreadrun()方法,该方法是在QThread表示的实际线程上调用的方法(即,一旦您在线程上调用start(),最终会调用的代码)。因此,您正在有效地尝试以不安全的方式从不同的线程访问主事件循环
  3. 您覆盖了QThreadrun()方法。QThread的默认实现设置一个QEventLoop,运行它,这使QObject的关联/拥有/由该线程拥有的线程能够自由使用信号/插槽。您可能并不打算丢失默认实现

基本上你想做的是:

  1. 不要对QThread进行子类化。相反,编写一个带有适当事件的自定义QObject工作子类
  2. 构造一个带有事件循环的QThread(默认的IIRC,但请检查文档)
  3. 构造自定义辅助对象的实例
  4. 使用moveToThread()将worker对象移动到新创建的QThread
  5. 连接信号和插槽,确保使用QueuedConnection类型(默认为DirectConnection,不适合跨线程边界通信)
  6. 启动您的线程,该线程应该只运行自己的QEventLoop
  7. 发出信号以启动工作对象逻辑
  8. 通过主线程(eventloop)上的信号接收结果通知,在那里可以安全地更新UI
  9. 弄清楚您希望如何终止QThread并清理相关资源。通常,您会启动一个连接到destroyLater()插槽的自定义事件,以清除您的工作对象。您可能希望缓存/重用QThread以用于后续的按钮单击,或者您也可能希望在清理工作对象后将其拆下

最新更新