/*************************************************************************** BionxControl Copyright © 2025 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 #include "qassert.h" #include #include #include /** * @brief Das Mainwindow erzeugen * @param parent Das Elternwidget */ BCMainWindow::BCMainWindow(QWidget *parent) : QMainWindow(parent) { // __fix! in der Form nötig? qRegisterMetaType("BCValue"); qRegisterMetaType>("BCValueList"); setupUi(this); // Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des // timers in die Event-Queue, damit er erst ausgeführt wird, // wenn das Fenster sichtbar ist. QTimer::singleShot(0, this, [this]() { initMainWindow(); }); } /** * @brief Destruktor. Räumt den Workthread auf. */ BCMainWindow::~BCMainWindow() { _worker.quit(); // Event Loop stoppen _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. */ 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 ); }); 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 ); } }; // 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; // 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; 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"); fitzeButton->setStyleSheet(style); // 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 ); _transmitter.moveToThread(&_worker); connect(this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::enqueueValue); connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); _worker.start(); // die Daten des eBikes laden _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1); _consoleAction->trigger(); // --- STATUSBAR SETUP --- QStatusBar *statBar = statusBar(); // Optional: Normale Nachricht links statBar->showMessage("Ready"); // 1. Unseren Switcher erstellen ThemeSwitchButton *themeBtn = new ThemeSwitchButton(this); // 2. WICHTIG: Rechts hinzufügen statBar->addPermanentWidget(themeBtn); // 3. Signal verbinden: Button klick -> Theme ändern connect(themeBtn, &ThemeSwitchButton::themeChanged, this, [this](bool isDark){ if (isDark) { //applyFluentDarkTheme(*qApp); // Funktion von vorhin statusBar()->showMessage("Dark Mode Activated", 3000); } else { //applyFluentLightTheme(*qApp); // Funktion von vorhin statusBar()->showMessage("Light Mode Activated", 3000); } }); } void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID ) { qDebug() << " --- onShowDevicePanel:" << deviceID; if( _devicePanels.contains( deviceID ) ) { BCDeviceView* nxtPanel = _devicePanels[deviceID]; if( nxtPanel != _currentPanel ) { _currentPanel = nxtPanel; qDebug() << " --- Firz: " << _currentPanel->property( BCKeyHeaderLabel ); setHeaderLabel( _currentPanel->property( BCKeyHeaderLabel ).toString() ); _stackedWidget->setCurrentWidget( nxtPanel ); // knopf auch abschalten? } } } void BCMainWindow::onConnectButtonToggled(bool checked ) { //_dataManager.setDriverConnectionState( checked ); } void BCMainWindow::onValueUpdated(BCDevice::ID deviceID, int index, BCValue::State state, const QString& newValue ) { qDebug() << "Reply: from: " << deviceID << " at: " << index << "finished. Success:" << (uint8_t)state << " on:" << newValue; if( _devicePanels.contains( deviceID ) ) { BCDeviceView& panel = *_devicePanels[deviceID]; panel.onValueUpdated( index, state, newValue ); } } /** * @brief SLOT, der aufgerufen wird, um das akutelle Device (Battery, Motor, ... ) * zu synchronisieren, d.h. die aktuellen Werte über den CAN-Bus abzufragen. */ void BCMainWindow::onSyncDeviceView() { Q_ASSERT_X(_currentPanel, "onSyncDeviceView()", "_currentpanel ist null!"); qDebug() << " ---Syncing"; const BCValueList& currentList =_currentPanel->getValueListX(); // alle einzeln? echt jetzt? for( const BCValuePtr& value : currentList ) { qDebug() << " --- begin sync of value: " << value->label; // wir setzen auf 'lesen' value->state.setFlag( BCValue::State::ReadMe ); // statt '_transmitter.enqueueValueCommand( value )' entkoppeln // wir das eleganter über emit requestValueUpdate() emit requestValueUpdate( value); } }