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...
代码如下:
main.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();
最后是 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
工作正常,我之前使用硬编码值(我把它留在代码中,所以你可以看到我做了什么)。但是,当我开始在 .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