简体   繁体   English

从工作线程更新模态QProgressDialog

[英]Modal QProgressDialog updated from worker thread

I want to update a modal QProgressDialog from my worker thread. 我想从我的工作线程更新模态 QProgressDialog。 However, if I set the dialog to be modal, my application crashes (and the dialog did not show any progress). 但是,如果将对话框设置为模式对话框,则我的应用程序将崩溃(对话框未显示任何进度)。 If I do not, everything goes fine (but the user can tinker around with the rest of the program, which may cause issues). 如果我不这样做,一切都会很好(但是用户可以修改程序的其余部分,这可能会导致问题)。

What am I doing wrong? 我究竟做错了什么?

Minimum code sample follows: 最小代码示例如下:

filereader qfr;    

QProgressDialog progress("Importing file.", "Cancel", 0, file_size);

connect(&qfr, &filereader::signalProgress, &progress, &QProgressDialog::setValue, Qt::QueuedConnection);

QThread worker_thread;

std::atomic<bool> success = false;

connect(&worker_thread, &QThread::started,
        [&]() {
    success = qfr.read_file(/* parameters */);
    worker_thread.quit();});

worker_thread.start();

//progress.setWindowModality(Qt::WindowModal); // Works only fine when this line is commented !!

while (worker_thread.isRunning()) {
    QApplication::processEvents();
    QThread::sleep(0);
}

progress.close();

Your thread is pretty much pointless. 您的线程几乎毫无意义。 It serves no real purpose. 它没有真正的目的。 You could have as well just called QApplication::processEvents in your read_file method. 您也可以在read_file方法中调用QApplication::processEvents But you shouldn't, calling processEvents is bad practice. 但是您不应该,调用processEvents是不好的做法。

What you should do is remove that while loop, and make your progress dialog a member of your class. 您应该做的是删除while循环,并使进度对话框成为您班级的成员。 I don't really like how that lambda looks either. 我也不很喜欢那个lambda的样子。 I would personally just use filereader::read_file as a slot. 我个人只是将filereader::read_file用作插槽。

Note that Qt::windowModal blocks input to the parent window. 注意, Qt::windowModal阻止输入到父窗口。 Your progress dialog has no parent. 您的进度对话框没有父级。 So you would either have to call progress->setModal(true) , or progress.setWindowModality(Qt::ApplicationModal); 因此,您必须调用progress->setModal(true)progress.setWindowModality(Qt::ApplicationModal); progress->setModal(true) . Or set a parent to it. 或为其设置父项。

Here is a small example (it is not tailor made for your application, but it should point you in the right direction): 这是一个小示例(它不是为您的应用程序量身定制的,但应为您指明正确的方向):

#include <QtWidgets>

class Worker : public QObject
{
    Q_OBJECT
public:
    Worker(QObject *parent = nullptr) : QObject(parent){}
public slots:
    void simulateLongProcess()
    {
        for(int i = 0; i < 101; i++)
        {
            emit progressChanged(i);
            QThread::msleep(100);
        }
        emit finishedWorking(true);
    }
signals:
    void progressChanged(int progress);
    void finishedWorking(bool result);
};

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr) : QWidget(parent)
    {
        setLayout(new QHBoxLayout);
        progress_dialog.setModal(true);
        progress_dialog.setAutoReset(false);
        progress_dialog.setCancelButton(nullptr);
        QThread *thread = new QThread(this);
        connect(thread, &QThread::started, &worker, &Worker::simulateLongProcess);
        connect(&worker, &Worker::finishedWorking, thread, &QThread::quit);
        connect(&worker, &Worker::progressChanged, &progress_dialog, &QProgressDialog::setValue);
        connect(&worker, &Worker::finishedWorking, &progress_dialog, &QProgressDialog::close);
        connect(&worker, &Worker::finishedWorking, this, &Widget::handleResult);
        QPushButton * start_button = new QPushButton("START");
        connect(start_button, &QPushButton::clicked, this, [=]
        {
            progress_dialog.show();
            thread->start();
        });
        layout()->addWidget(start_button);
        resize(400, 300);
    }

public slots:
    void handleResult(bool result)
    {
        // do something with the result
    }

private:
    QProgressDialog progress_dialog;
    Worker worker;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

#include "main.moc"

First of all, if you want the progress to be Modal, why do you need to perform the reading of the file in a background thread ? 首先,如果您希望进度为Modal,为什么需要在后台线程中执行文件读取? You can perform your file reading operation in the main (GUI) thread, as long as from time to time you check the cancellation status. 您可以在主(GUI)线程中执行文件读取操作,只要您不时检查取消状态即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM