0

I have a following Q_PROPERTYs declared and of course the respective glue code for it.

Q_PROPERTY(bool thisEnabled READ isThisEnabled NOTIFY thisEnabledChanged)
Q_PROPERTY(bool thatEnabled READ isThatEnabled NOTIFY thatEnabledChanged)
Q_PROPERTY(bool somethingEnabled READ isSomethingEnabled NOTIFY somethingEnabledChanged)
Q_PROPERTY(bool somethingElseEnabled READ isSomethingElseEnabled NOTIFY somethingElseEnabledChanged)

Above works great!

Question:
But, these Q_PROPERTYs are part of a class which will add more features in future and hence more Q_PROPERTYs. Please note that all would be of type bool.

Now my concern is that I don't want add 5 more Q_PROPERTYs to 5 more features added. Is there some way I can add a Q_PROPERTY which is checked in QML by passing a Q_ENUM? Something like below?

// QML code
Rectangle {
    id: some_rect
    visible: cppClass.isEnabled(some_qenum_value)
}

Issue with using a Q_INVOKABLE here:
I know that I could add a Q_INVOKABLE method but if I add a Q_INVOKABLE, I loose the dynamic binding feature of a Q_PROPERTY.

I am using Qt 5.15.9 commercial version.

TheWaterProgrammer
  • 7,055
  • 12
  • 70
  • 159
  • You could use `QML_IMPORT_MAJOR_VERSION` https://doc.qt.io/qt-6/qmake-variable-reference.html#qml-import-major-version to distinguish between different interfaces. – iam_peter Sep 30 '22 at 12:35
  • `QML_IMPORT_MAJOR_VERSION` sounds like `Q_PROPERTY` in different versions of the code? Thats not what I am looking for. May be there is no option in Qt to parameterise a `Q_PROPERTY`. May be the only way to do this is to add a `Q_INVOKABLE` method – TheWaterProgrammer Sep 30 '22 at 12:41
  • You can probably also use the [nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator) to write something like `visible: thisEnabled ?? false`. It is part of [Qt 5.15](https://www.qt.io/blog/new-qml-language-features-in-qt-5.15). – iam_peter Sep 30 '22 at 12:41
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator is interesting but that is a very different thing compared to what I am looking for. – TheWaterProgrammer Sep 30 '22 at 12:44
  • You could also use a QVariantMap together with the nullish coalescing operator. Only drawback is that the complete map will be updated for every change, but it greatly reduces your code-size – Amfasis Sep 30 '22 at 12:55
  • So, the type of Q_PROPERTY would be QVariantMap? – TheWaterProgrammer Sep 30 '22 at 13:39
  • Yes, you would have a single Q_PROPERTY of type QVariantMap – Amfasis Sep 30 '22 at 13:52
  • I might have read your question wrong, but after re-reading it, isn't a QAbstractListModel something you could work with? Adding more and more items and having roles like name and value? – iam_peter Sep 30 '22 at 14:40
  • What about `bitwise or`? Define an enum, return an int value, and then use `bitwise or` to determine whether or not a feature is enabled, similar to [Window.flags](https://doc.qt.io/qt-5/qml-qtquick-window-window.html#flags-prop). (e.g., `Type.flags | Type.SomeFeature`) – SMR Sep 30 '22 at 16:18

1 Answers1

2

Flags are the way to go. Since integers are 32-bit, technically, you can use this to store your 4 boolean values and you have the ability to scale up to 32 boolean values:

#ifndef MyClass_H
#define MyClass_H

#include <QObject>
#include <QtQml>
#include <QFlags>

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MyFlags myFlags READ myFlags WRITE setMyFlags NOTIFY myFlagsChanged)
    QML_ELEMENT
public:
    MyClass(QObject*parent=nullptr) : QObject(parent) { }
    enum MyFlag
    {
        This = 1,
        That = 2,
        Something = 4,
        SomethingElse = 8
    };
    Q_FLAG(MyFlag)
    Q_FLAGS(MyFlags)
    Q_DECLARE_FLAGS(MyFlags, MyFlag)
signals:
    void myFlagsChanged();
protected:
    MyFlags m_myFlags;
    MyFlags myFlags() const { return m_myFlags; }
    void setMyFlags(MyFlags value)
    {
        if (value == m_myFlags)
        {
            return;
        }
        m_myFlags = value;
        emit myFlagsChanged();
    }
};

Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::MyFlags)

#endif

In QML, you can set the flags property by combining 0, 1 or more flags together with a bitwise | operator. e.g.

MyClass {
    id: myClass
    myFlags: MyClass.This
           | MyClass.That
           | MyClass.Something
           | MyClass.SomethingElse
}

Here is a QML for that demonstrates reading, setting, clearing each flag:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import qt5enumapp 1.0
Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    Frame {
        ColumnLayout {
            Switch {
                text: qsTr("This")
                checked: (myClass.myFlags & MyClass.This) !== 0
                onToggled: myClass.myFlags = checked
                                      ? myClass.myFlags | MyClass.This
                                      : myClass.myFlags & ~MyClass.This
            }
            Switch {
                text: qsTr("That")
                checked: (myClass.myFlags & MyClass.That) !== 0
                onToggled: myClass.myFlags = checked
                                      ? myClass.myFlags | MyClass.That
                                      : myClass.myFlags & ~MyClass.That
            }
            Switch {
                text: qsTr("Something")
                checked: (myClass.myFlags & MyClass.Something) !== 0
                onToggled: myClass.myFlags = checked
                                      ? myClass.myFlags | MyClass.Something
                                      : myClass.myFlags & ~MyClass.Something
            }
            Switch {
                text: qsTr("Something Else")
                checked: (myClass.myFlags & MyClass.SomethingElse) !== 0
                onToggled: myClass.myFlags = checked
                                      ? myClass.myFlags | MyClass.SomethingElse
                                      : myClass.myFlags & ~MyClass.SomethingElse
            }
            Text {
                text: qsTr("myClass.myFlags=%1").arg(myClass.myFlags)
            }
        }
    }
    MyClass {
        id: myClass
    }
}

Below is another simplified version of the QML without the C++ class. It further illustrates how to read, set, and clear flags from your integer value:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
    anchors.fill: parent
    readonly property int kThis: 1
    readonly property int kThat: 2
    readonly property int kSomething: 4
    readonly property int kSomethingElse: 8
    property int myFlags: 0
    Frame {
        ColumnLayout {
            Switch {
                text: qsTr("This")
                checked: (myFlags & kThis) !== 0
                onToggled: myFlags = checked
                                          ? myFlags | kThis
                                          : myFlags & ~kThis
            }
            Switch {
                text: qsTr("That")
                checked: (myFlags & kThat) !== 0
                onToggled: myFlags = checked
                                          ? myFlags | kThat
                                          : myFlags & ~kThat
            }
            Switch {
                text: qsTr("Something")
                checked: (myFlags & kSomething) !== 0
                onToggled: myFlags = checked
                                          ? myFlags | kSomething
                                          : myFlags & ~kSomething
            }
            Switch {
                text: qsTr("Something Else")
                checked: (myFlags & kSomethingElse) !== 0
                onToggled: myFlags = checked
                                          ? myFlags | kSomethingElse
                                          : myFlags & ~kSomethingElse
            }
            Text {
                text: qsTr("myFlags=%1").arg(myFlags)
            }
        }
    }
}

You can Try it Online!

Stephen Quan
  • 21,481
  • 4
  • 88
  • 75