【问题标题】:How to run a QThread from QWizardPage and access field()如何从 QWizardPage 运行 QThread 并访问 field()
【发布时间】:2019-12-18 00:54:41
【问题描述】:

我需要一些建议才能从QThread 访问QWizardPage 中的字段(QString name) 变量。我正在构建某种安装程序,我想在单独的线程中进行安装工作。

我的目的: 当到达提交/安装页面时,我想执行代码来执行“安装”并用我的进度更新QWizardPage,直到完成。

安装函数依赖于来自其他QWizardPages 的许多field() 变量。因此,我尝试从 QThread 执行此安装功能,该 QThread 在我的QWizardPage 的内部类中定义。问题是,field()-function 我是一个非静态成员,所以它不起作用。所以我没有想法将我的安装功能与我的 WizardPage 并行运行。

我尝试过这样的事情:

InstallPage.h

class InstallPage : public QWizardPage
{
    Q_OBJECT
    class WorkerThread : public QThread
    {
        Q_OBJECT
        void run() override;
    };

public:
    InstallPage(QWidget *parent = 0);

private: 
    QLabel *lProgress;
    WorkerThread *installer;
    void install(); 
};


InstallPage.c

InstallPage::InstallPage(QWidget *parent)
    : QWizardPage(parent)
{
...
    installer = new WorkerThread(this);
    installer->start();
}

void InstallPage::WorkerThread::run()
{
    if(field("checkBox1").ToBool()) 
    {
         doStuff();
    }
}

//QT-Creator says at field("checkBox1"): 
//error: call to non-static member function without an object argument

我也愿意接受任何其他让我的安装程序正常工作的想法。也许有人知道我没有想到的事情。

【问题讨论】:

  • 就像 eyllaesc 建议的那样,反转依赖并创建一个单独的工作类,复制必要的信息。也使同步变得更加容易。

标签: c++ qt qwidget qwizard qwizardpage


【解决方案1】:

另一种方法是创建一个工作器(QObject),它位于另一个执行繁重任务的线程中,并通过信号通知该任务的状态:

#include <QtWidgets>

class InitialPage: public QWizardPage
{
public:
    InitialPage(QWidget *parent = nullptr): QWizardPage(parent)
    {
        QSpinBox *spinbox = new QSpinBox;
        QLineEdit *lineedit = new QLineEdit;
        QVBoxLayout *lay = new QVBoxLayout(this);
        lay->addWidget(spinbox);
        lay->addWidget(lineedit);

        registerField("value1", spinbox);
        registerField("value2", lineedit);
    }

};

class InstallWorker: public QObject
{
    Q_OBJECT
public:
    InstallWorker(QObject *parent=nullptr): QObject(parent)
    {

    }
public Q_SLOTS:
    void install(int param1, const QString & param2)
    {
        Q_EMIT started();
        for(int i=0; i < 100; i++){
            qDebug() << __PRETTY_FUNCTION__ << i << param1 << param2;
            QThread::msleep(100);
            Q_EMIT progressChanged(i);
        }
        qDebug()<< __PRETTY_FUNCTION__ << "finished";
        Q_EMIT finished();
    }
Q_SIGNALS:
    void started();
    void progressChanged(int value);
    void finished();
};

class InstallPage: public QWizardPage
{
    Q_OBJECT
public:
    InstallPage(QWidget *parent = nullptr): QWizardPage(parent),
        label(new QLabel), progressbar(new QProgressBar)
    {

        QVBoxLayout *lay = new QVBoxLayout(this);
        lay->addWidget(label);
        lay->addWidget(progressbar);

        progressbar->setMinimum(0);
        progressbar->setMaximum(100);

        thread = new QThread(this);
        worker.moveToThread(thread);
        connect(&worker, &InstallWorker::started, this, &InstallPage::onStarted);
        connect(&worker, &InstallWorker::finished, this, &InstallPage::onFinished);
        connect(&worker, &InstallWorker::progressChanged, this, &InstallPage::onProgressChanged);
        thread->start();
    }
    ~InstallPage(){
        thread->quit();
        thread->wait();
    }
    void initializePage(){
        start_install();
    }
private Q_SLOTS:
    void start_install(){
        int param1 = field("value1").toInt();; 
        QString param2 = field("value2").toString();
        QMetaObject::invokeMethod(&worker, "install", Qt::QueuedConnection, Q_ARG(int, param1), Q_ARG(QString, param2));
    }
    void onStarted(){
        for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
            if(QAbstractButton * button = wizard()->button(which))
                button->setEnabled(false);
    }
    void onFinished(){
        for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
            if(QAbstractButton * button = wizard()->button(which))
                button->setEnabled(true);
        wizard()->next();
    }
    void onProgressChanged(int value){
        progressbar->setValue(value);
        label->setNum(value);
    }
private:
    InstallWorker worker;
    QThread *thread;
    QLabel *label;
    QProgressBar *progressbar;
};

class FinalPage: public QWizardPage
{
public:
    FinalPage(QWidget *parent = nullptr): QWizardPage(parent)
    {

    }
};

#include "main.moc"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QWizard wizard;
    wizard.addPage(new InitialPage);
    wizard.addPage(new InstallPage);
    wizard.addPage(new FinalPage);
    wizard.show();
    return app.exec();
}

【讨论】:

  • 我用一些字段对其进行了测试,这种方法效果很好。但是我遇到的问题是,我有很多字段需要安装。在您的解决方案中,您将字段值作为参数传递给线程。但是我需要大约 20-30 个参数,这似乎不是一种优雅的方式来做到这一点。有什么建议吗?
  • @Tiger69 它们是同一类型吗?如果将它们打包在一个容器中,那么您只需传递一个参数。如果它们不同,您可以将其转换为 QVariant 并按照指示使用容器
猜你喜欢
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
  • 2011-08-07
  • 2014-09-26
  • 2021-01-08
  • 2020-04-26
相关资源
最近更新 更多