Skip to content

Commit

Permalink
Add QuantityObject and QuantityObjectModel
Browse files Browse the repository at this point in the history
QuantityObject provides QObject-based access to a quantity value.
QuantityObjectModel provides a model of QuantityObject values, and
is able to filter out items that do not have valid values.

This allows ListQuantityGroup and its derived types to specify their
models without creating a JavaScript array that directly refers each
quantity value. Otherwise, whenever a quantity value is updated, the
entire array is rebuilt, which in turn causes the QuantityRow repeater
to rebuild its delegates.

Part of #1338
  • Loading branch information
blammit committed Feb 13, 2025
1 parent 782c4c1 commit 3ccc9e7
Show file tree
Hide file tree
Showing 9 changed files with 800 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,11 @@ list(APPEND VenusQMLModule_CPP_SOURCES
src/productinfo.h
src/quantityinfo.h
src/quantityinfo.cpp
src/quantityinfo.cpp
src/quantityobject.h
src/quantityobject.cpp
src/quantityobjectmodel.h
src/quantityobjectmodel.cpp
src/qrangemodel_p.h
src/qrangemodel.h
src/qrangemodel.cpp
Expand Down
162 changes: 162 additions & 0 deletions src/quantityobject.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
** Copyright (C) 2025 Victron Energy B.V.
** See LICENSE.txt for license information.
*/

#include "quantityobject.h"

#include <QQmlInfo>

using namespace Victron::VenusOS;

namespace {
static const QString DefaultKey = QStringLiteral("value");
}

QuantityObject::QuantityObject(QObject *parent)
: QObject(parent)
, m_key(DefaultKey)
{
}

QObject* QuantityObject::object() const
{
return m_object;
}

void QuantityObject::setObject(QObject* object)
{
if (m_object != object) {
m_object = object;
connectNotifySignal();
updateValue();
emit objectChanged();
}
}

QString QuantityObject::key() const
{
return m_key;
}

void QuantityObject::setKey(const QString &key)
{
if (m_key != key) {
m_key = key;
connectNotifySignal();
updateValue();
emit keyChanged();
}
}

Victron::VenusOS::Enums::Units_Type QuantityObject::unit() const
{
return m_unit;
}

void QuantityObject::setUnit(Victron::VenusOS::Enums::Units_Type unit)
{
if (m_unit != unit) {
m_unit = unit;
emit unitChanged();
}
}

int QuantityObject::precision() const
{
return m_precision;
}

void QuantityObject::setPrecision(int precision)
{
if (m_precision != precision) {
m_precision = precision;
emit precisionChanged();
}
}

QVariant QuantityObject::defaultValue() const
{
return m_defaultValue;
}

void QuantityObject::setDefaultValue(const QVariant &value)
{
if (m_defaultValue != value) {
m_defaultValue = value;
updateValue();
emit defaultValueChanged();
}
}

qreal QuantityObject::numberValue() const
{
const QVariant &v = m_value.isValid() ? m_value : m_defaultValue;
return v.value<qreal>();
}

QString QuantityObject::textValue() const
{
const QVariant &v = m_value.isValid() ? m_value : m_defaultValue;
return v.metaType() == QMetaType(QMetaType::QString) ? v.toString() : QString();
}

bool QuantityObject::hasValue() const
{
return m_hasValue;
}

void QuantityObject::connectNotifySignal()
{
if (m_notifySignalConnection) {
QObject::disconnect(m_notifySignalConnection);
}

static const QMetaMethod updateValueMethod =
staticMetaObject.method(staticMetaObject.indexOfMethod("updateValue()"));
if (!updateValueMethod.isValid()) {
qmlInfo(this) << "Failed to find updateValue() method!";
return;
}

if (m_object && m_key.length()) {
const QMetaObject *metaObject = m_object->metaObject();
m_property = metaObject->property(metaObject->indexOfProperty(m_key.toLatin1()));
if (m_property.isValid()) {
m_notifySignalConnection = QObject::connect(m_object.data(), m_property.notifySignal(), this, updateValueMethod);
if (!m_notifySignalConnection) {
qmlInfo(this) << "Failed to connect to notify signal for: " << m_key;
}
} else if (m_key != DefaultKey) {
qmlInfo(this) << "Object does not have property: " << m_key;
}
}
}

void QuantityObject::updateValue()
{
const qreal prevNumberValue = numberValue();
const QString prevTextValue = textValue();
const bool prevHasValue = m_hasValue;

if (m_object && m_property.isValid()) {
m_value = m_property.read(m_object);
} else {
m_value.clear();
}

// hasValue=true if there is a valid number or string.
m_hasValue = m_object && m_key.length()
&& (m_value.isValid() || m_defaultValue.isValid())
&& (!qIsNaN(numberValue()) || textValue().length() > 0);

if (prevNumberValue != numberValue()) {
emit numberValueChanged();
}
if (prevTextValue != textValue()) {
emit textValueChanged();
}
if (prevHasValue != m_hasValue) {
emit hasValueChanged();
}
}
97 changes: 97 additions & 0 deletions src/quantityobject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
** Copyright (C) 2025 Victron Energy B.V.
** See LICENSE.txt for license information.
*/

#ifndef QUANTITYMODEL_H
#define QUANTITYMODEL_H

#include <qqmlintegration.h>
#include <QMetaObject>
#include <QMetaProperty>

#include "enums.h"

namespace Victron {
namespace VenusOS {

/*
Provides QObject-based access to a quantity value.
Example usage:
QtObject {
id: dataObject
property real voltage: 0.14
property real power: 536
}
QuantityObject { object: dataObject; key: "voltage"; unit: VenusOS.Units_Volt_DC }
QuantityObject { object: dataObject; key: "power"; unit: VenusOS.Units_Watt }
*/
class QuantityObject : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QObject* object READ object WRITE setObject NOTIFY objectChanged FINAL)
Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged FINAL)
Q_PROPERTY(Victron::VenusOS::Enums::Units_Type unit READ unit WRITE setUnit NOTIFY unitChanged FINAL)
Q_PROPERTY(int precision READ precision WRITE setPrecision NOTIFY precisionChanged FINAL)
Q_PROPERTY(QVariant defaultValue READ defaultValue WRITE setDefaultValue NOTIFY defaultValueChanged FINAL)
Q_PROPERTY(qreal numberValue READ numberValue NOTIFY numberValueChanged FINAL)
Q_PROPERTY(QString textValue READ textValue NOTIFY textValueChanged FINAL)
Q_PROPERTY(bool hasValue READ hasValue NOTIFY hasValueChanged FINAL)

public:
explicit QuantityObject(QObject *parent = nullptr);

QObject* object() const;
void setObject(QObject* object);

QString key() const;
void setKey(const QString &key);

Victron::VenusOS::Enums::Units_Type unit() const;
void setUnit(Victron::VenusOS::Enums::Units_Type unit);

int precision() const;
void setPrecision(int precision);

QVariant defaultValue() const;
void setDefaultValue(const QVariant &value);

qreal numberValue() const; // NaN if the value is not a real type
QString textValue() const; // empty if the value is not a string type
bool hasValue() const;

Q_SIGNALS:
void objectChanged();
void keyChanged();
void unitChanged();
void precisionChanged();
void defaultValueChanged();
void numberValueChanged();
void textValueChanged();
void hasValueChanged();

private Q_SLOTS:
void updateValue();

private:
void connectNotifySignal();

QPointer<QObject> m_object;
QString m_key;
QVariant m_value;
QVariant m_defaultValue;
QMetaProperty m_property;
QMetaObject::Connection m_notifySignalConnection;
Victron::VenusOS::Enums::Units_Type m_unit = Victron::VenusOS::Enums::Units_None;
int m_precision = Victron::VenusOS::Enums::Units_Precision_Default;
bool m_hasValue = false;
};

} /* VenusOS */
} /* Victron */

#endif // QUANTITYMODEL_H
Loading

0 comments on commit 3ccc9e7

Please sign in to comment.