当我的按钮被点击时,我正在尝试进行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++文档,以更好地了解正在发生的事情:
- http://doc.qt.io/qt-5/qobject.html
- http://doc.qt.io/qt-5/qthread.html
- https://wiki.qt.io/QThreads_general_usage
通常情况下,您会遇到两个问题(其中一个值得列出两次,从不同角度进行检查):
- 您的
QThread
由创建它以发出事件的线程拥有/关联。因此,所有试图发出事件的代码实际上都是试图访问主线程(UI)的事件循环。阅读QObject
的thread()
和moveToThread()
方法可以更全面地理解这一点 - 您覆盖了
QThread
的run()
方法,该方法是在QThread
表示的实际线程上调用的方法(即,一旦您在线程上调用start()
,最终会调用的代码)。因此,您正在有效地尝试以不安全的方式从不同的线程访问主事件循环 - 您覆盖了
QThread
的run()
方法。QThread
的默认实现设置一个QEventLoop
,运行它,这使QObject
的关联/拥有/由该线程拥有的线程能够自由使用信号/插槽。您可能并不打算丢失默认实现
基本上你想做的是:
- 不要对
QThread
进行子类化。相反,编写一个带有适当事件的自定义QObject
工作子类 - 构造一个带有事件循环的
QThread
(默认的IIRC,但请检查文档) - 构造自定义辅助对象的实例
- 使用
moveToThread()
将worker对象移动到新创建的QThread
- 连接信号和插槽,确保使用QueuedConnection类型(默认为DirectConnection,不适合跨线程边界通信)
- 启动您的线程,该线程应该只运行自己的
QEventLoop
- 发出信号以启动工作对象逻辑
- 通过主线程(eventloop)上的信号接收结果通知,在那里可以安全地更新UI
- 弄清楚您希望如何终止
QThread
并清理相关资源。通常,您会启动一个连接到destroyLater()
插槽的自定义事件,以清除您的工作对象。您可能希望缓存/重用QThread
以用于后续的按钮单击,或者您也可能希望在清理工作对象后将其拆下