c++ - Qt5 - QML:如何正确连接 ProgressBar 和 Button 以进行长时间运行的循环计算
问题描述
我正在连接 aProgressBar
到两个Buttons
。ProgressBar
只要我按下按钮,它就会计算一个循环。如果我按下另一个按钮,计算将被删除。我还没有实现pause
按钮。
我正在尝试实现的打印屏幕如下,如果需要,可以在此处获得最小可验证示例的整个代码:
问题是,一旦我将文件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)根据官方文档和这里的这篇文章,我以为我会修复错误,但它仍然存在。
我没有想法,任何可能遇到此问题的人,请指出正确的方向以获得可能的解决方案。
解决方案
Q_PROPERTY 旨在更轻松地读取/写入/响应 QML 端的变量更改(C++ 端)。要使用变量 read/write/notify 完成您的示例,请添加正确的 getter/setter(信号已经正常)。
float progress();
void setProgress(float);
如果您想从 QML 调用函数,请将其标记为 Q_INVOKABLE。另外,您的代码中 volatile 的意义何在?
总而言之,将您的迭代函数标记为 Q_INVOKABLE。它将增加一些内部进度值,然后发出 progressChanged()。这应该会导致 QML 端更新。
推荐阅读
- python - 如何使用 FacetGrid 显示一些选定的行
- autodesk-forge - Autodesk Forge Viewer three.min.js 找不到错误
- mod-rewrite - 反向代理的虚拟主机中的 mod_rewrite
- python - S3的Python getsize问题
- c# - 在 .NET Standard 中的属性中表示颜色
- html - 在网页中居中缩放的 asp.net 图像
- r - 如何在 R 中调用多个对象(数据框),所以我只需运行一次函数?
- c# - Azure 函数代理响应正文不能在大括号内包含值
- python - 监视套接字连接
- javascript - Javascript 性能提升