我有一个玩具QML应用程序(Qt 5.7),它包含以下QML页面和一些C ++代码,允许QML在音频设备上订阅某个状态并在QML GUI中反映该状态。玩具QML看起来像这样:
import QtQuick 2.6
import QtQuick.Controls 1.5
import QtQuick.Window 2.2
Item {
id: deviceState
property bool mute1: false
property bool mute2: false
property bool mute3: false
property bool mute4: false
Component.onCompleted: {
// Call out to C++ land, to tell the server what
// control points we are interested in tracking the state of
topLevelWindow.subscribeToControlPoints("Input 1-4 Mute", deviceState);
}
// Called by C++ land to tell us the current state of
// a control point we previously subscribed to.
function onControlPointValueUpdated(address, value) {
if (address == "Input 1 Mute") {
mute1 = value
}
else if (address == "Input 2 Mute") {
mute2 = value
}
else if (address == "Input 3 Mute") {
mute3 = value
}
else if (address == "Input 4 Mute") {
mute4 = value
}
}
// Absurdly minimalist example GUI
Text {
text: parent.mute4 ? "MUTED" : "unmuted"
}
}
当我运行它时,只要我的音频设备上的四个静音状态之一发生变化,我的C ++代码就会调用onControlPointValueUpdated(),并且根据调用中的'address'参数,四个muteN QML属性中的一个被更改。并且,每当更改mute4属性时,“文本”区域中的文本都会更新以反映该内容。
这一切都很好,就目前而言,但在某些时候我需要扩大规模;特别是我需要跟踪数百或数千个值而不是仅仅四个,并且必须为每个值手动声明一个单独的,手工命名的QML属性将很快变得艰难且难以维护;并且onControlPointValueUpdated()的实现将变得非常低效,如果它必须手动if / elseif通过每个属性。
所以我的问题是,在QML中是否有某种方式可以声明属性的数组(或者更好的是字典/映射),这样我就可以一次声明大量的QML属性?就像这样:
property bool mutes[256]: {false} // would declare mutes[0] through mutes[255], all default to false
......或者如果没有,是否有其他一些建议的方法来保存QML文档中的大量可观察状态?我喜欢让我的GUI小部件绑定到QML属性以根据需要自动更新的能力,但似乎QML属性可能不打算用于 en masse ?
答案 0 :(得分:8)
只要您获得大量必须显示的数据(特别是如果它们都共享一种通用格式),您应该考虑使用QAbstractItemModel
衍生物。
另一点需要指出的是,从C ++调用用户界面的QML并不是一个好主意,因为它往往会限制你在QML中可以做的事情,并将QML与C ++联系起来
例如,如果您要在列表中显示数据:
视图中的项目是按需创建的,并且在任何时候都分配了有限的数量,这有助于您拥有大量数据和可能复杂的代理。
以下是具有自定义角色的简单自定义QAbstractListModel
的外观:
main.cpp
:
#include <QtGui>
#include <QtQuick>
class UserModel : public QAbstractListModel
{
public:
UserModel() {
for (int i = 0; i < 100; ++i) {
mData.append(qMakePair(QString::fromLatin1("Input %1").arg(i), i % 5 == 0));
}
}
enum Roles
{
NameRole = Qt::UserRole,
MutedRole
};
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE
{
if (!index.isValid())
return QVariant();
switch (role) {
case Qt::DisplayRole:
case NameRole:
return mData.at(index.row()).first;
case MutedRole:
return mData.at(index.row()).second;
}
return QVariant();
}
int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const Q_DECL_OVERRIDE
{
return mData.size();
}
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const Q_DECL_OVERRIDE
{
return 1;
}
virtual QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE
{
QHash<int, QByteArray> names;
names[NameRole] = "name";
names[MutedRole] = "muted";
return names;
}
private:
QVector<QPair<QString, int> > mData;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
UserModel userModel;
engine.rootContext()->setContextProperty("userModel", &userModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
:
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
Window {
width: 300
height: 300
visible: true
ListView {
id: listView
anchors.fill: parent
anchors.margins: 10
model: userModel
spacing: 20
delegate: ColumnLayout {
width: ListView.view.width
Text {
text: name
width: parent.width / 2
font.pixelSize: 14
horizontalAlignment: Text.AlignHCenter
}
RowLayout {
Rectangle {
width: 16
height: 16
radius: width / 2
color: muted ? "red" : "green"
}
Text {
text: muted ? qsTr("Muted") : qsTr("Unmuted")
width: parent.width / 2
horizontalAlignment: Text.AlignHCenter
}
}
}
}
}
例如,您可以轻松地将ListView
与GridView
交换出来。最重要的是,C ++对QML一无所知。
您可以阅读有关使用Qt Quick here的C ++模型的更多信息。
查看您在评论中发布的图片,可以通过多种方式制作这种类型的用户界面。您可以使用上述方法,但在委托中使用Loader
。 Loader
可以根据模型中的某些数据确定要显示的component类型。但是,看起来UI上显示的数据量虽然数量很大,但如果没有视图,可能会更好。
你仍然可以使用一个模型(例如像{peper提到的那样Repeater
),但你也可以通过列出每个&#34;面板&#34; (我不知道他们被称之为什么)的方式与你目前的做法类似。看起来一个面板中的大多数数据是相同的,因此每个数据都可以是它自己的类(未经测试的代码如下):
#include <QtGui>
#include <QtQuick>
class Panel : public QObject
{
Q_OBJECT
Q_PROPERTY(bool muted READ muted WRITE setMuted NOTIFY mutedChanged)
Panel() { /* stuff */ }
// getters
// setters
signals:
// change signals
private:
// members
};
class WeirdPanel : public QObject
{
Q_OBJECT
Q_PROPERTY(int level READ level WRITE setLevel NOTIFY levelChanged)
WeirdPanel() { /* stuff */ }
// getters
// setters
signals:
// change signals
private:
// members
};
class Board : public QObject
{
Q_OBJECT
Q_PROPERTY(Panel *panel1 READ panel1 CONSTANT)
Q_PROPERTY(Panel *panel2 READ panel2 CONSTANT)
Q_PROPERTY(Panel *panel3 READ panel3 CONSTANT)
// etc.
Q_PROPERTY(WeirdPanel *weirdPanel READ weirdPanel WRITE setWeirdPanel NOTIFY weirdPanelChanged)
public:
Board() {
}
// getters
// setters
signals:
// change signals
private:
Panel panel1;
Panel panel2;
Panel panel3;
Panel panel4;
WeirdPanel weirdPanel;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Board board;
engine.rootContext()->setContextProperty("board", &board);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
:
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
Window {
width: 300
height: 300
visible: true
RowLayout {
anchors.fill: parent
Panel {
id: panel1
panel: board.panel1
}
Panel {
id: panel2
panel: board.panel2
}
Panel {
id: panel3
panel: board.panel3
}
WeirdPanel {
id: weirdPanel
panel: board.weirdPanel
}
}
}
然后,Panel.qml
可能只是一列按钮和诸如此类的东西:
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
Rectangle {
property var panel
color: "#444"
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight
ColumnLayout {
anchors.fill: parent
Text {
text: panel.name
width: parent.width / 2
font.pixelSize: 14
horizontalAlignment: Text.AlignHCenter
}
RowLayout {
Rectangle {
width: 16
height: 16
radius: width / 2
color: panel.muted ? "red" : "green"
}
Text {
text: panel.muted ? qsTr("Muted") : qsTr("Unmuted")
width: parent.width / 2
horizontalAlignment: Text.AlignHCenter
}
}
}
}
答案 1 :(得分:2)
经过一番搜索,看来最适合我当前需求的机制是QQmlPropertyMap
类。
特别是,我向QQmlPropertyMap
添加QQmlContext
,并通过setContextProperty()
将其公开给QML世界:
_sessionContext = new QQmlContext(_engine.rootContext());
_propertyMap = new QQmlPropertyMap(_sessionContext);
_sessionContext->setContextProperty("cps", _propertyMap);
...然后当我在C ++代码中收到一个值更新,而不是调用QML函数时,我只是用它来更新QQmlPropertyMap对象:
_propertyMap->insert("input_4_mute", QVariant(true)); // arguments hard-coded here for clarity
...在QML代码中还有待完成的事情是绑定到属性但是我想:
Rectangle {
width: 0; height: 0
x:160; y:120
color: cps.input_4_mute ? "red" : "blue"
}