代码之家  ›  专栏  ›  技术社区  ›  Pat. ANDRIA

如何使用QProgressDialog的“取消”按钮停止/取消辅助工单

  •  0
  • Pat. ANDRIA  · 技术社区  · 4 年前

    我的代码由一个worker类和一个dialog类组成。 工人阶级开始一项工作(一项很长的工作)。 我想实施一个繁忙的酒吧,表明工作正在进行中。 cancel 巴顿,我听不到信号 &QProgressDialog::canceled 我试过了,这个(放入Worker构造函数):

    QObject::connect(progress, &QProgressDialog::canceled, this, &Worker::stopWork);
    

    没有任何影响。

    如何通过单击QprogressDialog取消按钮来停止作业?

    //工人.h

    #ifndef WORKER_H
    #define WORKER_H
    #include <QObject>
    #include <QProgressDialog>
    class Worker : public QObject
    {
        Q_OBJECT
    public:
        explicit Worker(QObject *parent = nullptr);
        virtual ~Worker();
        QProgressDialog * getProgress() const;
        void setProgress(QProgressDialog *value);
    signals:
        void sigAnnuler(bool);
        // pour dire que le travail est fini
        void sigFinished();
        // mise à jour du progression bar
        void sigChangeValue(int);
    public slots:
        void doWork();
        void stopWork();
    private:
        bool workStopped = false;
        QProgressDialog* progress = nullptr;
    };
    #endif // WORKER_H
    

    #include "worker.h"
    #include <QtConcurrent>
    #include <QThread>
    #include <functional>
    // Worker.cpp
    Worker::Worker(QObject* parent/*=nullptr*/)
    {
        //progress = new QProgressDialog("Test", "Test", 0, 0);
        QProgressDialog* progress = new QProgressDialog("do Work", "Annuler", 0, 0);
        progress->setMinimumDuration(0);
        QObject::connect(this, &Worker::sigChangeValue, progress, &QProgressDialog::setValue);
        QObject::connect(this, &Worker::sigFinished, progress, &QProgressDialog::close);
        QObject::connect(this, &Worker::sigAnnuler, progress, &QProgressDialog::cancel);
        QObject::connect(progress, &QProgressDialog::canceled, this, &Worker::stopWork);
    }
    Worker::~Worker()
    {
        //delete timer;
        delete progress;
    }
    void Worker::doWork()
    {
        emit sigChangeValue(0);
    
        for (int i=0; i< 100; i++)
        {
    
           qDebug()<<"work " << i;
           emit sigChangeValue(0);
           QThread::msleep(100);
    
           if (workStopped)
           {
               qDebug()<< "Cancel work";
               break;
           }
           
        }
        emit sigFinished();
    }
    void Worker::stopWork()
    {
        workStopped = true;
    }
    QProgressDialog *Worker::getProgress() const
    {
        return progress;
    }
    void Worker::setProgress(QProgressDialog *value)
    {
        progress = value;
    }
    

    //我的对话.h

    #ifndef MYDIALOG_H
    #define MYDIALOG_H
    
    #include <QDialog>
    #include "worker.h"
    
    namespace Ui {
    class MyDialog;
    }
    
    class MyDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        explicit MyDialog(QWidget *parent = 0);
        ~MyDialog();
        void triggerWork();
        void StopWork();
    private:
        Ui::MyDialog *ui;
        QThread* m_ThreadWorker = nullptr;
        Worker* m_TraitementProdCartoWrkr = nullptr;
    };
    
    #endif // MYDIALOG_H
    
    #include "mydialog.h"
    #include "ui_mydialog.h"
    #include <QProgressDialog>
    #include <QThread>
    
    MyDialog::MyDialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::MyDialog)
    {
        ui->setupUi(this);
        m_TraitementProdCartoWrkr = new Worker(this);
        connect(ui->OK, &QPushButton::clicked, this, &MyDialog::triggerWork);
        connect(ui->Cancel, &QPushButton::clicked, this, &MyDialog::StopWork);
    }
    MyDialog::~MyDialog()
    {
        delete ui;
    }
    void MyDialog::triggerWork()
    {
        m_ThreadWorker = new QThread;
        QProgressDialog* progress = m_TraitementProdCartoWrkr->getProgress();
        m_TraitementProdCartoWrkr->moveToThread(m_ThreadWorker);
        QObject::connect(m_ThreadWorker, &QThread::started, m_TraitementProdCartoWrkr, &Worker::doWork);
        m_ThreadWorker->start();
    }
    
    void MyDialog::StopWork()
    {
        m_TraitementProdCartoWrkr->stopWork();
    }
    

    //主.cpp

    #include "mydialog.h"
    #include "ui_mydialog.h"
    #include <QProgressDialog>
    #include <QThread>
    
    MyDialog::MyDialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::MyDialog)
    {
        ui->setupUi(this);
        m_TraitementProdCartoWrkr = new Worker(this);
        connect(ui->OK, &QPushButton::clicked, this, &MyDialog::triggerWork);
        connect(ui->Cancel, &QPushButton::clicked, this, &MyDialog::StopWork);
    }
    
    MyDialog::~MyDialog()
    {
        delete ui;
    }
    
    void MyDialog::triggerWork()
    {
        m_ThreadWorker = new QThread;
    
        QProgressDialog* progress = m_TraitementProdCartoWrkr->getProgress();
    
        m_TraitementProdCartoWrkr->moveToThread(m_ThreadWorker);
        QObject::connect(m_ThreadWorker, &QThread::started, m_TraitementProdCartoWrkr, &Worker::doWork);
        //QObject::connect(m_ThreadWorker, &QThread::started, progress, &QProgressDialog::exec);
    
        //QObject::connect(progress, &QProgressDialog::canceled, m_TraitementProdCartoWrkr, &Worker::sigAnnuler);
    
        m_ThreadWorker->start();
    }
    
    void MyDialog::StopWork()
    {
        m_TraitementProdCartoWrkr->stopWork();
    }
    
    0 回复  |  直到 4 年前
        1
  •  2
  •   ypnos    4 年前

    有(至少)三种方法可以避免这个问题:

    1. 在做这项工作的时候,有规律地打断你的工作,这样传入的信号就可以被处理了。例如,您可以使用 QTimer::singleShot(0, ...) 告诉自己什么时候该恢复工作。在任何取消/停止工作信号之后,该信号将位于队列的末尾。显然,这是破坏性的,并使代码复杂化。

    2. 使用从GUI线程设置的状态变量,但从工作线程读取。所以,一个 bool isCancelled 默认为false。一旦是真的,就停止工作。

    3. isCancelled() 方法由辅助进程直接调用。

    我以前使用第二种方法,现在在我的代码中使用第三种方法,并且通常将其与进度更新相结合。每当我发布一个进度更新,我也检查取消标志。原因是我对我的进度更新进行了计时,这样对用户来说它们是平滑的,但不会让工作人员无法完成工作。

    std::atomic 混合)。GUI/worker之间的其余通信仍将使用信号&插槽——因此它们在各自的线程中进行处理。

    有关第三种方法的示例,请参见 here here . 作业注册表还管理进度( see here ),并进一步向监控器(即进度条)发送信号。

        2
  •  2
  •   Vladimir Bershov    4 年前

    我的对话.h

    #include <QtWidgets/QDialog>
    #include "ui_MyDialog.h"
    
    class MyDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        MyDialog(QWidget *parent = nullptr);
        ~MyDialog();
    
        void triggerWork();
        void stopWork();
    
    signals:
        void sigChangeValue(int val);
    
    private:
        Ui::MyDialogClass ui;
    };
    

    我的对话.cpp

    #include "MyDialog.h"
    
    #include <QtConcurrent/QtConcurrent>
    #include <QThread>
    #include <atomic>
    #include <QProgressDialog>
    
    // Thread-safe flag to stop the thread. No mutex protection is needed 
    std::atomic<bool> gStop = false;
    
    MyDialog::MyDialog(QWidget *parent)
        : QDialog(parent)
    {
        ui.setupUi(this);
    
        auto progress = new QProgressDialog;
    
        connect(this, &MyDialog::sigChangeValue, 
            progress, &QProgressDialog::setValue);
    
        connect(progress, &QProgressDialog::canceled, 
            this, [this]()
            {
                stopWork();
            }
        );
    
        // To simplify the example, start the work here:
        triggerWork();
    }
    
    MyDialog::~MyDialog()
    { 
        stopWork();
    }
    
    void MyDialog::triggerWork()
    {
        // Run the code in another thread using High-Level QtConcurrent API
        QtConcurrent::run([this]()
            {
                for(int i = 0; i < 100 && !gStop; i++)
                {
                    this->sigChangeValue(i); // signal emition is always thread-safe
    
                    qDebug() << "running... i =" << i;
    
                    QThread::msleep(100);
                }
    
                qDebug() << "stopped";
            });
    }
    
    void MyDialog::stopWork()
    {
        gStop = true;
    }
    

    另请阅读:

    Threading Basics in Qt
    Multithreading Technologies in Qt
    Synchronizing Threads
    Threads and Objects
    The Missing Article About Qt Multithreading in C++
    Threads Events QObjects

        3
  •  1
  •   Pat. ANDRIA    4 年前

    @伊普斯,谢谢你的建议。 我解决问题的方法是修改:

        QObject::connect(progress, &QProgressDialog::canceled, this, &Worker::stopWork);
    

    Worker 这行中的构造函数:

        QObject::connect(progress, &QProgressDialog::canceled, [&]() {
                                                                      this->stopWork();
                                                                     });
    

    cancel 按钮 QProgressDialog .

    我不明白的是,为什么第一个代码(下面)不起作用?

    它不起作用,因为 signals/slots 在发出信号时选择,默认情况下为 Qt::AutoConnection ,但是我在接收器和发射器之间有不同的线程。 (详见 here ),因此无法工作

    Qt::DirectConnection

        QObject::connect(progress, &QProgressDialog::canceled, this, &Worker::stopWork, Qt::DirectConnection);