I've got a toy QML app (Qt 5.7) that consists of the following QML page plus some C++ code that allows the QML to subscribe to some state on an audio device and reflect that state in the QML GUI. The toy QML looks like this:
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"
   }
}
When I run this, my C++ code calls onControlPointValueUpdated() whenever one of the four mute-states on my audio device changes, and based on the 'address' argument in the call, one of the four muteN QML properties is changed. And, whenever the mute4 property is changed, the text in the Text area is updated to reflect that.
This all works fine, as far as it goes, but at some point I'm going to need to scale it up; in particular I will need to track hundreds or thousands of values rather than just four, and having to manually declare an individual, hand-named QML property for each value would quickly become arduous and difficult to maintain; and the implementation of onControlPointValueUpdated() would become very inefficient if it has to manually if/elseif its way through every property.
So my question is, is there some way in QML for me to declare an array (or better yet, a dictionary/map) of properties, so that I can declare a large number of QML properties at once? i.e. something like this:
property bool mutes[256]: {false}   // would declare mutes[0] through mutes[255], all default to false
... or if not, is there some other recommended approach to holding large amounts of observable state within a QML document? I like the ability to have my GUI widgets bind to a QML property to get automatically updated as necessary, but it seems like perhaps QML properties weren't intended to be used en masse?
As soon as you get a large amount of data that has to be displayed (especially if it all shares a common format), you should think about using a QAbstractItemModel derivative.
Another thing to point out is that it's not a good idea to call into the QML of the user interface from C++, because it tends to limit what you can do in QML, and ties the QML to the C++.
For example, if you were to display the data in a list:

The items in the view are created on demand, and there are a limited amount that are allocated at any one time, which helps when you have a lot of data and potentially complex delegates.
Here's how a simple, custom QAbstractListModel with custom roles looks:
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
                }
            }
        }
    }
}
You can easily swap the ListView out with a GridView, for example. Best of all, the C++ knows nothing about the QML.
You can read more about using C++ models with Qt Quick here.
Looking at the image you posted in the comments, that type of UI could be made in a couple of ways. You could use the approach above, but using a Loader in the delegate. The Loader can determine which type of component to show based on some data in the model. However, it looks like the amount of data displayed on your UI, while large in quantity, might indeed be better off without a view.
You could still use a model (e.g. with a Repeater like @peppe mentioned), but you could also get away with just listing off each "panel" (I dunno what they're called) in a similar way to how you're currently doing it. It looks like most of the data in one panel is the same, so each one could be its own class (untested code follows):
#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
        }
    }
}
Then, Panel.qml might just be a column of buttons and whatnot:
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
            }
        }
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With