diff --git a/BionxControl.pro b/BionxControl.pro index 3706654..e9fbbca 100644 --- a/BionxControl.pro +++ b/BionxControl.pro @@ -51,6 +51,7 @@ SOURCES += \ bcdriver.cpp \ bcdrivertinycan.cpp \ bcguihelpers.cpp \ + bcsliderstyle.cpp \ bctransmitter.cpp \ bcvalue.cpp \ bcvaluemodel.cpp \ @@ -68,6 +69,7 @@ HEADERS += \ bcdrivertinycan.h \ bcguihelpers.h \ bcmainwindow.h \ + bcsliderstyle.h \ bctransmitter.h \ bcvalue.h \ bcvaluemodel.h \ diff --git a/bcanimateddelegate.cpp b/bcanimateddelegate.cpp index 17ab2c3..d04e83d 100644 --- a/bcanimateddelegate.cpp +++ b/bcanimateddelegate.cpp @@ -181,54 +181,31 @@ QSize BCAnimatedDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo void BCAnimatedDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - // 1. Standard-Zeichnen (Text, Hintergrund, Selection) durchführen + // Standard-Zeichnen (Text, Hintergrund, Selection) durchführen QStyledItemDelegate::paint(painter, option, index); - //QPen pen(QColor(255, 165, 0)); // Orange - - /* - if (index.row() == _highlightedRow && _opacity > 0.0) - { - painter->save(); - - qDebug() << " --- is highlight: " << index.row(); - - QColor highlightColor( 0xFF9800 ); - highlightColor.setAlphaF(_opacity); - - QPen pen(highlightColor, 3); - painter->setPen(pen); - painter->setBrush(Qt::NoBrush); - painter->drawRoundedRect(option.rect.adjusted(2, 2, -2, -2), 8, 8); - - painter->restore(); - } - -*/ - int row = index.row(); - if (index.column() == 1 ) + int col = index.column(); + + switch (col) { - if( m_rowOpacities.contains(row)) - { - paintHighlightRow(painter,option,index); - } + case 1: + + if( m_rowOpacities.contains(row)) + paintHighlightRow(painter,option,index); + break; + + case 2: + + if( row>-1 && row <= _valueList.size() ) + paintSliderIndicator(painter,option,index); + + default: + break; } } -/* -qreal opacity = m_rowOpacities.value(row); -if (opacity > 0.01) -{ - painter->save(); - painter->setOpacity(opacity); - painter->fillRect(option.rect, QColor(255, 140, 0, 120)); - painter->restore(); -} -*/ - - void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { painter->save(); @@ -239,33 +216,70 @@ void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOption // Margin von 4px QRect itemRect = option.rect.adjusted(3, 3, -3, -3); - // Border von 2px berücksichtigen (nach innen) - //QRect contentRect = itemRect.adjusted(2, 2, -2, -2); - // painter->fillRect(contentRect,Qt::green); - /* - // Hintergrund (weiß) - painter->setBrush(Qt::white); - painter->setPen(Qt::NoPen); - painter->drawRoundedRect(itemRect, 8, 8); - */ // Border (2px solid #2196F3) QPen borderPen( Qt::red, 1); painter->setPen(borderPen); painter->setBrush(Qt::NoBrush); painter->drawRoundedRect(itemRect, 2, 2); - // Padding von 8px für den Content - //QRect textRect = contentRect.adjusted(8, 8, -8, -8); - - /* - // Text zeichnen - painter->setPen(Qt::black); // oder option.palette.color(QPalette::Text) - QString text = index.data(Qt::DisplayRole).toString(); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); -*/ painter->restore(); + } +void BCAnimatedDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + + const BCValue& valueX = *(_valueList[ index.row()].get()); + int value = 50;index.model()->data(index, Qt::DisplayRole).toInt(); + + // Hintergrund + if (option.state & QStyle::State_Selected) + { + painter->fillRect(option.rect, option.palette.highlight()); + } + else if (index.row() % 2 == 1) + { + painter->fillRect(option.rect, QColor(0xFAFAFA)); + } + else + { + painter->fillRect(option.rect, Qt::white); + } + + // Text und kleiner Slider-Indikator zeichnen + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + //QRect textRect = option.rect.adjusted(8, 0, -120, 0); + + QRect barRect = option.rect.adjusted + ( + 8, + option.rect.height() / 2 - 2, + -8, + -option.rect.height() / 2 + 2 + ); + + //QRect barRect = option.rect; + // Text + //painter->setPen(option.state & QStyle::State_Selected ? option.palette.highlightedText().color() : Qt::black); + //painter->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, + // QString::number(value)); + + // Mini Progress Bar + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(0xE0E0E0)); + painter->drawRoundedRect(barRect, 2, 2); + + QRect fillRect = barRect; + fillRect.setWidth(barRect.width() * value / 100); + painter->setBrush(QColor(0x0078D4)); + painter->drawRoundedRect(fillRect, 2, 2); + + painter->restore(); + + +} void BCAnimatedDelegate::onHighlightRow(int row) { diff --git a/bcanimateddelegate.h b/bcanimateddelegate.h index 3c66fe8..4b81d9e 100644 --- a/bcanimateddelegate.h +++ b/bcanimateddelegate.h @@ -61,21 +61,6 @@ public: QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex& index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const override; - /* - qreal highlightOpacity() const - { - return _opacity; - } - - void setHighlightOpacity(qreal opacity) - { - _opacity = opacity; - //qDebug() << " --- opa: " << opacity; - // __fix! unsinn! - emit viewUpdateNeeded(); - } -*/ - void clearAllHighlights(); public slots: @@ -91,13 +76,11 @@ private: void updateRow(int row); void paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; const BCValueList& _valueList; QTableView* _view{}; - //int _highlightedRow{-1}; - //qreal _opacity{1.0}; - QPropertyAnimation* _animation{}; private: diff --git a/bcdeviceview.cpp b/bcdeviceview.cpp index 790f1be..19bc748 100644 --- a/bcdeviceview.cpp +++ b/bcdeviceview.cpp @@ -30,6 +30,8 @@ ***************************************************************************/ +#include + #include #include @@ -44,18 +46,13 @@ BCDeviceView::BCDeviceView(QWidget *parent) // __fix! ziemlich wildes ge-pointere, hier _itemDelegate = new BCAnimatedDelegate( _valueModel.getValueList(), this); setItemDelegate( _itemDelegate ); - - qDebug() << " --- View size I: " << this->size(); - } void BCDeviceView::setDeviceID( BCDevice::ID deviceID ) { - qDebug() << " --- View size II: " << this->size(); - - _devideID = deviceID; + _devideID = deviceID; } BCDevice::ID BCDeviceView::getDeviceID() const @@ -64,7 +61,9 @@ BCDevice::ID BCDeviceView::getDeviceID() const } - +/** + * @brief Gibt eine Referenz auf der ValueList zurück. + */ const BCValueList& BCDeviceView::getValueListX() { @@ -72,8 +71,10 @@ const BCValueList& BCDeviceView::getValueListX() } - -// __FIX ist das ok so? +/** + * @brief SLOT, der aufgerufen wird, wenn die ValueList vom XML-Lader fertig geladen wurde. + * Die DeviceView nimmt die ValueList dann in Besitz. + */ void BCDeviceView::onValueListReady( BCDevice::ID deviceID, BCValueList valueList ) { qDebug() << " --- onValueListReady: " << deviceID << ": " << valueList.size(); @@ -81,11 +82,28 @@ void BCDeviceView::onValueListReady( BCDevice::ID deviceID, BCValueList valueLis _valueModel.takeValueList( valueList ); } + +/** + * @brief SLOT, der aufgerufen wird, wenn ein Value geändert wurde. Gibt dem ItemDelegate Bescheid. + */ + void BCDeviceView::onValueUpdated(int index, BCValue::State state, const QString& newVisibleValue ) { _valueModel.onValueUpdated( index, state, newVisibleValue); _itemDelegate->onHighlightRow( index ); - } +void BCDeviceView::resizeEvent(QResizeEvent *event) +{ + // Zuerst die Basisklasse aufrufen (Wichtig für Layouts!) + QWidget::resizeEvent(event); + + // Berechnung: 40% der aktuellen Breite + // Tipp: viewport()->width() ist genauer als width(), da es Scrollbars rausrechnet! + int totalWidth = viewport()->width(); + int col0Width = static_cast(totalWidth * 0.60); + + // Setzen der Breite + horizontalHeader()->resizeSection(0, col0Width); +} diff --git a/bcdeviceview.h b/bcdeviceview.h index 7898806..c9f021a 100644 --- a/bcdeviceview.h +++ b/bcdeviceview.h @@ -64,6 +64,8 @@ public slots: protected: + void resizeEvent(QResizeEvent *event) override; + BCDevice::ID _devideID{BCDevice::ID::Invalid}; BCValueModel _valueModel; BCAnimatedDelegate* _itemDelegate{}; diff --git a/bcguihelpers.cpp b/bcguihelpers.cpp index 72bd28a..e6f8b20 100644 --- a/bcguihelpers.cpp +++ b/bcguihelpers.cpp @@ -43,23 +43,25 @@ BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent ) // CSS: Transparent, damit es sich nahtlos in den StatusBar einfügt // Schriftgröße etwas erhöhen, damit die Emojis gut erkennbar sind - /* + setStyleSheet(R"( - QPushButton { + BCThemeSwitchButton + { border: none; - background-color: transparent; + background-color: green; font-size: 11pt; } - QPushButton:hover { - background-color: rgba(128, 128, 128, 30); // Leichter Hover-Effekt + BCThemeSwitchButton:Hover + { + background-color: rgba(128, 128, 128, 30); border-radius: 24px; } )"); - */ + // Initialer Status (Startet im Dark Mode -> zeigt Mond) updateIcon(); - connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggle); + connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggleMode); } diff --git a/bcmainwindow.cpp b/bcmainwindow.cpp index fefc18c..4e551d4 100644 --- a/bcmainwindow.cpp +++ b/bcmainwindow.cpp @@ -52,8 +52,6 @@ BCMainWindow::BCMainWindow(QWidget *parent) setupUi(this); - // den pimp-my-ride-button schalten wir vorerst aus. - _pimpButton->hide(); // Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des // timers in die Event-Queue, damit er erst ausgeführt wird, @@ -111,14 +109,11 @@ void BCMainWindow::initMainWindow() _devicePanels[BCDevice::ID::Console] = _consolePanel; _devicePanels[BCDevice::ID::Battery] = _batteryPanel; _devicePanels[BCDevice::ID::Motor] = _motorPanel; - //_devicePanels[BCDevice::ID::Pimp] = _pimpPanel; // Die actions an die Buttons binden configureAction(_motorButton, _motorAction, BCDevice::ID::Motor ); configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console ); configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery ); - //configureAction(_pimpButton, _pimpAction, BCDevice::ID::Pimp ); - /* bool m_isDarkMode = false; @@ -149,10 +144,11 @@ void BCMainWindow::initMainWindow() // also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole}); connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleDriverConnection ); connect( _syncButton, &QToolButton::clicked, this, &BCMainWindow::onSyncDeviceView ); - connect( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated ); + connect( _exitButton, &QToolButton::clicked, qApp, &QCoreApplication::quit ); - connect(this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::onEnqueueValue); - connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); + connect( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated ); + connect( this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::onUpdateValue); + connect( &_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged ); // transmitter starten @@ -161,10 +157,16 @@ void BCMainWindow::initMainWindow() // die Daten des eBikes laden _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1); - //_consoleAction->trigger(); - _batteryAction->trigger(); + _consoleAction->trigger(); + //_batteryAction->trigger(); -} + // Dummy sync beim starten + + QTimer::singleShot(1000, this, [this]() + { + onSyncDeviceView(); + }); + } /* // 2. Bild für den Zustand UNCHECKED (Off) hinzufügen @@ -191,15 +193,19 @@ void BCMainWindow::initStatusBar() statBar->addPermanentWidget(themeBtn); connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark) { + QString message = isDark ? "Dark Mode Activated" : "Light Mode Activated"; statusBar()->showMessage( message, 3000); - setApplicationStyleSheet( isDark ? ":/claude_dark_mode.qss"_L1 : ":/claude_light_mode.qss"_L1 ); + setApplicationStyleSheet( isDark ? ":claude_dark_mode.qss"_L1 : ":claude_light_mode.qss"_L1 ); }); // Wir starten im light mode //themeBtn->setDarkMode( false ); statBar->showMessage("Ready"); + + setApplicationStyleSheet(":bionxcontrol.qss"_L1); + } /* @@ -322,7 +328,7 @@ void BCMainWindow::onSyncDeviceView() // wir setzen auf 'lesen' value->state.setFlag( BCValue::State::ReadMe ); - // statt '_transmitter.onEnqueueValue( value )' müssen wir hier + // statt '_transmitter.onUpdateValue( value )' müssen wir hier // über emit requestValueUpdate() zur Thread sysnchronisation // entkoppeln, diff --git a/bcmainwindow.ui b/bcmainwindow.ui index 71e4ac3..c66cb4e 100644 --- a/bcmainwindow.ui +++ b/bcmainwindow.ui @@ -71,9 +71,15 @@ 64 + + true + true + + _buttonGroup + @@ -93,9 +99,15 @@ 64 + + true + true + + _buttonGroup + @@ -115,31 +127,18 @@ 64 - + true - - - - - - - 64 - 64 - - - - ... - - - - 48 - 48 - + + true true + + _buttonGroup + @@ -157,17 +156,78 @@ + + + 64 + 64 + + - Sync + + + + + :/sync_yellow.svg:/sync_yellow.svg + + + + 48 + 48 + + + + true - + - - Connect + + + 64 + 64 + - + + + + + + :/connect.svg:/connect.svg + + + + 48 + 48 + + + + true + + + + + + + + 64 + 64 + + + + Quit + + + + :/exit_red.svg:/exit_red.svg + + + + 48 + 48 + + + true @@ -190,7 +250,7 @@ 0 - 3 + 2 @@ -279,61 +339,13 @@ false - - - QFrame::Shape::NoFrame - - - QFrame::Shadow::Plain - - - 0 - - - false - - - Qt::PenStyle::NoPen - - - Pimp my Ride ... - - - false - - - true - - - false - - - - - background-color: #DADADA - - - - - true - - - - :restart.png:restart.png - - - pimp - - - Pimp my Ride - - + @@ -343,7 +355,7 @@ motor - Show motor settings + Motoreinstellungen anzeigen und bearbeiten @@ -355,7 +367,7 @@ battery - Show battery settings + Batterieeinstellungen anzeigen und bearbeiten @@ -367,31 +379,19 @@ console - Show console settings - - - - - - :exit.png:exit.png - - - Exit - - - Exit + Konseleneinstellungen anzeigen und bearbeiten - + :/connected.png:/connected.png connect - connect to bike + TinyCAN native Treiber laden QAction::MenuRole::TextHeuristicRole @@ -409,4 +409,7 @@ + + + diff --git a/bcsliderstyle.cpp b/bcsliderstyle.cpp new file mode 100644 index 0000000..4da62f9 --- /dev/null +++ b/bcsliderstyle.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** + + BionxControl + © 2025 -2026 christoph holzheuer + christoph.holzheuer@gmail.com + + Using: + + mhs_can_drv.c + © 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany + Klaus Demlehner, klaus@mhs-elektronik.de + @see www.mhs-elektronik.de + + Based on Bionx data type descriptions from: + + BigXionFlasher USB V 0.2.4 rev. 97 + © 2011-2013 by Thomas Koenig + @see www.bigxionflasher.org + + Bionx Bike Info + © 2018 Thorsten Schmidt (tschmidt@ts-soft.de) + @see www.ts-soft.de + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + @see https://github.com/bikemike/bionx-bikeinfo + + ***************************************************************************/ + + +#include + diff --git a/bcsliderstyle.h b/bcsliderstyle.h new file mode 100644 index 0000000..0c779f5 --- /dev/null +++ b/bcsliderstyle.h @@ -0,0 +1,269 @@ +/*************************************************************************** + + BionxControl + © 2025 -2026 christoph holzheuer + christoph.holzheuer@gmail.com + + Using: + + mhs_can_drv.c + © 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany + Klaus Demlehner, klaus@mhs-elektronik.de + @see www.mhs-elektronik.de + + Based on Bionx data type descriptions from: + + BigXionFlasher USB V 0.2.4 rev. 97 + © 2011-2013 by Thomas Koenig + @see www.bigxionflasher.org + + Bionx Bike Info + © 2018 Thorsten Schmidt (tschmidt@ts-soft.de) + @see www.ts-soft.de + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + @see https://github.com/bikemike/bionx-bikeinfo + + ***************************************************************************/ + + +#ifndef BCSLIDERSTYLE_H +#define BCSLIDERSTYLE_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Fluent Design Slider Style +class FluentSliderStyle : public QProxyStyle +{ +public: + + FluentSliderStyle() + : QProxyStyle() + {} + + // Wichtig: Genug Platz für Handle reservieren + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override + { + switch (metric) + { + case PM_SliderThickness: + return 32; // Höhe für horizontalen Slider + case PM_SliderLength: + return 20; // Handle-Größe + case PM_SliderControlThickness: + return 20; + case PM_SliderSpaceAvailable: + if (option) + { + if (const QStyleOptionSlider* sliderOpt = qstyleoption_cast(option)) { + if (sliderOpt->orientation == Qt::Horizontal) { + return sliderOpt->rect.width() - 20; + } else { + return sliderOpt->rect.height() - 20; + } + } + } + return QProxyStyle::pixelMetric(metric, option, widget); + + default: + return QProxyStyle::pixelMetric(metric, option, widget); + } + } + + 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 = 20; + + 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 + + if (slider->orientation == Qt::Horizontal) { + drawHorizontalFluentSlider(painter, slider, accentColor, inactiveColor, bgColor); + } else { + drawVerticalFluentSlider(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 = 20; + 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 = 32; + 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 = 8; + 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 drawVerticalFluentSlider(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 grooveWidth = 4; + // Track sollte im Widget-Zentrum sein + int grooveX = slider->rect.center().x() - grooveWidth / 2; + + // Full background track + QRect fullTrack(grooveX, groove.top(), grooveWidth, groove.height()); + painter->setPen(Qt::NoPen); + painter->setBrush(inactiveColor.lighter(150)); + painter->drawRoundedRect(fullTrack, grooveWidth / 2, grooveWidth / 2); + + // Active track + int activeHeight = groove.bottom() - handle.center().y(); + QRect activeTrack(grooveX, handle.center().y(), grooveWidth, activeHeight); + painter->setBrush(activeColor); + painter->drawRoundedRect(activeTrack, grooveWidth / 2, grooveWidth / 2); + + // Handle + int handleSize = 20; + QRect thumbRect(handle.center().x() - handleSize / 2, + handle.center().y() - handleSize / 2, + handleSize, handleSize); + + if (slider->state & State_MouseOver) { + painter->setBrush(QColor(activeColor.red(), activeColor.green(), + activeColor.blue(), 30)); + int glowSize = 32; + QRect glow(handle.center().x() - glowSize / 2, + handle.center().y() - glowSize / 2, + glowSize, glowSize); + painter->drawEllipse(glow); + } + + painter->setBrush(bgColor); + painter->setPen(QPen(activeColor, 2)); + painter->drawEllipse(thumbRect); + + if (slider->state & State_Sunken) { + int innerSize = 8; + QRect inner(handle.center().x() - innerSize / 2, + handle.center().y() - innerSize / 2, + innerSize, innerSize); + painter->setPen(Qt::NoPen); + painter->setBrush(activeColor); + painter->drawEllipse(inner); + } + } +}; + + +#endif // BCSLIDERSTYLE_H diff --git a/bctransmitter.cpp b/bctransmitter.cpp index f1db6f8..a17ed4c 100644 --- a/bctransmitter.cpp +++ b/bctransmitter.cpp @@ -32,6 +32,7 @@ #include #include +#include #include @@ -40,7 +41,7 @@ */ BCTransmitter::BCTransmitter(QObject *parent) - : QObject(parent), _isBusy(false) + : QObject(parent)//, _isBusy(false) { //_canDriver = new BCDriverTinyCan{this}; _canDriver = &_dummyDriver; @@ -56,18 +57,24 @@ BCTransmitter::BCTransmitter(QObject *parent) void BCTransmitter::onToggleDriverConnection( bool connect ) { qDebug() << " --- onToggleDriverConnection: " << connect; - // FIX! Ende der current op abwarten! + emit driverStateChanged(BCDriver::DriverState::Initialized, "BUSY!"); + bc::delay_millis(350); + // kill all pending stuff + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + // FIX! Ende der current op abwarten! BCDriver::DriverState state = connect ? BCDriver::DriverState::DeviceReady : BCDriver::DriverState::NotPresent; const QString& message = connect ? "Trying to connect" : " FAILED"; emit driverStateChanged(state, message); return; + /* // Hier sind wir noch in GUI Thread QMutexLocker locker(&_mutex); // weitere operation stoppen _isBusy = true; connect ? connectCanDriver() : disconnectCanDriver(); _isBusy = false; + */ } @@ -120,98 +127,69 @@ void BCTransmitter::disconnectCanDriver() } -void BCTransmitter::onEnqueueValue( BCValuePtrConst value) +void BCTransmitter::onUpdateValue( BCValuePtrConst valuePtr) { - // wir stellen hier auf die arte Tour sicher, das onEnqueueValue + // wir stellen hier auf die harte Tour sicher, das onUpdateValue // nicht aus dem Parent-Thread direkt sondern über die EventQueue aufgerufen wurde. Q_ASSERT(QThread::currentThread() == this->thread()); - // Hier sind wir noch in GUI Thread - //QMutexLocker locker(&_mutex); - _valueQueue.enqueue( value ); + // Wir arbeiten hier ohne besondere Threadsynchronisation, mutexed o.ä: Die + // entkoppelung und serialisierung passiert bereits durch die Qt-Eventqueue. + // Das klappt aber nur in der hier gewählten Konstellation mit einer Parent-Thread + // und einem Worker. - // wir wollen nicht den ganzen Value verschicken, erstrecht - // wollen wir nicht den Value in verschiedenen Threads gleichzeitig - // in die Hand nehmen, also hantieren wir nur mit den Inidizes. + // Kosmetik + const BCValue& value = *(valuePtr.get()); + + qDebug() << "------- DE.-.QUEUE: " << QThread::currentThreadId() << ": " << value.label; + + // Value ist 'under construction' + //emit valueUpdated( value.deviceID, value.indexRow, BCValue::State::Locked ); + + + uint32_t devID = static_cast(value.deviceID); + uint8_t regID = static_cast (value.registerID); + + QString newVisibleValue; + BCValue::State newState = BCValue::State::NoState; + + if(value.state.testFlag( BCValue::State::WriteMe ) ) + { + + + } + // oder sollen wir hier beides erlauben ? readFlag & writeFlag ? + // Was kommt dann zuerst? Schreiben und lesen als verify ? + + else if( value.state.testFlag( BCValue::State::ReadMe ) ) + { + // wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen + TransmitResult result = value.isWord ? readWordValue( devID, regID ) : readByteValue( devID, regID ); + if( result.has_value() ) + { + // quark! das gehört hier nicht hin! + newVisibleValue = value.formatValues( result.value() ); + newState = BCValue::State::InSync; + } + else + { + newState = BCValue::State::Failed; + } + } + + emit valueUpdated( value.deviceID, value.indexRow, newState, newVisibleValue ); + + // __fix + //bc::processEventsFor(150); + bc::delay_millis(50); - // Trigger processing im Event-Loop des Worker Threads - // invokeMethod mit QueuedConnection entkoppelt den Aufruf, - // damit onEnqueueValue sofort zurückkehrt (non-blocking für den Aufrufer). - //QMetaObject::invokeMethod(this, "onProcessValue", Qt::QueuedConnection); - onProcessValue(); } void BCTransmitter::onProcessValue() -{ - - //if (_isBusy) - // return; - - _isBusy = true; - - while (true) - { - BCValuePtrConst valuePtr{}; - { - //QMutexLocker locker(&_mutex); - if (_valueQueue.isEmpty()) - { - _isBusy = false; - break; // Schleife verlassen, warten auf neue Events - } - valuePtr =_valueQueue.dequeue(); - } // Mutex wird hier freigegeben! WICHTIG: Execute ohne Lock! - - // Kosmetik - const BCValue& value = *(valuePtr.get()); - - qDebug() << "------- DE.-.QUEUE: " << QThread::currentThreadId() << ": " << value.label; - - // Value ist 'under construction' - //emit valueUpdated( value.deviceID, value.indexRow, BCValue::State::Locked ); - - - uint32_t devID = static_cast(value.deviceID); - uint8_t regID = static_cast (value.registerID); - - QString newVisibleValue; - BCValue::State newState = BCValue::State::NoState; - - if(value.state.testFlag( BCValue::State::WriteMe ) ) - { - - - } - // oder sollen wir hier beides erlauben ? readFlag & writeFlag ? - // Was kommt dann zuerst? Schreiben und lesen als verify ? - - else if( value.state.testFlag( BCValue::State::ReadMe ) ) - { - // wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen - TransmitResult result = value.isWord ? readWordValue( devID, regID ) : readByteValue( devID, regID ); - if( result.has_value() ) - { - newVisibleValue = value.formatValue( result.value() ); - newState = BCValue::State::InSync; - } - else - { - newState = BCValue::State::Failed; - } - } - - emit valueUpdated( value.deviceID, value.indexRow, newState, newVisibleValue ); - - // __fix - //bc::processEventsFor(150); - bc::delay_millis(150); - - } -} - +{} TransmitResult BCTransmitter::readByteValue( uint32_t deviceID, uint8_t registerID ) { diff --git a/bctransmitter.h b/bctransmitter.h index 8eb0265..5f414f6 100644 --- a/bctransmitter.h +++ b/bctransmitter.h @@ -63,7 +63,7 @@ public: public slots: void onToggleDriverConnection( bool connect ); - void onEnqueueValue(BCValuePtrConst value ); + void onUpdateValue(BCValuePtrConst valuePtr ); void onProcessValue(); void onStartNativeDriver(); @@ -80,11 +80,11 @@ private: TransmitResult readByteValue( uint32_t deviceID, uint8_t registerID ); TransmitResult readWordValue( uint32_t deviceID, uint8_t registerID ); - using BCDataQueue = QQueue; + //using BCDataQueue = QQueue; - BCDataQueue _valueQueue; - QMutex _mutex; - std::atomic _isBusy{ false }; + //BCDataQueue _valueQueue; + //QMutex _mutex; + //std::atomic _isBusy{ false }; // __fix! BCDriver* _canDriver{}; diff --git a/bcvalue.cpp b/bcvalue.cpp index e8e6075..12bcb4a 100644 --- a/bcvalue.cpp +++ b/bcvalue.cpp @@ -44,7 +44,7 @@ BCValue::BCValue( BCDevice::ID deviceID_, BC::ID registerID_) visibleValue = "--"; } -QString BCValue::formatValue( uint32_t value ) const +QString BCValue::formatValues( uint32_t value ) const { if( factor == 1 ) return QString::number( value ); diff --git a/bcvalue.h b/bcvalue.h index 1fb293b..118f563 100644 --- a/bcvalue.h +++ b/bcvalue.h @@ -93,7 +93,7 @@ public: BCValue( BCDevice::ID deviceID_, BC::ID registerID_ ); - QString formatValue( uint32_t value ) const; + QString formatValues( uint32_t value ) const; void dumpValue() const; mutable States state{BCValue::State::ReadOnly}; @@ -103,6 +103,7 @@ public: int indexRow{-1}; QString label; mutable QString visibleValue; + mutable double rawValue; bool isWord{false}; QString unitLabel; double factor{1}; diff --git a/bcvaluemodel.cpp b/bcvaluemodel.cpp index 5079ce9..2d9b78e 100644 --- a/bcvaluemodel.cpp +++ b/bcvaluemodel.cpp @@ -85,7 +85,7 @@ void BCValueModel::onValueUpdated( int row, BCValue::State state, const QString& { if( row > -1 && row < _valueList.size() ) { - BCValue& value = *(_valueList[row].get()); + const BCValue& value = *(_valueList[row].get()); QModelIndex idx = index(row,1); //qDebug(); @@ -132,7 +132,7 @@ int BCValueModel::columnCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; - return 2; + return 3; } @@ -191,7 +191,8 @@ Das Model Gibt hier, unabhängig von der DataRole, immer das Qt::ItemFlags BCValueModel::flags(const QModelIndex& index) const { - if (!index.isValid()) + // die label spalte lässt sich nicht editieren + if (!index.isValid() || index.column() == 0 ) return Qt::NoItemFlags; return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; diff --git a/bcxmlloader.cpp b/bcxmlloader.cpp index c1a8204..fabd87f 100644 --- a/bcxmlloader.cpp +++ b/bcxmlloader.cpp @@ -59,11 +59,7 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) } qDebug().noquote() << parts.join(" "); }; - /* - QString fileName = QFileDialog::getOpenFileName(this, "XML öffnen", "", "XML Files (*.xml)"); - if (fileName.isEmpty()) - return; - */ + QMetaEnum bcDeviceEnum{QMetaEnum::fromType()}; @@ -92,7 +88,7 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) if (token == QXmlStreamReader::StartElement) { QString deviceType = _xml.attributes().value("Type"_L1).toString(); - printAttrs (_xml); + //printAttrs (_xml); // Wir wollen die Device-ID aus dem XML Tag ermitteln const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData(); bool ok; @@ -109,22 +105,13 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) } } - - /* - if (xml.hasError()) - { - QMessageBox::critical(nullptr, "Parsing Fehler", xml.errorString()); - } - else - { - m_model->setDevices(parsedValues); - } - */ - - // create & add new model to the model map - } +/** + * @brief Lädt deie Daten des BionX eBikes + * @param deviceID + */ + void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID) { auto printAttrs = [](const QXmlStreamReader& xml) @@ -138,7 +125,7 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID) printAttrs (_xml); Q_ASSERT(_xml.isStartElement() && _xml.name() == "Device"_L1); - qDebug() << " XXX ---------------"; + // temporäre Wertliste für neues Device BCValueList currentValues; diff --git a/bionxcontrol.qrc b/bionxcontrol.qrc index d5e1960..758bfc4 100644 --- a/bionxcontrol.qrc +++ b/bionxcontrol.qrc @@ -7,15 +7,12 @@ resources/bionx_akku.png resources/bionx_console.png resources/bionx_motor.png - resources/exit.png - resources/important.png - resources/restart.png - resources/splash.png - resources/connect.png - resources/connected.png - resources/disconnected.png - resources/connect_white.svg - resources/sync_green.svg - resources/sync_yellow.svg + resources/connect.svg + resources/exit.svg + resources/exit_red.svg + resources/sync_green.svg + resources/sync_yellow.svg + resources/connect_white.svg + resources/sync.svg diff --git a/doc/Challenges.docx b/doc/Challenges.docx deleted file mode 100644 index eface7b..0000000 Binary files a/doc/Challenges.docx and /dev/null differ diff --git a/doc/Install.on.pi.txt b/doc/Install.on.pi.txt index 14b4970..d367b4b 100644 --- a/doc/Install.on.pi.txt +++ b/doc/Install.on.pi.txt @@ -27,6 +27,43 @@ sudo apt purge modemmanager // --- +sudo apt install \ + qt6-declarative-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ + qt6-l10n-tools \ + qt6-scxml-dev \ + qt6-serialport-dev \ + qt6-serialbus-dev \ + qt6-connectivity-dev \ + qt6-sensors-dev \ + qt6-websockets-dev \ + qt6-webchannel-dev \ + qt6-svg-dev \ + qt6-multimedia-dev \ + qt6-charts-dev \ + qt6-wayland \ + qml6-module-qtquick-controls \ + qml6-module-qtquick-layouts \ + qml6-module-qtquick-templates \ + qml6-module-qtquick-window \ + qml6-module-qtqml-workerscript \ + qml6-module-qtcharts \ + qml6-module-qt-labs-platform \ + libqt6sql6-mysql libqt6sql6-psql libqt6sql6-sqlite \ + libqt6opengl6-dev + + sudo apt update +sudo apt install \ + qtcreator \ + qtcreator-doc \ + cmake \ + ninja-build \ + gdb \ + clang \ + clang-format + + https://github.com/MHS-Elektronik/OBD-Display sudo chgrp pi /opt @@ -36,6 +73,7 @@ https://github.com/MHS-Elektronik/OBD-Display tar -xzvf tiny_can_raspberry_790.tar.gz rm tiny_can_raspberry_790.tar.gz +sudo apt install fonts-open-sans RANT diff --git a/doc/bewerb/Anschreiben.Weining.docx b/doc/bewerb/Anschreiben.Weining.docx deleted file mode 100644 index e481afc..0000000 Binary files a/doc/bewerb/Anschreiben.Weining.docx and /dev/null differ diff --git a/doc/bewerb/Anschreiben.alt.docx b/doc/bewerb/Anschreiben.alt.docx deleted file mode 100644 index 19fce40..0000000 Binary files a/doc/bewerb/Anschreiben.alt.docx and /dev/null differ diff --git a/doc/bewerb/Anschreiben.txt b/doc/bewerb/Anschreiben.txt deleted file mode 100644 index 46ca802..0000000 --- a/doc/bewerb/Anschreiben.txt +++ /dev/null @@ -1,30 +0,0 @@ - - -Sehr geehrte Frau Spaag, - -Aktuell bin ich als Softwareentwickler im Bereich C++/Qt fest angestellt, suche jedoch -nach neuen Entwicklungsmöglichkeiten. - -Ihre Stellenanzeige hat mich sofort angesprochen, denn das beschriebene Anforderungsprofil -harmonisiert sehr gut mit meiner derzeitigen Tätigkeit im Bereich Datenvisualisierung und - -blub - -Ich freu mich wie woschd und verbeibe mit freundlichen Grüßen - -Christoph Holzheuer - - -// ---------- - -znode ist eine header-only Klasse in modern c++ für generische Baumstrukturen -In der derzeitigen Ausführung dient sie mit Hilfe des pugixml Parser zur Verwaltung -von XML Daten innerhalb des xtree-Projekts. - -Die Knoten sind std::c++, die Verwaltung der Attribs ist drangeebrt und somit variable. - -Besonderheit ist die Übergabe das String types als template parameter: In Xtree instanzierung durch QString, -andere durch std::string bzw. std::w_string ohne Dependencies zur Qt-Lib eingehen zu müssen. -bridge-code - -// ---------- diff --git a/doc/bewerb/CV_C_Holzheuer.docx b/doc/bewerb/CV_C_Holzheuer.docx deleted file mode 100644 index 23163f5..0000000 Binary files a/doc/bewerb/CV_C_Holzheuer.docx and /dev/null differ diff --git a/doc/bewerb/CV_C_Holzheuer.pdf b/doc/bewerb/CV_C_Holzheuer.pdf deleted file mode 100644 index 7dd9428..0000000 Binary files a/doc/bewerb/CV_C_Holzheuer.pdf and /dev/null differ diff --git a/doc/bewerb/Diplom_Holzheuer.pdf b/doc/bewerb/Diplom_Holzheuer.pdf deleted file mode 100644 index 045de55..0000000 Binary files a/doc/bewerb/Diplom_Holzheuer.pdf and /dev/null differ diff --git a/doc/bewerb/Foto_C_Holzheuer.jpg b/doc/bewerb/Foto_C_Holzheuer.jpg deleted file mode 100644 index 6d1ff68..0000000 Binary files a/doc/bewerb/Foto_C_Holzheuer.jpg and /dev/null differ diff --git a/doc/bewerb/Profil_C_Holzheuer.docx b/doc/bewerb/Profil_C_Holzheuer.docx deleted file mode 100644 index 8764225..0000000 Binary files a/doc/bewerb/Profil_C_Holzheuer.docx and /dev/null differ diff --git a/doc/bewerb/Profil_C_Holzheuer.pdf b/doc/bewerb/Profil_C_Holzheuer.pdf deleted file mode 100644 index 8cd069a..0000000 Binary files a/doc/bewerb/Profil_C_Holzheuer.pdf and /dev/null differ diff --git a/doc/challenges.txt b/doc/challenges.txt deleted file mode 100644 index 4728ed1..0000000 --- a/doc/challenges.txt +++ /dev/null @@ -1,63 +0,0 @@ -Challenges - - -------------------------------------------------------------------------------------------------- - -BionxControl - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- - - -------------------------------------------------------------------------------------------------- - -xtree - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- - -znode - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- - -libPigPio - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- - -supportware - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- - -raDIYo - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- - -miniCash.connect - -Aufgabe: - -Ansatz: - -------------------------------------------------------------------------------------------------- diff --git a/resources/TinyCan_812.exe b/resources/TinyCan_812.exe deleted file mode 100644 index 72941d6..0000000 Binary files a/resources/TinyCan_812.exe and /dev/null differ diff --git a/resources/bikeinfo.xml b/resources/bikeinfo.xml index abb0327..b3b845c 100644 --- a/resources/bikeinfo.xml +++ b/resources/bikeinfo.xml @@ -30,7 +30,7 @@ - +