线程中的事件循环
QT实现线程的方式有很多中,如果是QThread,本身自带事件循环,能够收到信号
如果是std::thread,是没有事件循环的需要在内部增加事件循环
发现这个问题是因为我在std::thread中使用QProcess发现没有收到信号,但是如果加了process->waitForFinished(-1)就能收到。 因为waitfor中带了事件循环QEventloop,如果不用waitfor的方式,那么我们可以这么写
std::thread thread([=]{
QDir dir(QCoreApplication::applicationDirPath());
QString applicationPath = dir.path();
dir.cdUp();
QString PyScriptPath = dir.absolutePath() + "/python/3DSeg/AI3DNet";
QString modelPath = dir.absolutePath() + "/python/3DSeg/model";
QString pybinPath = dir.absolutePath() + "/python";
QString auto3D = pybinPath + "/Auto3D/scripts/automatic_mask_generator_itk3D.py";
QString port = "1520";
QString url = QString("http://localhost:%1").arg(port);
dir.cdUp();
dir.cdUp();
QString CTPyexePath = dir.absolutePath() + "/package/CTPython/python.exe";
QString PyexePath = dir.absolutePath() + "/package/Python311/python.exe";
QString Pymain = PyScriptPath + "/main.py";
QString FinalIni = modelPath + "/final.ini";
QString ModelFile = modelPath + "/demo.pth";
QString LicenseFile = modelPath + "/license";
QString LicenseDat = pybinPath + "/license.dat";
QString path_cache = QCoreApplication::applicationDirPath() + "/cachePowder";
QDir dir_cache;
if (!dir_cache.exists(path_cache)) {
if (dir_cache.mkpath(path_cache)) {
qDebug() << "文件夹创建成功:" << path_cache;
} else {
qDebug() << "文件夹创建失败:" << path_cache;
}
}
QString NpyFile = path_cache + "/image_npy.npy";
QString ImageFile = path_cache + "/data.nii";
saveToNII(ImageFile);
QString resultFile = path_cache + "/result.nii";
QStringList scripts;
scripts << auto3D
<< "--SERVER" << url
<< "--PORT" << port
<< "--BATCH_SIZE" << QString::number(ui->spinBox_thread->value())
<< "--iou_threshold" << QString::number(ui->doubleSpinBox_apd->value())
<< "--start_index" << QString::number(ui->spinBox_start->value())
<< "--end_index" << QString::number(ui->spinBox_end->value())
<< "--precision" << QString::number(ui->doubleSpinBox_preci->value())
<< "--low_range" << QString::number(ui->doubleSpinBox_low->value())
<< "--upper_range" << QString::number(ui->doubleSpinBox_up->value())
<< "--low_threshold" << QString::number(-1000)
<< "--upper_threshold" << QString::number(8585)
<< "--hole_fill" << (ui->checkBox_hole->isChecked() ? "1" : "0")
<< "--save_iter" << (ui->checkBox_save->isChecked() ? "1" : "0")
<< "--direction" << QString::number(ui->comboBox_dir->currentIndex())
<< "--python_exe_file" << PyexePath
<< "--python_file_path" << Pymain
<< "--model3D_path" << FinalIni
<< "--model_path" << ModelFile
<< "--lisence_path" << LicenseFile
<< "--python_lisence_path" << LicenseDat
<< "--npy_save_path" << NpyFile
<< "--image_path" << ImageFile
<< "--save_path" << resultFile;
QProcess* process = new QProcess;
QEventLoop loop;
QObject::connect(process, &QProcess::readyReadStandardOutput, [=]() {
qDebug() << "Python out: " << process->readAllStandardOutput();
});
QObject::connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [&](int exitCode, QProcess::ExitStatus exitStatus){
loop.quit();
qDebug() << "thread" << QThread::currentThread();
qDebug() << exitCode << exitStatus;
if(exitCode == 0) {
QMetaObject::invokeMethod(this, [&](){
emit sig_fin(true);
Toast::showTip(Toast::SuccessToast, "AI计算完成");
this->accept();
});
} else {
QMetaObject::invokeMethod(this, [&](){
emit sig_fin(false);
Toast::showTip(Toast::ErrorToast, "AI计算失败");
this->accept();
});
}
QMetaObject::invokeMethod(this, [&](){
m_wait->accept();
});
});
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("PYTHONPATH", pybinPath + "/Auto3D");
process->setProcessEnvironment(env);
process->setProcessChannelMode(QProcess::MergedChannels);
qDebug() << "process work dir:" << process->workingDirectory();
qDebug() << "ctpy:" << CTPyexePath << scripts;
process->start(CTPyexePath, scripts);
// process->waitForFinished(-1);
loop.exec();
});
下面看看chpt的分析
为什么 waitForFinished 时 readyRead 也能输出?
这个现象最迷惑人。
原因是:
waitForFinished 内部会偷偷处理事件
Qt 源码里 waitForFinished 会:
阻塞等待一小段时间
中间调用 processEvents()
让 readyRead 有机会触发
所以你看到:
waitForFinished(-1) 期间 readyRead 也能收到输出
但这只是 Qt “顺便帮你跑了一点事件循环”。
⚠️所以 waitForFinished 本质是:
阻塞等待 + 临时 pump 一下事件
补充说明一下,QProcess本身并不需要放到线程中,本身是异步的,但是我为了一个等待计算的进度条而这么做的。也可以如下
m_wait = new WaitDialog("AI计算中...", this);
m_wait->setWindowModality(Qt::ApplicationModal);
m_wait->show();
QProcess* process = new QProcess(this);
process->setProcessChannelMode(QProcess::MergedChannels);
connect(process, &QProcess::readyReadStandardOutput, this, [=](){
qDebug() << process->readAllStandardOutput();
});
connect(process,
static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished),
this,
[=](int exitCode, QProcess::ExitStatus)
{
m_wait->close(); // ✅关闭等待框
if(exitCode == 0)
Toast::showTip(Toast::SuccessToast, "AI计算完成");
else
Toast::showTip(Toast::ErrorToast, "AI计算失败");
process->deleteLater();
});
process->start(CTPyexePath, scripts);