From ab4abd214e3d1ea99bd32f25b2cf3b594f804b1a Mon Sep 17 00:00:00 2001 From: Christoph Holzheuer Date: Wed, 21 Jan 2026 17:07:00 +0100 Subject: [PATCH] Fixed value display bug. --- BionxControl.vcxproj.user | 22 ++--- bcsliderstyle.cpp | 169 ++++++++++++++++++++++++++++++++++++++ bcsliderstyle.h | 151 +++------------------------------- bcvalue.cpp | 45 +++++++++- bcvalue.h | 33 ++++++-- bcvaluedelegate.cpp | 17 ++-- bcvalueeditor.cpp | 21 ++--- bcvalueeditor.h | 13 ++- 8 files changed, 289 insertions(+), 182 deletions(-) diff --git a/BionxControl.vcxproj.user b/BionxControl.vcxproj.user index 41a709a..5a4d7e1 100644 --- a/BionxControl.vcxproj.user +++ b/BionxControl.vcxproj.user @@ -1,12 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/bcsliderstyle.cpp b/bcsliderstyle.cpp index 4da62f9..dff41bf 100644 --- a/bcsliderstyle.cpp +++ b/bcsliderstyle.cpp @@ -32,3 +32,172 @@ #include +BCSliderStyle::BCSliderStyle() + : QProxyStyle() +{ + +} + +int BCSliderStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget ) const +{ + switch (metric) + { + case PM_SliderThickness: + return 24; // Höhe für horizontalen Slider + case PM_SliderLength: + return 16; // Handle-Größe + case PM_SliderControlThickness: + return 16; + case PM_SliderSpaceAvailable: + if (option) + { + if (const QStyleOptionSlider* sliderOpt = qstyleoption_cast(option)) + { + return sliderOpt->rect.width() - 20; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); + + default: + return QProxyStyle::pixelMetric(metric, option, widget); + } +} + +QRect BCSliderStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex* opt,SubControl sc, const QWidget* widget) const +{ + if (cc == CC_Slider) { + if (const QStyleOptionSlider* slider = qstyleoption_cast(opt)) + { + QRect rect = slider->rect; + int handleSize = 16; + + if (sc == SC_SliderHandle) { + // Handle Position korrekt berechnen + if (slider->orientation == Qt::Horizontal) { + int range = slider->maximum - slider->minimum; + int pos = slider->sliderPosition - slider->minimum; + int pixelRange = rect.width() - handleSize; + int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0; + + return QRect(rect.x() + pixelPos, + rect.center().y() - handleSize / 2, + handleSize, handleSize); + } else { + int range = slider->maximum - slider->minimum; + int pos = slider->sliderPosition - slider->minimum; + int pixelRange = rect.height() - handleSize; + int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0; + + return QRect(rect.center().x() - handleSize / 2, + rect.bottom() - pixelPos - handleSize, + handleSize, handleSize); + } + } + } + } + return QProxyStyle::subControlRect(cc, opt, sc, widget); +} + +void BCSliderStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + if (control == CC_Slider) + { + if (const QStyleOptionSlider* slider = qstyleoption_cast(option)) { + painter->setRenderHint(QPainter::Antialiasing); + + // Fluent Colors + QColor accentColor(0, 120, 212); // #0078D4 + QColor inactiveColor(138, 136, 134); // #8A8886 + QColor bgColor(255, 255, 255); // White background + //QColor disabledText = option.palette.color(QPalette::Disabled, QPalette::Text); + //painter->setBrush(disabledText); + //QColor bgColor = Qt::green;//option->palette.color(QPalette::Base); + drawHorizontalFluentSlider(painter, slider, accentColor, inactiveColor, bgColor); + return; + } + } + QProxyStyle::drawComplexControl(control, option, painter, widget); +} + + +void BCSliderStyle::drawHorizontalFluentSlider(QPainter* painter, const QStyleOptionSlider* slider, + const QColor& activeColor, const QColor& inactiveColor, + const QColor& bgColor) const +{ + QRect groove = slider->rect; + + int grooveHeight = 4; + // Track sollte im Widget-Zentrum sein, nicht im groove-Zentrum + int grooveY = groove.center().y() - grooveHeight / 2; + + // Full background track + QRect fullTrack(groove.left(), grooveY, groove.width(), grooveHeight); + painter->setPen(Qt::NoPen); + painter->setBrush(inactiveColor.lighter(150)); + painter->drawRoundedRect(fullTrack, grooveHeight / 2, grooveHeight / 2); + + QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr); + + // Active track (filled portion) + int activeWidth = handle.center().x() - groove.left(); + QRect activeTrack(groove.left(), grooveY, activeWidth, grooveHeight); + painter->setBrush(activeColor); + painter->drawRoundedRect(activeTrack, grooveHeight / 2, grooveHeight / 2); + + // Handle (Thumb) - Fluent style is more subtle + int handleSize = 16; + QRect thumbRect(handle.center().x() - handleSize / 2, + handle.center().y() - handleSize / 2, + handleSize, handleSize); + + // Hover effect - subtle glow + if (slider->state & State_MouseOver) + { + painter->setBrush(QColor(activeColor.red(), activeColor.green(), + activeColor.blue(), 30)); + int glowSize = 18; + QRect glow(handle.center().x() - glowSize / 2, + handle.center().y() - glowSize / 2, + glowSize, glowSize); + painter->drawEllipse(glow); + } + + // Thumb + painter->setBrush(bgColor); + painter->setPen(QPen(activeColor, 2)); + painter->drawEllipse(thumbRect); + + // Inner circle for pressed state + if (slider->state & State_Sunken) { + int innerSize = 6; + QRect inner(handle.center().x() - innerSize / 2, + handle.center().y() - innerSize / 2, + innerSize, innerSize); + painter->setPen(Qt::NoPen); + painter->setBrush(activeColor); + painter->drawEllipse(inner); + } +} + +void BCSliderStyle::drawSliderIndicator( QPainter* painter, QRect& groove ) +{ + /* + int grooveHeight = 4; + // Track sollte im Widget-Zentrum sein, nicht im groove-Zentrum + int grooveY = groove.center().y() - grooveHeight / 2; + + // Full background track + QRect fullTrack(groove.left(), grooveY, groove.width(), grooveHeight); + painter->setPen(Qt::NoPen); + painter->setBrush(inactiveColor.lighter(150)); + painter->drawRoundedRect(fullTrack, grooveHeight / 2, grooveHeight / 2); + + QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr); + + // Active track (filled portion) + int activeWidth = handle.center().x() - groove.left(); + QRect activeTrack(groove.left(), grooveY, activeWidth, grooveHeight); + painter->setBrush(activeColor); + painter->drawRoundedRect(activeTrack, grooveHeight / 2, grooveHeight / 2); + */ +} diff --git a/bcsliderstyle.h b/bcsliderstyle.h index 3e99077..d72d6a5 100644 --- a/bcsliderstyle.h +++ b/bcsliderstyle.h @@ -61,152 +61,23 @@ // Fluent Design Slider Style class BCSliderStyle : public QProxyStyle { + public: - BCSliderStyle() - : QProxyStyle() - {} + BCSliderStyle(); - int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override - { - switch (metric) - { - case PM_SliderThickness: - return 24; // Höhe für horizontalen Slider - case PM_SliderLength: - return 16; // Handle-Größe - case PM_SliderControlThickness: - return 16; - case PM_SliderSpaceAvailable: - if (option) - { - if (const QStyleOptionSlider* sliderOpt = qstyleoption_cast(option)) - { - return sliderOpt->rect.width() - 20; - } - } - return QProxyStyle::pixelMetric(metric, option, widget); + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; - default: - return QProxyStyle::pixelMetric(metric, option, widget); - } - } + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const override; + void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const override; + void drawHorizontalFluentSlider(QPainter* painter, + const QStyleOptionSlider* slider, + const QColor& activeColor, + const QColor& inactiveColor, + const QColor& bgColor) const; - QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt,SubControl sc, const QWidget* widget) const override - { - if (cc == CC_Slider) { - if (const QStyleOptionSlider* slider = qstyleoption_cast(opt)) - { - QRect rect = slider->rect; - int handleSize = 16; - if (sc == SC_SliderHandle) { - // Handle Position korrekt berechnen - if (slider->orientation == Qt::Horizontal) { - int range = slider->maximum - slider->minimum; - int pos = slider->sliderPosition - slider->minimum; - int pixelRange = rect.width() - handleSize; - int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0; - - return QRect(rect.x() + pixelPos, - rect.center().y() - handleSize / 2, - handleSize, handleSize); - } else { - int range = slider->maximum - slider->minimum; - int pos = slider->sliderPosition - slider->minimum; - int pixelRange = rect.height() - handleSize; - int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0; - - return QRect(rect.center().x() - handleSize / 2, - rect.bottom() - pixelPos - handleSize, - handleSize, handleSize); - } - } - } - } - return QProxyStyle::subControlRect(cc, opt, sc, widget); - } - - void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const override - { - if (control == CC_Slider) - { - if (const QStyleOptionSlider* slider = qstyleoption_cast(option)) { - painter->setRenderHint(QPainter::Antialiasing); - - // Fluent Colors - QColor accentColor(0, 120, 212); // #0078D4 - QColor inactiveColor(138, 136, 134); // #8A8886 - QColor bgColor(255, 255, 255); // White background - //QColor disabledText = option.palette.color(QPalette::Disabled, QPalette::Text); - //painter->setBrush(disabledText); - //QColor bgColor = Qt::green;//option->palette.color(QPalette::Base); - drawHorizontalFluentSlider(painter, slider, accentColor, inactiveColor, bgColor); - return; - } - } - QProxyStyle::drawComplexControl(control, option, painter, widget); - } - -private: - - void drawHorizontalFluentSlider(QPainter* painter, const QStyleOptionSlider* slider, - const QColor& activeColor, const QColor& inactiveColor, - const QColor& bgColor) const - { - QRect groove = slider->rect; - QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr); - - int grooveHeight = 4; - // Track sollte im Widget-Zentrum sein, nicht im groove-Zentrum - int grooveY = slider->rect.center().y() - grooveHeight / 2; - - // Full background track - QRect fullTrack(groove.left(), grooveY, groove.width(), grooveHeight); - painter->setPen(Qt::NoPen); - painter->setBrush(inactiveColor.lighter(150)); - painter->drawRoundedRect(fullTrack, grooveHeight / 2, grooveHeight / 2); - - // Active track (filled portion) - int activeWidth = handle.center().x() - groove.left(); - QRect activeTrack(groove.left(), grooveY, activeWidth, grooveHeight); - painter->setBrush(activeColor); - painter->drawRoundedRect(activeTrack, grooveHeight / 2, grooveHeight / 2); - - // Handle (Thumb) - Fluent style is more subtle - int handleSize = 16; - QRect thumbRect(handle.center().x() - handleSize / 2, - handle.center().y() - handleSize / 2, - handleSize, handleSize); - - // Hover effect - subtle glow - if (slider->state & State_MouseOver) - { - painter->setBrush(QColor(activeColor.red(), activeColor.green(), - activeColor.blue(), 30)); - int glowSize = 18; - QRect glow(handle.center().x() - glowSize / 2, - handle.center().y() - glowSize / 2, - glowSize, glowSize); - painter->drawEllipse(glow); - } - - // Thumb - painter->setBrush(bgColor); - painter->setPen(QPen(activeColor, 2)); - painter->drawEllipse(thumbRect); - - // Inner circle for pressed state - if (slider->state & State_Sunken) { - int innerSize = 6; - QRect inner(handle.center().x() - innerSize / 2, - handle.center().y() - innerSize / 2, - innerSize, innerSize); - painter->setPen(Qt::NoPen); - painter->setBrush(activeColor); - painter->drawEllipse(inner); - } - } + static void drawSliderIndicator( QPainter* painter, QRect& groove ); }; diff --git a/bcvalue.cpp b/bcvalue.cpp index c1272cb..c5be90a 100644 --- a/bcvalue.cpp +++ b/bcvalue.cpp @@ -31,6 +31,7 @@ #include +#include #include @@ -208,6 +209,8 @@ double BCValue::calcMinMaxRatio() const bool BCValue::valuesForSlider( int& value, int& min, int& max ) const { + value = min = max = 0; + // min & max sind vorraussetzung für den slider if( !_optMin.has_value() || !_optMax.has_value() ) return false; @@ -225,6 +228,7 @@ bool BCValue::valuesForSlider( int& value, int& min, int& max ) const } + void BCValue::dumpValue() const { @@ -235,4 +239,43 @@ void BCValue::dumpValue() const } -/// ---- +QString BCValue::toString() const +{ + QString result; + QTextStream stream(&result); + + stream << *this; + + /* + qDebug() << "DeviceID: " << _deviceID << " Register: " << _registerID << " state:" " << state << " << " label: " << _label; + qDebug() << "formattedValue: " << formatValue() << " min: " << _optMin << " max: " << _optMax << " factor: " << _factor << " ValueType: " << (char)_valueType << " "; + qDebug() << "indexRow: " << _indexRow << " isWord: " << isWord() << " isRO: " << isReadOnly(); + qDebug(); + */ + + return result; + +} + + +// Generischer Operator für ALLE Q_GADGETs +inline QTextStream& operator<<(QTextStream& out, const BCValue& obj) +{ + const QMetaObject* meta = &obj.staticMetaObject; + + out << meta->className() << " { "; + + // Iteriere über alle Properties (Reflection) + for (int i = 0; i < meta->propertyCount(); ++i) { + QMetaProperty prop = meta->property(i); + const char* propName = prop.name(); + QVariant val = prop.readOnGadget(&obj); + + out << propName << ": " << val.toString(); + + if (i < meta->propertyCount() - 1) out << ", "; + } + out << " }"; + + return out; +} diff --git a/bcvalue.h b/bcvalue.h index 02566c7..af8fa62 100644 --- a/bcvalue.h +++ b/bcvalue.h @@ -6,7 +6,7 @@ Using: - mhs_can_drv.c + mhsMEMBER _canMEMBER _drv.c © 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany Klaus Demlehner, klaus@mhs-elektronik.de @see www.mhs-elektronik.de @@ -30,18 +30,18 @@ ***************************************************************************/ -#ifndef BCVALUE_H -#define BCVALUE_H +#ifndef BCVALUEMEMBER_H +#define BCVALUEMEMBER_H + +#include #include #include #include #include -#include #include - /* Werte haben verschiedene Längen (1,2 und 4 Byte) und werder auf unterschiedliche Art und Weise ausgelesen und geschrieben (Siehe BCValueTypeWord). Sie können also Wert-Typen zugeordnet werden. Ein Werttyp @@ -61,7 +61,7 @@ using OptDouble = std::optional; // Enthält den gelesenen Wert oder einen Fehlerstring using TransmitResult = std::expected; // Funktionsobject, um Werte aus der Transmitterschicht zu holden -//using ReadValueFunc = std::function; +//using ReadValueFunc = std::function; class BCValue { @@ -96,7 +96,19 @@ public: Q_DECLARE_FLAGS(Flags, Flag ) Q_FLAG(Flags) - BCValue( BCDevice::ID deviceID_, BC::ID registerID_ ); + //Q_PROPERTY(Flags valueFlags MEMBER _valueFlags ) + Q_PROPERTY(BCDevice::ID deviceID MEMBER _deviceID READ deviceID ) + Q_PROPERTY(BC::ID registerID MEMBER _registerID READ registerID ) + Q_PROPERTY(ValueType valueType MEMBER _valueType READ valueType ) + Q_PROPERTY(int indexRow MEMBER _indexRow READ indexRow) + Q_PROPERTY(QString label MEMBER _label READ label ) + Q_PROPERTY(uint32_t rawValue MEMBER _rawValue READ rawValue ) + Q_PROPERTY(QString unitLabel MEMBER _unitLabel READ unitLabel ) + Q_PROPERTY(double factor MEMBER _factor ) + //QMEMBER _PROPERTY(OptDouble MEMBER _optMin) + //QMEMBER _PROPERTY(OptDouble MEMBER _optMax) + + BCValue( BCDevice::ID deviceID, BC::ID registerID ); QString formatValue() const; double calcMinMaxRatio() const; @@ -116,12 +128,15 @@ public: ValueType valueType() const noexcept; int indexRow() const noexcept; + void setIndexRow(int newIndexRow); QString label() const; QString unitLabel() const; bool valuesForSlider( int& value, int& min, int& max ) const; + QString toString() const; + protected: mutable Flags _valueFlags{BCValue::Flag::NoFlag}; @@ -138,6 +153,7 @@ protected: }; Q_DECLARE_OPERATORS_FOR_FLAGS(BCValue::Flags) +Q_DECLARE_METATYPE(BCValue::Flags) using BCValuePtr = std::shared_ptr; using BCValuePtrConst = std::shared_ptr; @@ -147,6 +163,7 @@ using BCValueList = QList; Q_DECLARE_METATYPE(const BCValuePtr) Q_DECLARE_METATYPE(BCValueList) - +// Generischer Operator für ALLE GADGETs +inline QTextStream& operator<<(QTextStream& out, const BCValue& obj); #endif // BCVALUE_H diff --git a/bcvaluedelegate.cpp b/bcvaluedelegate.cpp index 08bb6fd..bdc843f 100644 --- a/bcvaluedelegate.cpp +++ b/bcvaluedelegate.cpp @@ -62,16 +62,15 @@ QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt const BCValue& bcValue = *(_valueList[ index.row()].get()); - int value,min,max; - bool hasData = bcValue.valuesForSlider( value, min, max ); + BCValueEditorParams params; + bool hasData = bcValue.valuesForSlider( params.value, params.min, params.max ); if( !hasData ) return nullptr; - qDebug() << " --- Create Editor: " << bcValue.label() << " value: " << value << " min: " << min << " max: " << max << " ratio:" << bcValue.calcMinMaxRatio()*100.0 << '%'; + qDebug() << " --- Create Editor: " << bcValue.label() << " value: " << params.value << " min: " << params.min << " max: " << params.max << " ratio:" << bcValue.calcMinMaxRatio()*100.0 << '%'; auto* valueEditor = new BCValueEditor(parent); - valueEditor->setValue( value ); - valueEditor->setRange( min, max ); + valueEditor->setValueAndRange( params ); // Signal für sofortige Updates connect(valueEditor, &BCValueEditor::valueChanged, this, [this, valueEditor]() @@ -108,7 +107,7 @@ void BCValueDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, c BCValueEditor* slider = qobject_cast(editor); if (slider) { - int value = slider->getValue(); + int value = slider->value(); model->setData(index, value, Qt::EditRole); } return; @@ -197,7 +196,11 @@ void BCValueDelegate::paintButtonIndicator(QPainter* painter, const QStyleOption void BCValueDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const { double ratio = bcValue.calcMinMaxRatio(); - qDebug() << " --- paint: " << bcValue.label() << ":" << ratio; + + int value,min,max; + bcValue.valuesForSlider( value, min, max ); + + qDebug() << " --- paint: " << bcValue.label() << " value: " << value << " min: " << min << " max: " << max << " ratio:" << ratio*100.0 << '%'; // Text und kleiner Slider-Indikator zeichnen painter->save(); diff --git a/bcvalueeditor.cpp b/bcvalueeditor.cpp index be3c7b7..6a6b81a 100644 --- a/bcvalueeditor.cpp +++ b/bcvalueeditor.cpp @@ -28,31 +28,28 @@ BCValueEditor::BCValueEditor( QWidget *parent ) // Wenn Reset gedrückt wird -> Slider auf 0 (löst auch valueChanged aus) connect(_commitButton, &QPushButton::clicked, this, [this]() { - emit valueCommited( getValue() ); + emit valueCommited( value() ); }); } -int BCValueEditor::getValue() const +int BCValueEditor::value() const { return _slider->value(); } -void BCValueEditor::setValue(int val) + + +void BCValueEditor::setValueAndRange( const BCValueEditorParams& params ) { + _slider->setRange( params.min, params.max); // Block Signals verhindern Endlosschleifen, falls das Model // das Widget während des Updates neu setzt (passiert manchmal bei Live-Updates). - if (val != _slider->value()) + if (params.value != _slider->value()) { bool blocked = _slider->blockSignals(true); - _slider->setValue(val); + _slider->setValue(params.value); _slider->blockSignals(blocked); } + } - -void BCValueEditor::setRange( int min, int max ) -{ - _slider->setRange( min, max); -} - - diff --git a/bcvalueeditor.h b/bcvalueeditor.h index 1c3d52f..d6e10fe 100644 --- a/bcvalueeditor.h +++ b/bcvalueeditor.h @@ -9,6 +9,13 @@ class QSlider; class QPushButton; class BCValue; +struct BCValueEditorParams +{ + int value{0}; + int min{0}; + int max{0}; +}; + class BCValueEditor : public QWidget, private Ui::BCValueEditor { Q_OBJECT @@ -17,9 +24,8 @@ public: explicit BCValueEditor(QWidget *parent = nullptr); - int getValue() const; - void setValue(int val); - void setRange( int min, int max ); + int value() const; + void setValueAndRange( const BCValueEditorParams& params ); signals: @@ -27,4 +33,5 @@ signals: void valueCommited(int value); }; + #endif // BCValueEditor_H