Qt5 - QML:如何正确连接 ProgressBar 和 Button 以进行长时间运行的循环计算

Posted

技术标签:

【中文标题】Qt5 - QML:如何正确连接 ProgressBar 和 Button 以进行长时间运行的循环计算【英文标题】:Qt5 - QML: How to properly connect a ProgressBar with Button for long-running loop calculation 【发布时间】:2019-11-27 18:23:19 【问题描述】:

我将一个ProgressBar 连接到两个Buttons。只要我按下按钮,ProgressBar 就会进行循环计算。如果我按下另一个按钮,计算将被删除。我还没有实现pause 按钮。

我想要实现的打印屏幕如下,如果需要,最小可验证示例的整个代码可用here:

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

代码如下:

ma​​in.cpp

#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();

progressbardialog.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

progressbardialog.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();

最后是 ma​​in.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 工作正常,我之前使用硬编码值(我把它留在代码中,所以你可以看到我做了什么)。但是,当我开始在 .cpp 文件中实现它并设置 Q_PROPERTY 时,我收到了我在第二个屏幕截图中发布的所有错误。

2) 我确信到目前为止所采取的程序是因为:a) 根据official documentation 和this post,因为我需要检查长时间运行的操作的过程; b) 根据QtConcurrent,该过程更有可能被检查,这正是我使用QtConcurrent::map()所做的

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

QObject &refModel()

    return m_Model;

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

4) 根据official documentation 和this post here 我以为我会修复错误,但它仍然存在。

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

【问题讨论】:

【参考方案1】:

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

float progress();
void setProgress(float);

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

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

【讨论】:

非常感谢您阅读我的问题:)。您能否更好地解释一下您的回答,更详细地说明代码中应该修改的内容? 我在我的存储库中放了一些代码:github.com/bialasjaroslaw/ProgressBar/tree/master 您唯一需要添加的是将 QFutureWatcher 信号连接到将发出 progressChanged() 的东西:doc.qt.io/qt-5/qfuturewatcher.html#progressValueChanged 我将在几分钟内添加它. volatile 存在(我推测)这样编译器就不会优化整个循环,否则它可以告诉变量v 从未实际使用过。确切的行为将取决于编译器和优化级别,但可以肯定的是更安全。在这样的基准测试和测试循环中,这是相当普遍的做法。当编译器摆脱所有实际代码时,基准测试可以返回一些非常惊人的结果...... :-) 我更喜欢 Chandler 的 asm volatile 把戏 :) youtube.com/… @bialy,非常感谢你,我明白你的意思了。它有效,但是我没有看到您在示例中使用了属性Q_INVOKABLE

以上是关于Qt5 - QML:如何正确连接 ProgressBar 和 Button 以进行长时间运行的循环计算的主要内容,如果未能解决你的问题,请参考以下文章

Qt5 和 QML:如何使用 WebEngine Quick Nano Browser 自动输入用户名和密码

[Qt5] Develop openCV3 by QML on Qt-creator

如何使用 QML (Qt5.3) for Windows 8.1 RT 创建多语言应用程序?

Qt 5 样式:动态加载 qml 文件

Qt5-QML:在第三方设备上自动验证用户名和密码

Qt5 / PyQt5 : 带有 QML 前端和 Python 后端的自定义 QML 组件