From 4545bf81c328fa3cb30c339208303612ad01d2c1 Mon Sep 17 00:00:00 2001 From: "DIASPORA\\chris" Date: Sat, 3 Jan 2026 00:09:26 +0100 Subject: [PATCH] Visual Cleanups. --- bcmainwindow.cpp | 99 ++++++++++++++++---------------- bcvaluetype.cpp | 41 ++++--------- bcvaluetype.h | 5 +- bcxmlloader.cpp | 10 ---- doc/BigXionFlasher USB V 0.docx | Bin 0 -> 15796 bytes resources/bikeinfo.xml | 61 +++++++++----------- 6 files changed, 91 insertions(+), 125 deletions(-) create mode 100644 doc/BigXionFlasher USB V 0.docx diff --git a/bcmainwindow.cpp b/bcmainwindow.cpp index 568f27f..5564c50 100644 --- a/bcmainwindow.cpp +++ b/bcmainwindow.cpp @@ -52,6 +52,9 @@ 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, // wenn das Fenster sichtbar ist. @@ -74,17 +77,6 @@ BCMainWindow::~BCMainWindow() _worker.wait(); // Warten bis Thread wirklich fertig ist } - -/** - * @brief Setzt den Headerlabel ( == die Device-Bezeichnung ) - * @param headerLabel Der headerLabel - */ - -void BCMainWindow::setHeaderLabel( const QString& headerText) -{ - _headerLabel->setText( " BionxControl: " + headerText ); -} - /** * @brief Initialisiert alle Komponenten des MainWindows. */ @@ -95,63 +87,62 @@ void BCMainWindow::initMainWindow() // Lambda um die buttons mit ihren Actions zu verbinden auto configureAction = [&]( QToolButton* button, QAction* action, BCDevice::ID deviceID ) { - // Action an den Button binden button->setDefaultAction( action); - // new way: die DeviceID muss aber explizit vom Lambda eingefanden werden. connect( action, &QAction::triggered, this, [this,deviceID]() - { - onShowDevicePanel( deviceID ); - }); + { + onShowDevicePanel( deviceID ); + }); if( _devicePanels.contains(deviceID) ) - { - BCDeviceView* currentPanel = _devicePanels[deviceID]; - // ... und ihre device ID - currentPanel->setDeviceID( deviceID ); - // Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde, - // wird es weitergereicht. - // Problem: alle Panels bekommen alle Datenmodelle angeboten. - connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady ); - } + { + BCDeviceView* currentPanel = _devicePanels[deviceID]; + // ... und ihre device ID + currentPanel->setDeviceID( deviceID ); + // Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde, + // wird es weitergereicht. + // Problem: alle Panels bekommen alle Datenmodelle angeboten. + connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady ); + } }; // Wir wollen die Devices den Views zuordnen können _devicePanels[BCDevice::ID::Console] = _consolePanel; _devicePanels[BCDevice::ID::Battery] = _batteryPanel; _devicePanels[BCDevice::ID::Motor] = _motorPanel; - _devicePanels[BCDevice::ID::Pimp] = _pimpPanel; + //_devicePanels[BCDevice::ID::Pimp] = _pimpPanel; - // Die actions an die Buttons binden + // 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 ); + //configureAction(_pimpButton, _pimpAction, BCDevice::ID::Pimp ); + /* bool m_isDarkMode = false; QString icon = m_isDarkMode ? "☀️" : "🌙"; fitzeButton->setText(icon); QString style = QString( - "QPushButton {" - " background-color: %1;" - " border: 1px solid %2;" - " border-radius: 6px;" - " font-size: 12pt;" - " padding: 0px;" - "}" - "QPushButton:hover {" - " background-color: %3;" - "}" - ).arg(m_isDarkMode ? "#2B2B2B" : "#FFFFFF") - .arg(m_isDarkMode ? "#3F3F3F" : "#E1DFDD") - .arg(m_isDarkMode ? "#3A3A3A" : "#F9F9F9"); + "QPushButton {" + " background-color: %1;" + " border: 1px solid %2;" + " border-radius: 6px;" + " font-size: 12pt;" + " padding: 0px;" + "}" + "QPushButton:hover {" + " background-color: %3;" + "}" + ).arg(m_isDarkMode ? "#2B2B2B" : "#FFFFFF") + .arg(m_isDarkMode ? "#3F3F3F" : "#E1DFDD") + .arg(m_isDarkMode ? "#3A3A3A" : "#F9F9F9"); fitzeButton->setStyleSheet(style); - - // besser: model::emit dataChanged - // also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole}); + */ + // besser: model::emit dataChanged + // also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole}); connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleConnectionState ); connect( _syncButton, &QToolButton::clicked, this, &BCMainWindow::onSyncDeviceView ); connect( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated ); @@ -161,18 +152,19 @@ void BCMainWindow::initMainWindow() connect(this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::enqueueValue); connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); + connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged ); + // transmitter starten _worker.start(); // die Daten des eBikes laden _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1); - _consoleAction->trigger(); - - connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged ); - + //_consoleAction->trigger(); + _batteryAction->trigger(); } + void BCMainWindow::initStatusBar() { // __fix @@ -203,6 +195,17 @@ void BCMainWindow::initStatusBar() } +/** + * @brief Setzt den Headerlabel ( == die Device-Bezeichnung ) + * @param headerLabel Der headerLabel + */ + +void BCMainWindow::setHeaderLabel( const QString& headerText) +{ + _headerLabel->setText( " BionxControl: " + headerText ); +} + + void BCMainWindow::autoConnect() { // __fix! diff --git a/bcvaluetype.cpp b/bcvaluetype.cpp index 6ebf01f..5a3efdf 100644 --- a/bcvaluetype.cpp +++ b/bcvaluetype.cpp @@ -102,6 +102,7 @@ BCValueType::BCValueType() } + BCValueType::BCValueType(QString unitKey_, QString unitLabel_, double factor_, optDouble min_, optDouble max_ ) : unitLabel{unitLabel_}, factor{factor_}, min{min_}, max{max_} { @@ -110,28 +111,6 @@ BCValueType::BCValueType(QString unitKey_, QString unitLabel_, double factor_, o } -QString BCValueType::readRawValueX(const BCAbstractTransmitter& transmitter , const BCValue& value) const -{ - qDebug() << " --- READ X!"; - /* - uint32_t devID = static_cast(deviceID); - uint8_t regID = static_cast (registerID); - - // wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen - if( valueType->readValueFunc ) - { - uint32_t result = valueType->readValueFunc( transmitter, devID, regID ); - return valueType->formatValue( result ); - } - */ - return QString(); -} - -void BCValueType::writeRawValueX( const BCAbstractTransmitter& transmitter, const BCValue& value ) const -{ - qDebug() << " --- WRITE X!"; -} - QString BCValueType::formatValue( uint32_t value ) const { if( factor == 1 ) @@ -147,17 +126,20 @@ std::optional BCValueType::fetchReadValueFunction( const QString& { { "Byte", readByteValue }, { "Word", readWordValue }, + { "Assist", readByteValue }, + + { "Kmh", readByteValue }, { "Percent",readByteValue }, { "KWh", readByteValue }, { "Watt", readByteValue }, { "Km", readByteValue }, - { "Kmh", readByteValue }, + { "Mm", readByteValue }, { "Sec", readByteValue }, { "Degree", readByteValue }, { "SoC", readByteValue }, { "Odo", readByteValue }, - { "Assist", readByteValue } + }; if( !s_bcReadValueFunctions.contains( unitTypeKey ) ) @@ -170,11 +152,12 @@ std::optional BCValueType::fetchValueType( const QString& unitType { static QHash s_bcDataTypes { - { "Byte", new BCValueType( "Byte", "", 1.5625F) }, - { "Word", new BCValueType( "Word", "", 1.5625F) } + { "Byte", new BCValueType( "Byte", "", 1.5625F) }, + { "Word", new BCValueType( "Word", "", 1.5625F) }, + { "Percent", new BCValueType( "Byte", "%", 1.5625 ) }, + { "AssInit", new BCValueType( "Byte", "", 1.0, 0 ,4 ) }, + { "Assist", new BCValueType( "Byte", "%", 0,400 ) } /* - { "Float", new BCValueType( "", 1.5625F) }, - { "Percent",new BCValueType( "%", 1.5625 ) }, { "KWh", new BCValueType( "kwh", 1.5625 ) }, { "Watt", new BCValueType( "w", 1.5625 ) }, { "Km", new BCValueType( "km", 1.5625 ) }, @@ -184,7 +167,7 @@ std::optional BCValueType::fetchValueType( const QString& unitType { "Degree", new BCValueType( "°C", 1.0 ) }, { "SoC", new BCValueType( "%", 1.5625 ) }, { "Odo", new BCValueType( "km", 1.5625 ) }, - { "Assist", new BCValueType( "", 0 ,4 ) }, + { "Assist", new BCValueType( "%" ) }, */ }; diff --git a/bcvaluetype.h b/bcvaluetype.h index 3d94faa..b4a5ca3 100644 --- a/bcvaluetype.h +++ b/bcvaluetype.h @@ -76,9 +76,6 @@ public: BCValueType(); BCValueType( QString unitKey_, QString unitLabel_, double factor_= 1.0, optDouble min_=std::nullopt, optDouble max_= std::nullopt ); - QString readRawValueX ( const BCAbstractTransmitter& transmitter, const BCValue& value ) const; - void writeRawValueX( const BCAbstractTransmitter& transmitter, const BCValue& value ) const; - virtual QString formatValue( uint32_t value ) const; QString unitLabel; @@ -86,6 +83,8 @@ public: optDouble min; optDouble max; ReadValueFunc readValueFunc; + //ReadValueFunc readValueFunc; + static std::optional fetchValueType( const QString& unitTypeKey ); static std::optional fetchReadValueFunction( const QString& unitTypeKey ); diff --git a/bcxmlloader.cpp b/bcxmlloader.cpp index 0b487ab..3e90034 100644 --- a/bcxmlloader.cpp +++ b/bcxmlloader.cpp @@ -103,16 +103,6 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName ) printAttrs (_xml); // Wir wollen die Device-ID aus dem XML Tag ermitteln const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData(); - /* - auto optDeviceID = bcDeviceEnum.keyToValue64(deviceKey); - //_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) ); - if( optDeviceID.has_value()) - { - qDebug() << " --- Device: " << _xml.name() << ": " << deviceType << " : " << optDeviceID; - BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() ); - loadXmlBikeDeviceData(currentDeviceID); - } - */ bool ok; auto optDeviceID = bcDeviceEnum.keyToValue(deviceKey,&ok); //_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) ); diff --git a/doc/BigXionFlasher USB V 0.docx b/doc/BigXionFlasher USB V 0.docx new file mode 100644 index 0000000000000000000000000000000000000000..749d61c7ee3c9dc8b19f12a0c6c5ea017027b801 GIT binary patch literal 15796 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 literal 0 HcmV?d00001 diff --git a/resources/bikeinfo.xml b/resources/bikeinfo.xml index 43d6e8c..9b55d5f 100644 --- a/resources/bikeinfo.xml +++ b/resources/bikeinfo.xml @@ -3,53 +3,44 @@ - - - - + + + - - - - - + + + + + - - + + - + - + - - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + +