Better_Software_Header_Mobile Better_Software_Header_Web

Find what you need - explore our website and developer resources

Bind QML Values across an Arbitrary Number of Elements

Properties bound across multiple instances of a window

// internalmessagebroker.hpp
// Singleton broker class contains signals that serve as pipes
// for different parts of a program to communicate with each other.

#pragma once

#include <QObject>

class InternalMessageBroker : public QObject
{
    Q_OBJECT

// Hide regular constructor
private:
    InternalMessageBroker() = default;

public:
    // Disable copy constructor
    InternalMessageBroker(const InternalMessageBroker& obj) = delete;
    InternalMessageBroker& operator=(InternalMessageBroker const&) = delete;
    static std::shared_ptr<InternalMessageBroker> instance()
    {
        static std::shared_ptr<InternalMessageBroker> sharedPtr{new InternalMessageBroker};
        return sharedPtr;
    }

// This is where all the signals would go
signals:
    void broadcastAPropertyChange(int value, bool broadcast);
};
// Main.qml
// Here are 3 windows, each with a PropertiesModel,
// that allows them all to share a binding to aProperty
// across all window instantiatons.

import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Controls

import com.kdab.example

Item {
    Instantiator {
        model: 3
        delegate: Window {
            PropertiesModel {
                id: propertiesModel
                aProperty: 1
            }
            ColumnLayout {
                anchors.fill: parent
                Text {
                    text: propertiesModel.aProperty
                }
                Slider {
                    id: hueSlider
                    value: propertiesModel.aProperty
                    Layout.fillWidth: true
                    onMoved: {
                        propertiesModel.aProperty = value;
                    }
                }
            }
        }
    }
}
// An ordinary getter
int PropertiesModel::aProperty()
{
    return m_aProperty;
}
// An ordinary notifier, form propertiesmodel.h
signals:
    void aPropertyChanged();
// Private setter implementation for a property that's being broadcasted
void PropertiesModel::setAProperty(const int value, const bool broadcast)
{
    if (broadcast)
        emit m_broker.get()->broadcastAPropertyChange(value, false);
    else if (m_currentScreen == screenName) {
        m_aProperty = value;
        emit screenSaturationChanged();
    }
}

// Publicly exposed setter prevents the broadcast argument from being
// specified by other callers
void PropertiesModel::setAProperty(const int value)
{
    // Always broadcast properties not being set by the message broker
    setAProperty(value, true);
}
// The constructor is used to connect signals from the broker to setter properties
PropertiesModel::PropertiesModel(QObject* parent)
    : QObject { parent }
{
    m_mb = InternalMessageBroker::instance();
    // Connections take place after the class has been instantiated
    // and its QML properties parsed.
    QTimer::singleShot(0, this, [this] () {
        connect(m_mb.get(), &InternalMessageBroker::aPropertyChange,
        this, &ScreenModel::setAProperty);
    });
}
// propertiesmodel.h
#pragma once

#include "internalmessagebroker.hpp"

#include <QQmlEngine>

class PropertiesModel : public QObject {
    Q_OBJECT
    QML_ELEMENT

    Q_PROPERTY(int aProperty READ aProperty WRITE setAProperty NOTIFY aPropertyChanged FINAL)
public:
    explicit PropertiesModel(QObject* parent = nullptr);

    int aProperty();
    void setAProperty(const int value);

signals:
    void aPropertyChanged();

private: 
    void setAProperty(const int value, const bool broadcast);

    std::shared_ptr<InternalMessageBroker> m_mb;
    int m_aProperty;
};
// In the original code what here I named broadcast used to be named spread.
void ScreenModel::setScreenHue(const int hue, const bool spread=true, const QString &screenName="s")
{
    if (spread)
        emit m_mb.get()->spreadHueChange(hue, false, m_currentScreen);
    // The incoming value is only accepted if screenName matches the screen
    // that the window is at and discarded otherwise.
    else if (m_currentScreen == screenName) {
        m_screens[m_currentScreen].hue = hue;
        emit screenHueChanged();
    }
}

Properties bound across instances of a window

About KDAB

JavierPérez

Javier Cordero

Software Engineer

Sign up for the KDAB Newsletter

Qt 5 to Qt 6 Migration Services

Learn Modern C++

Learn more