关于如何实例化和使用
QThread
可以在这里找到:
http://doc.qt.io/qt-5/qthread.html
文档描述了两种基本方法:(1)工作对象方法和(2)
Q螺纹
子类方法。
我在几篇文章中读到了第二种方法不好,所以让我们集中讨论第一种方法。
编辑:
@Ekhomigo向我指出了以下有趣的文章:
https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
. 显然,两种方法(1)和(2)都有各自的优点:
根据经验:
如果您在线程中不真正需要一个事件循环,那么您应该子类化。
如果您需要一个事件循环并处理线程中的信号和槽,那么您可能不需要子类。
因为我确实需要在Qapplication线程和新的qthread之间进行某种通信(我相信信号槽是一种很好的通信方式),所以我将使用
工作对象方法
.
1。C++中的WorkObjor方法
我已经复制粘贴了C++代码了
工作对象方法
(摘自官方qt5文件,见
网址:http://doc.qt.io/qt-5/qthread.html
):
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
2。python中的worker对象方法
我努力把给定的C++代码翻译成Python。如果安装了python 3.6和pyqt5,只需复制粘贴代码并在计算机上运行它。它应该能工作。
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Worker(QObject):
resultReady = pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@pyqtSlot(str)
def doWork(self, param):
result = "hello world"
print("foo bar")
# ...here is the expensive or blocking operation... #
self.resultReady.emit(result)
class Controller(QObject):
operate = pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 1. Create 'workerThread' and 'worker' objects
# ----------------------------------------------
self.workerThread = QThread()
self.worker = Worker() # <- SEE NOTE(1)
self.worker.moveToThread(self.workerThread)
# 2. Connect all relevant signals
# --------------------------------
self.workerThread.finished.connect(self.worker.deleteLater)
self.workerThread.finished.connect(lambda: print("workerThread finished.")) # <- SEE NOTE(2)
self.operate.connect(self.worker.doWork)
self.worker.resultReady.connect(self.handleResults)
# 3. Start the thread
# --------------------
self.workerThread.start()
def __del__(self):
self.workerThread.quit()
self.workerThread.wait()
@pyqtSlot(str)
def handleResults(self, param):
print(param)
# One way to end application
# ---------------------------
# global app # <- SEE
# app.exit() # NOTE(3)
# Another way to end application
# -------------------------------
self.workerThread.quit() # <- SEE NOTE(4)
self.thread().quit()
if __name__ == '__main__':
app = QCoreApplication([])
controller = Controller()
controller.operate.emit("foo") # <- SEE NOTE(5)
sys.exit(app.exec_())
注(1):
最初我执行了
worker
变量作为构造函数中的局部变量。我把C++样本逐字翻译成Python,这个变量也是C++样本中的一个局部变量。
正如您在@pschill的注释中看到的,这个局部变量被垃圾收集,因此我无法让线程运行。在做了更改之后,我得到了预期的输出。
注(2):
我添加了这一行以准确知道
workerThread
完成。
注(3):
显然我需要添加这两个代码行
global app
和
app.exit()
到
handleResults(..)
狭槽。感谢@matic指出这一点!
注(4):
我发现(通过一些文档)这种方法可以结束应用程序。第一行代码结束
工作线程
(通过终止事件循环)。第二条代码行结束
mainThread
(也通过终止事件循环)。
注(5):
在Windows控制台中运行代码时,没有发生任何事情(它只是挂起)。根据@pschill的建议(见下面他的评论),我添加了这个代码行以确保
doWork()
函数被调用。
三。我的问题
-
首先,我想知道我是否从C++到Python的翻译是正确的。请告诉我哪里出错了(如果你发现的话)。
-
添加代码行
全球应用程序
和
应用程序退出()
到
把手结果(..)
插槽解决了悬挂问题。但是在背景上到底发生了什么呢?这些代码行会杀死工作线程吗?还是主Qapplication线程?
-
有没有一种方法可以在不杀死主QApplication线程的情况下杀死工作线程?
4。一些答案
1。仍然不确定……
2。我相信
应用程序退出()
杀死主线程,然后杀死工作线程,因为它属于
类型。我发现工作线程是
迪蒙
键入,因为我插入了代码行
print(threading.current_thread())
在
doWork(..)
功能。它是印刷的
<_DummyThread(Dummy-1, started daemon 9812)>
。当程序退出时,任何守护进程线程都会自动终止。
三。是的,我找到了办法!这个
QThread::quit()
功能是你的朋友。官方文件说:
无效
qthread::退出()
告诉线程的事件循环退出,返回代码为0(成功)。相当于呼叫
QThread::exit(0)
.
如果线程没有事件循环,则此函数不执行任何操作。
[
http://doc.qt.io/qt-5/qthread.html#quit]
所以我的功能
把手结果(..)
现在看起来像这样:
@pyqtSlot(str)
def handleResults(self, param):
print(param)
self.workerThread.quit() # Kill the worker thread
self.thread().quit() # Kill the main thread
我已经通过在
Controller(..)
:
self.workerThread.finished.connect(lambda: print("workerThread finished."))
我确实按预期把这行打印出来了。我也尝试过用类似的方法检查主线程的终止:
self.thread().finished.connect(lambda: print("mainThread finished."))
很遗憾,这行没有打印出来。为什么?
在此,我提供我当前的系统设置:
>第5季度(
QT_VERSION_STR
=5.10.1)
>Pyqt5(
PYQT_VERSION_STR
=5.10.1)
>Windows 10,64位