【问题标题】:Prevent event loop blocking in modal dialog防止模式对话框中的事件循环阻塞
【发布时间】:2018-03-15 15:14:51
【问题描述】:

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

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

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::processEvents (感觉有点滞后,因为它仅在文件完成解析时才进行)或将繁重的计算外包给一些 QConcurrent 实现(::run, ::映射)。不幸的是,我不知道如何在 QFuture 完成后将程序流返回到 exec() 方法,而不依赖于一些 CPU 密集型循环,例如:

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

有没有更聪明的方法来让模态对话框运行繁重的计算(可能被用户取消)而不阻塞事件循环?

【问题讨论】:

    标签: c++ qt qdialog qtconcurrent


    【解决方案1】:

    首先我不会继承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
    

    解析函数可以是 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 信号连接到选择的插槽,并从那里相应地设置进度条值。

    【讨论】:

    • 如何将函数 Parser::parse 传递给 QtConcurrent::run,同时仍然拥有文件和解析器(用于 parser.parse)?
    • 拥有Parser parser 对象:QtConcurrent::run(parser, &amp;Parser::parse);
    【解决方案2】:

    我个人的做法是:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-22
      • 1970-01-01
      • 1970-01-01
      • 2014-01-24
      • 2013-08-16
      • 1970-01-01
      相关资源
      最近更新 更多