首页 > 解决方案 > Qt5 - QML:如何正确连接 ProgressBar 和 Button 以进行长时间运行的循环计算

问题描述

我正在连接 aProgressBar到两个ButtonsProgressBar只要我按下按钮,它就会计算一个循环。如果我按下另一个按钮,计算将被删除。我还没有实现pause按钮。

我正在尝试实现的打印屏幕如下,如果需要,可以在此处获得最小可验证示例的整个代码:

pbar

问题是,一旦我将文件ProgressBar与我的文件连接起来,就会main.cpp出现一堆错误,如下所示: class ProgressDialog has no member named...

错误

代码如下:

主文件

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include "progressbardialog.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQuickView view;
    ProgressBarDialog progressDialog;

    // One way but it does not work
    // engine.rootContext()->setContextProperty("control", QVariant::fromValue(progressDialog.refModel()));
    // Another way but this also does not work
    view.setSource(QUrl::fromLocalFile("main.qml"));

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

进度条对话框.h

#ifndef PROGRESSBARDIALOG_H
#define PROGRESSBARDIALOG_H

#include <QObject>
#include <QFutureWatcher>

class ProgressBarDialog : public QObject
{
    Q_OBJECT
    Q_PROPERTY(float progress READ progress WRITE setProgress NOTIFY progressChanged)
public:
    ProgressBarDialog();
    float progress(int &iterator);
    // I will use this as a reference to pass to main.cpp using setContextProperty()
    QObject &refModel()
    {
        return m_Model;
    }

public Q_SLOTS:
    void startComputation();
    void cancelComputation();
signals:
    void progressChanged();
private:
    int m_progressValue;
    QObject m_Model;
    QFutureWatcher<void> m_futureWatcher;
};

#endif // PROGRESSBARDIALOG_H

进度条对话框.cpp

#include "progressbardialog.h"
#include <QtConcurrent/QtConcurrentMap>


ProgressBarDialog::ProgressBarDialog()
{}

void spin(int &iteration)
{
    const int work = 1000 * 1000 * 40;
    volatile int v = 0;
    for(int j = 0; j < work; ++j)
        ++v;
}

float ProgressBarDialog::progress(int &iterator)
{
    (void) iterator;
    const int work = 1000 * 1000 * 40;
    volatile int v = 0;
    for(int j = 0; j < work; ++j)
        ++v;
    emit progressChanged();
}

void ProgressBarDialog::startComputation()
{
    // Prepare the vector
    QVector<int> vector;
    for(int i = 0; i < 40; ++i)
        vector.append(i);
    const QFuture<void> future = QtConcurrent::map(vector, spin);
    m_futureWatcher.setFuture(future);
}

void ProgressBarDialog::cancelComputation()
{
    m_futureWatcher.cancel();
}

最后是main.qml

import QtQuick 2.12                    // for the Item
import QtQuick.Controls 2.12           // for ApplicationWindow
import QtQuick.Layouts 1.12

ApplicationWindow {
    visible: true
    width: 440
    height: 480
    title: qsTr("Progress Bar")
    ColumnLayout {
            spacing: 10
            width: parent.width
        GroupBox {
            id: box1
            title: "Start - Stop"
            font.pointSize: 20
            Layout.alignment: parent.width
            spacing: 10
            GridLayout {
                width: parent.width
                columns: 1
                RowLayout {
                    spacing: 200
                    Layout.fillWidth: true
                    Layout.fillHeight: false
                    Button {
                        id: buttonStart
                        text: "Start"
                        font.pointSize: 15
                        enabled: !progressDialog.active
                        onClicked: progressDialog.startComputation()
                    }
                    Button {
                        id: buttonFinish
                        text: "Finish"
                        font.pointSize: 15
                        enabled: progressDialog.cancelComputation()
                    }
                }
            }
        }
        GroupBox {
            id: boxprogress
            title: "Progressbar"
            font.pointSize: 20
            Layout.alignment: parent.width
            spacing: 10
            GridLayout {
                width: parent.width
                columns: 1
                RowLayout {
                    Layout.fillWidth: true
                    Layout.fillHeight: false
                    ProgressBar {
                        id: progressbar_id
                        Layout.fillWidth: true
                        Layout.fillHeight: true
                        width: parent.width

                        // These are hard-coded values to confirm it is working
                        from: 0
                        to: 100
                        value: 5
                        onValueChanged: {
                            console.log("Progressbar value changed: ", progressbar_id.value)
                        }
                        onVisibleChanged: {
                            console.log("Progressbar visibility changed: ", progressbar_id.visible)
                        }
                    }
                    Connections {
                        target: progressDialog
                        onProgressChanged: progressbar_id.value = progress;
                    }
                    // This is working if clicking on the progressbar
                    MouseArea {
                        anchors.fill: parent
                        onClicked: progressbar_id.value += 5;
                    }
                }
            }
    }
}
}

到目前为止我尝试了什么

1)为了证明自己ProgressBarDialog是正确的,我之前尝试过main.qml使用硬编码值(我把它留在代码中,这样你就可以看到我做了什么)。但是,当我开始在.cpp文件中实现它并设置它时,Q_PROPERTY我收到了我在第二个屏幕截图上发布的所有错误。

2)我确定到目前为止所采取的程序,因为:a)根据官方文档这篇文章,因为我需要检查长时间运行的操作过程;b)根据QtConcurrent,该过程更有可能被检查,而这正是我使用的QtConcurrent::map()

3)我在progressbardialog.h中使用以下语句来确保对象正确连接并监听QML文件:

QObject &refModel()
{
    return m_Model;
}

但这并没有显示出实质性的改善。因此,深入研究这个过程,我认为它应该是使用的情况setContextProperty(),这让我明白了下一点。

4)根据官方文档这里的这篇文章,我以为我会修复错误,但它仍然存在。

我没有想法,任何可能遇到此问题的人,请指出正确的方向以获得可能的解决方案。

标签: c++qtqmlqt5

解决方案


Q_PROPERTY 旨在更轻松地读取/写入/响应 QML 端的变量更改(C++ 端)。要使用变量 read/write/notify 完成您的示例,请添加正确的 getter/setter(信号已经正常)。

float progress();
void setProgress(float);

如果您想从 QML 调用函数,请将其标记为 Q_INVOKABLE。另外,您的代码中 volatile 的意义何在?

总而言之,将您的迭代函数标记为 Q_INVOKABLE。它将增加一些内部进度值,然后发出 progressChanged()。这应该会导致 QML 端更新。


推荐阅读