防止模式对话框中的事件循环阻塞

Prevent event loop blocking in modal dialog

本文关键字:事件 循环 模式 对话框      更新时间:2023-10-16

我正在开发一个应用程序,用户可以在其中解析一些二进制文件。一旦他点击"解析"按钮,他首先可以选择一些文件,然后进行解析。当应用程序处理文件时,我想显示一个模式对话框,通知用户进度(QProgressBar 栏)和已解析的文件(QListView list/listModel)。

我目前的方法是覆盖QDialog-sublcass的exec()方法。这样我就可以打电话

parseAssistant.exec()

当前实现如下所示:

class ParseAssistant : public QDialog { public: int exec(); };
int ParseAssistant::exec()
{
bar->setMaximum(files.size());
this->show();
this->setModal(true);
for (int i = 0; i < files.size(); i++) {
PluginTable* table = parser.parse(files[i]);
// do something with the table
// saveTableintoDB();
// update GUI
// bar->setValue(i);
// listModel->insertRow(0, new QStandardItem(files[i]));
}
this->hide();
return QDialog::Accepted;
}

运行此(阻塞)方法后,用户已解析所有文件或在某处取消进度。为了实现这一点,我尝试在while循环中使用QApplication::p rocessEvents(感觉有点滞后,因为它只有在文件完成解析时才进行)或将繁重的计算外包给一些QConcurrent实现(::run,::mapped)。不幸的是,我不知道如何在 QFuture 完成后将程序流返回到 exec() 方法,而不依赖于一些 CPU 密集型循环,例如:

while (!future.isFinished()) { QApplication::processEvents(); }

是否有更智能的方法来运行繁重的计算(用户可能会取消)而不会阻塞事件循环?

我首先不会Qdialog子类,而只是使用QFutureWatcher并将观察器finished信号连接到对话框close插槽,这样:

QDialog d;
QFutureWatcher<void> watcher;
QObject::connect(&watcher, &QFutureWatcher<void>::finished, &d, &QDialog::close);
QFuture<void> future = QtConcurrent::run(your_parse_function);
watcher.setFuture(future);
d.exec();
//control returns here when your_parse_function exits

parse 函数可以是 QObject 派生类中的一个方法,如下所示:

class Parser : public QObject
{
Q_OBJECT
public:
void parse()
{
for (int i = 0; i < files.size(); i++) {
PluginTable* table = parser.parse(files[i]);
emit fileParsed(i, files.size);
// ...
}
}    
signals:
void fileParsed(int id, int count);
};

您可以将fileParsed信号连接到所选插槽,然后从那里相应地设置进度条值。

我个人的方法是:

  • 创建一个单独的线程并在那里进行处理(QThread; std::thread也应该可以解决问题)
  • 提供通知当前正在处理的文件的信号
  • 可能是另一个信号,告知 % 的进展
  • 另一个信号通知处理已完成,在线程结束之前发出
  • 为您的对话提供适当的插槽并将它们连接到信号(因为涉及不同的线程,请确保连接类型Qt::QueuedConnection)