【问题标题】:How to implement QProgressDialog?如何实现 QProgressDialog?
【发布时间】:2018-02-12 10:30:10
【问题描述】:

我尝试使用QProgressDialog 向用户提供一些有关长任务进度的信息,同时允许他取消此任务。

基本上,我有一个QDialog 和一个按钮Compute。通过单击它,会在我的QDialog 的父级成员上调用一个耗时的方法。该方法通过回调告诉调用者工作进度。

问题是进度对话框在出现之前需要一些时间,并且不会立即考虑点击其Cancel 按钮。

很明显我的代码有问题,但是我不习惯Qt,我尝试了很多东西。我可能需要一个单独的线程。

我的代码摘录:

void MyDialog::callbackFunction(int value, void * objPtr) {
  ((QProgressDialog *)(objPtr))->setValue(value);
  QCoreApplication::processEvents();
}

void MyDialog::on_mComputeBtn_clicked()
{
   Compute();
}

void MyDialog::Compute()
{
  QProgressDialog progressDialog("Optimization in progress...", "Cancel", 0, 100, this);
  progressDialog.setMinimumDuration(500); // 500 ms
  progressDialog.setWindowModality(Qt::WindowModal);
  progressDialog.setValue(0);
  connect(&progressDialog, SIGNAL(canceled()), this, SLOT(Cancel()));
  QCoreApplication::processEvents();

    parentMember->LongComputation(callbackFunction);

    // probably useless
  progressDialog.reset();
  progressDialog.hide();
  QCoreApplication::processEvents();
}

【问题讨论】:

    标签: c++ qt qthread qprogressdialog


    【解决方案1】:

    对话框没有立即出现,因为您将最短持续时间设置为 500 毫秒。将其设置为 0 可使对话框在进度更改时立即显示或手动调用其显示函数。

    为了使取消按钮起作用,请将您的长计算移至另一个线程(例如,使用 QThread 或 std::async )并让您的主事件循环继续执行。

    实际上,您的代码存在很多问题,但这两点应该为您指明正确的方向。根据我的经验,每次手动调用 processEvents 都会产生很大的代码异味。

    【讨论】:

    • “根据我的经验,每次手动调用 processEvents 都会产生很大的代码异味”——我非常同意。致电processEvents() 是您做错了什么的明确信号。
    • 是的,我觉得手动调用processEvents 代码不好,但主要是为了让它工作。至少,show() 在视觉上解决了一部分问题。我将重构这整段代码。
    【解决方案2】:

    您所做的是尝试使用 QProgressdialog 的模态范例,不让主事件泵触发,还设置 0.5 秒超时,最短持续时间将确保暂停。也许无模式变体更适合您的情况。

    如果进程有离散的步骤,你可以在没有单独的线程的情况下完成

    class MyTask : public QObject
    {
        Q_OBJECT
    public:
        explicit MyTask(QObject *parent = 0);
    
    signals:
    
    public slots:
        void perform();
        void cancel();
    private:
        int steps;
        QProgressDialog *pd;
        QTimer *t;
    
    };
    
    ...
    
    void MyDialog::on_mComputeBtn_clicked()
    {
        myTask = new MyTask;
    }
    ...
    
    
    MyTask::MyTask(QObject *parent) :
        QObject(parent), steps(0)
    {
        pd = new QProgressDialog("Task in progress.", "Cancel", 0, 100000);
        connect(pd, SIGNAL(canceled()), this, SLOT(cancel()));
        t = new QTimer(this);
        connect(t, SIGNAL(timeout()), this, SLOT(perform()));
        t->start(0);
    }
    
    void MyTask::perform()
    {
        pd->setValue(steps);
        //... perform one percent of the operation
        steps++;
        if (steps > pd->maximum())
            t->stop();
    }
    
    void MyTask::cancel()
    {
        t->stop();
        //... cleanup
    }
    

    这里有 QThread 示例:Qt 5 : update QProgressBar during QThread work via signal

    【讨论】:

    • 谢谢。不幸的是,主要工作是在一个单独的类中执行的,我无法将其作为MyTask::perform() 方法导入。我可以通过回调来调用它。所以我想我不能没有 Qthread...
    • @Greg82 为什么,从 QThread 执行,在该线程中使用回调通过信号更新进度?顺便说一句,它如何能够接受您的方法作为回调?它是否执行单独线程中不允许的操作?
    • 不,不是,因为我说我不能没有;)
    猜你喜欢
    • 2021-03-05
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2012-07-18
    • 2018-05-19
    • 1970-01-01
    相关资源
    最近更新 更多