Merge branch 'experimental/gui_cleanup'

This commit is contained in:
2026-01-06 16:32:05 +01:00
56 changed files with 798 additions and 502 deletions

View File

@@ -51,6 +51,7 @@ SOURCES += \
bcdriver.cpp \ bcdriver.cpp \
bcdrivertinycan.cpp \ bcdrivertinycan.cpp \
bcguihelpers.cpp \ bcguihelpers.cpp \
bcsliderstyle.cpp \
bctransmitter.cpp \ bctransmitter.cpp \
bcvalue.cpp \ bcvalue.cpp \
bcvaluemodel.cpp \ bcvaluemodel.cpp \
@@ -68,6 +69,7 @@ HEADERS += \
bcdrivertinycan.h \ bcdrivertinycan.h \
bcguihelpers.h \ bcguihelpers.h \
bcmainwindow.h \ bcmainwindow.h \
bcsliderstyle.h \
bctransmitter.h \ bctransmitter.h \
bcvalue.h \ bcvalue.h \
bcvaluemodel.h \ bcvaluemodel.h \

View File

@@ -181,54 +181,31 @@ QSize BCAnimatedDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
void BCAnimatedDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 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); 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(); int row = index.row();
if (index.column() == 1 ) int col = index.column();
switch (col)
{ {
if( m_rowOpacities.contains(row)) case 1:
{
paintHighlightRow(painter,option,index); 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 void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{ {
painter->save(); painter->save();
@@ -239,33 +216,70 @@ void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOption
// Margin von 4px // Margin von 4px
QRect itemRect = option.rect.adjusted(3, 3, -3, -3); 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) // Border (2px solid #2196F3)
QPen borderPen( Qt::red, 1); QPen borderPen( Qt::red, 1);
painter->setPen(borderPen); painter->setPen(borderPen);
painter->setBrush(Qt::NoBrush); painter->setBrush(Qt::NoBrush);
painter->drawRoundedRect(itemRect, 2, 2); 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(); 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) void BCAnimatedDelegate::onHighlightRow(int row)
{ {

View File

@@ -61,21 +61,6 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex& index) const override;
void paint(QPainter *painter, 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(); void clearAllHighlights();
public slots: public slots:
@@ -91,13 +76,11 @@ private:
void updateRow(int row); void updateRow(int row);
void paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; 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; const BCValueList& _valueList;
QTableView* _view{}; QTableView* _view{};
//int _highlightedRow{-1};
//qreal _opacity{1.0};
QPropertyAnimation* _animation{}; QPropertyAnimation* _animation{};
private: private:

View File

@@ -30,6 +30,8 @@
***************************************************************************/ ***************************************************************************/
#include <QHeaderView>
#include <bcdeviceview.h> #include <bcdeviceview.h>
#include <bcanimateddelegate.h> #include <bcanimateddelegate.h>
@@ -44,18 +46,13 @@ BCDeviceView::BCDeviceView(QWidget *parent)
// __fix! ziemlich wildes ge-pointere, hier // __fix! ziemlich wildes ge-pointere, hier
_itemDelegate = new BCAnimatedDelegate( _valueModel.getValueList(), this); _itemDelegate = new BCAnimatedDelegate( _valueModel.getValueList(), this);
setItemDelegate( _itemDelegate ); setItemDelegate( _itemDelegate );
qDebug() << " --- View size I: " << this->size();
} }
void BCDeviceView::setDeviceID( BCDevice::ID deviceID ) void BCDeviceView::setDeviceID( BCDevice::ID deviceID )
{ {
qDebug() << " --- View size II: " << this->size(); _devideID = deviceID;
_devideID = deviceID;
} }
BCDevice::ID BCDeviceView::getDeviceID() const 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() 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 ) void BCDeviceView::onValueListReady( BCDevice::ID deviceID, BCValueList valueList )
{ {
qDebug() << " --- onValueListReady: " << deviceID << ": " << valueList.size(); qDebug() << " --- onValueListReady: " << deviceID << ": " << valueList.size();
@@ -81,11 +82,28 @@ void BCDeviceView::onValueListReady( BCDevice::ID deviceID, BCValueList valueLis
_valueModel.takeValueList( valueList ); _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 ) void BCDeviceView::onValueUpdated(int index, BCValue::State state, const QString& newVisibleValue )
{ {
_valueModel.onValueUpdated( index, state, newVisibleValue); _valueModel.onValueUpdated( index, state, newVisibleValue);
_itemDelegate->onHighlightRow( index ); _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<int>(totalWidth * 0.60);
// Setzen der Breite
horizontalHeader()->resizeSection(0, col0Width);
}

View File

@@ -64,6 +64,8 @@ public slots:
protected: protected:
void resizeEvent(QResizeEvent *event) override;
BCDevice::ID _devideID{BCDevice::ID::Invalid}; BCDevice::ID _devideID{BCDevice::ID::Invalid};
BCValueModel _valueModel; BCValueModel _valueModel;
BCAnimatedDelegate* _itemDelegate{}; BCAnimatedDelegate* _itemDelegate{};

View File

@@ -43,23 +43,25 @@ BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
// CSS: Transparent, damit es sich nahtlos in den StatusBar einfügt // CSS: Transparent, damit es sich nahtlos in den StatusBar einfügt
// Schriftgröße etwas erhöhen, damit die Emojis gut erkennbar sind // Schriftgröße etwas erhöhen, damit die Emojis gut erkennbar sind
/*
setStyleSheet(R"( setStyleSheet(R"(
QPushButton { BCThemeSwitchButton
{
border: none; border: none;
background-color: transparent; background-color: green;
font-size: 11pt; font-size: 11pt;
} }
QPushButton:hover { BCThemeSwitchButton:Hover
background-color: rgba(128, 128, 128, 30); // Leichter Hover-Effekt {
background-color: rgba(128, 128, 128, 30);
border-radius: 24px; border-radius: 24px;
} }
)"); )");
*/
// Initialer Status (Startet im Dark Mode -> zeigt Mond) // Initialer Status (Startet im Dark Mode -> zeigt Mond)
updateIcon(); updateIcon();
connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggle); connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggleMode);
} }

View File

@@ -52,8 +52,6 @@ BCMainWindow::BCMainWindow(QWidget *parent)
setupUi(this); setupUi(this);
// den pimp-my-ride-button schalten wir vorerst aus.
_pimpButton->hide();
// Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des // Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des
// timers in die Event-Queue, damit er erst ausgeführt wird, // 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::Console] = _consolePanel;
_devicePanels[BCDevice::ID::Battery] = _batteryPanel; _devicePanels[BCDevice::ID::Battery] = _batteryPanel;
_devicePanels[BCDevice::ID::Motor] = _motorPanel; _devicePanels[BCDevice::ID::Motor] = _motorPanel;
//_devicePanels[BCDevice::ID::Pimp] = _pimpPanel;
// Die actions an die Buttons binden // Die actions an die Buttons binden
configureAction(_motorButton, _motorAction, BCDevice::ID::Motor ); configureAction(_motorButton, _motorAction, BCDevice::ID::Motor );
configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console ); configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console );
configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery ); configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery );
//configureAction(_pimpButton, _pimpAction, BCDevice::ID::Pimp );
/* /*
bool m_isDarkMode = false; bool m_isDarkMode = false;
@@ -149,10 +144,11 @@ void BCMainWindow::initMainWindow()
// also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole}); // also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleDriverConnection ); connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleDriverConnection );
connect( _syncButton, &QToolButton::clicked, this, &BCMainWindow::onSyncDeviceView ); 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( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated );
connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); connect( this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::onUpdateValue);
connect( &_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged ); connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged );
// transmitter starten // transmitter starten
@@ -161,10 +157,16 @@ void BCMainWindow::initMainWindow()
// die Daten des eBikes laden // die Daten des eBikes laden
_dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1); _dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);
//_consoleAction->trigger(); _consoleAction->trigger();
_batteryAction->trigger(); //_batteryAction->trigger();
} // Dummy sync beim starten
QTimer::singleShot(1000, this, [this]()
{
onSyncDeviceView();
});
}
/* /*
// 2. Bild für den Zustand UNCHECKED (Off) hinzufügen // 2. Bild für den Zustand UNCHECKED (Off) hinzufügen
@@ -191,15 +193,19 @@ void BCMainWindow::initStatusBar()
statBar->addPermanentWidget(themeBtn); statBar->addPermanentWidget(themeBtn);
connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark) connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark)
{ {
QString message = isDark ? "Dark Mode Activated" : "Light Mode Activated"; QString message = isDark ? "Dark Mode Activated" : "Light Mode Activated";
statusBar()->showMessage( message, 3000); 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 // Wir starten im light mode
//themeBtn->setDarkMode( false ); //themeBtn->setDarkMode( false );
statBar->showMessage("Ready"); statBar->showMessage("Ready");
setApplicationStyleSheet(":bionxcontrol.qss"_L1);
} }
/* /*
@@ -322,7 +328,7 @@ void BCMainWindow::onSyncDeviceView()
// wir setzen auf 'lesen' // wir setzen auf 'lesen'
value->state.setFlag( BCValue::State::ReadMe ); 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 // über emit requestValueUpdate() zur Thread sysnchronisation
// entkoppeln, // entkoppeln,

View File

@@ -71,9 +71,15 @@
<height>64</height> <height>64</height>
</size> </size>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise"> <property name="autoRaise">
<bool>true</bool> <bool>true</bool>
</property> </property>
<attribute name="buttonGroup">
<string notr="true">_buttonGroup</string>
</attribute>
</widget> </widget>
</item> </item>
<item alignment="Qt::AlignmentFlag::AlignHCenter"> <item alignment="Qt::AlignmentFlag::AlignHCenter">
@@ -93,9 +99,15 @@
<height>64</height> <height>64</height>
</size> </size>
</property> </property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise"> <property name="autoRaise">
<bool>true</bool> <bool>true</bool>
</property> </property>
<attribute name="buttonGroup">
<string notr="true">_buttonGroup</string>
</attribute>
</widget> </widget>
</item> </item>
<item alignment="Qt::AlignmentFlag::AlignHCenter"> <item alignment="Qt::AlignmentFlag::AlignHCenter">
@@ -115,31 +127,18 @@
<height>64</height> <height>64</height>
</size> </size>
</property> </property>
<property name="autoRaise"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> <property name="checked">
</item> <bool>true</bool>
<item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QToolButton" name="_pimpButton">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property> </property>
<property name="autoRaise"> <property name="autoRaise">
<bool>true</bool> <bool>true</bool>
</property> </property>
<attribute name="buttonGroup">
<string notr="true">_buttonGroup</string>
</attribute>
</widget> </widget>
</item> </item>
<item> <item>
@@ -157,17 +156,78 @@
</item> </item>
<item alignment="Qt::AlignmentFlag::AlignHCenter"> <item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QToolButton" name="_syncButton"> <widget class="QToolButton" name="_syncButton">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text"> <property name="text">
<string>Sync</string> <string/>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/sync_yellow.svg</normaloff>:/sync_yellow.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QToolButton" name="_connectButton"> <widget class="QToolButton" name="_connectButton">
<property name="text"> <property name="minimumSize">
<string>Connect</string> <size>
<width>64</width>
<height>64</height>
</size>
</property> </property>
<property name="checkable"> <property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/connect.svg</normaloff>:/connect.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QToolButton" name="_exitButton">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string>Quit</string>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/exit_red.svg</normaloff>:/exit_red.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
@@ -190,7 +250,7 @@
<number>0</number> <number>0</number>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>3</number> <number>2</number>
</property> </property>
<widget class="BCDeviceView" name="_consolePanel"> <widget class="BCDeviceView" name="_consolePanel">
<property name="frameShape"> <property name="frameShape">
@@ -279,61 +339,13 @@
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
</widget> </widget>
<widget class="BCDeviceView" name="_pimpPanel">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="gridStyle">
<enum>Qt::PenStyle::NoPen</enum>
</property>
<property name="BCHeaderLabel" stdset="0">
<string>Pimp my Ride ...</string>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QStatusBar" name="_statusbar"> <widget class="QStatusBar" name="_statusbar"/>
<property name="styleSheet">
<string notr="true">background-color: #DADADA</string>
</property>
</widget>
<action name="_pimpAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:restart.png</normaloff>:restart.png</iconset>
</property>
<property name="text">
<string>pimp</string>
</property>
<property name="toolTip">
<string>Pimp my Ride</string>
</property>
</action>
<action name="_motorAction"> <action name="_motorAction">
<property name="icon"> <property name="icon">
<iconset> <iconset>
@@ -343,7 +355,7 @@
<string>motor</string> <string>motor</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Show motor settings</string> <string>Motoreinstellungen anzeigen und bearbeiten</string>
</property> </property>
</action> </action>
<action name="_batteryAction"> <action name="_batteryAction">
@@ -355,7 +367,7 @@
<string>battery</string> <string>battery</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Show battery settings</string> <string>Batterieeinstellungen anzeigen und bearbeiten</string>
</property> </property>
</action> </action>
<action name="_consoleAction"> <action name="_consoleAction">
@@ -367,31 +379,19 @@
<string>console</string> <string>console</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Show console settings</string> <string>Konseleneinstellungen anzeigen und bearbeiten</string>
</property>
</action>
<action name="_exitAction">
<property name="icon">
<iconset>
<normaloff>:exit.png</normaloff>:exit.png</iconset>
</property>
<property name="text">
<string>Exit</string>
</property>
<property name="toolTip">
<string>Exit</string>
</property> </property>
</action> </action>
<action name="_connectAction"> <action name="_connectAction">
<property name="icon"> <property name="icon">
<iconset resource="bionxcontrol.qrc"> <iconset>
<normaloff>:/connected.png</normaloff>:/connected.png</iconset> <normaloff>:/connected.png</normaloff>:/connected.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>connect</string> <string>connect</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>connect to bike</string> <string>TinyCAN native Treiber laden</string>
</property> </property>
<property name="menuRole"> <property name="menuRole">
<enum>QAction::MenuRole::TextHeuristicRole</enum> <enum>QAction::MenuRole::TextHeuristicRole</enum>
@@ -409,4 +409,7 @@
<include location="bionxcontrol.qrc"/> <include location="bionxcontrol.qrc"/>
</resources> </resources>
<connections/> <connections/>
<buttongroups>
<buttongroup name="_buttonGroup"/>
</buttongroups>
</ui> </ui>

34
bcsliderstyle.cpp Normal file
View File

@@ -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 <info@bigxionflasher.org>
@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 <bcsliderstyle.h>

269
bcsliderstyle.h Normal file
View File

@@ -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 <info@bigxionflasher.org>
@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 <QStyledItemDelegate>
#include <QApplication>
#include <QWidget>
#include <QTableView>
#include <QStandardItemModel>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QSlider>
#include <QPainter>
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QLineEdit>
#include <QSlider>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QProxyStyle>
#include <QPainter>
#include <QStyleOptionSlider>
#include <QGraphicsDropShadowEffect>
// 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<const QStyleOptionSlider*>(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<const QStyleOptionSlider*>(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<const QStyleOptionSlider*>(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

View File

@@ -32,6 +32,7 @@
#include <QThread> #include <QThread>
#include <QDebug> #include <QDebug>
#include <QCoreApplication>
#include <bctransmitter.h> #include <bctransmitter.h>
@@ -40,7 +41,7 @@
*/ */
BCTransmitter::BCTransmitter(QObject *parent) BCTransmitter::BCTransmitter(QObject *parent)
: QObject(parent), _isBusy(false) : QObject(parent)//, _isBusy(false)
{ {
//_canDriver = new BCDriverTinyCan{this}; //_canDriver = new BCDriverTinyCan{this};
_canDriver = &_dummyDriver; _canDriver = &_dummyDriver;
@@ -56,18 +57,24 @@ BCTransmitter::BCTransmitter(QObject *parent)
void BCTransmitter::onToggleDriverConnection( bool connect ) void BCTransmitter::onToggleDriverConnection( bool connect )
{ {
qDebug() << " --- onToggleDriverConnection: " << 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; BCDriver::DriverState state = connect ? BCDriver::DriverState::DeviceReady : BCDriver::DriverState::NotPresent;
const QString& message = connect ? "Trying to connect" : " FAILED"; const QString& message = connect ? "Trying to connect" : " FAILED";
emit driverStateChanged(state, message); emit driverStateChanged(state, message);
return; return;
/*
// Hier sind wir noch in GUI Thread // Hier sind wir noch in GUI Thread
QMutexLocker locker(&_mutex); QMutexLocker locker(&_mutex);
// weitere operation stoppen // weitere operation stoppen
_isBusy = true; _isBusy = true;
connect ? connectCanDriver() : disconnectCanDriver(); connect ? connectCanDriver() : disconnectCanDriver();
_isBusy = false; _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. // nicht aus dem Parent-Thread direkt sondern über die EventQueue aufgerufen wurde.
Q_ASSERT(QThread::currentThread() == this->thread()); Q_ASSERT(QThread::currentThread() == this->thread());
// Hier sind wir noch in GUI Thread // Wir arbeiten hier ohne besondere Threadsynchronisation, mutexed o.ä: Die
//QMutexLocker locker(&_mutex); // entkoppelung und serialisierung passiert bereits durch die Qt-Eventqueue.
_valueQueue.enqueue( value ); // 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 // Kosmetik
// wollen wir nicht den Value in verschiedenen Threads gleichzeitig const BCValue& value = *(valuePtr.get());
// in die Hand nehmen, also hantieren wir nur mit den Inidizes.
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<uint32_t>(value.deviceID);
uint8_t regID = static_cast<uint8_t> (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() 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<uint32_t>(value.deviceID);
uint8_t regID = static_cast<uint8_t> (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 ) TransmitResult BCTransmitter::readByteValue( uint32_t deviceID, uint8_t registerID )
{ {

View File

@@ -63,7 +63,7 @@ public:
public slots: public slots:
void onToggleDriverConnection( bool connect ); void onToggleDriverConnection( bool connect );
void onEnqueueValue(BCValuePtrConst value ); void onUpdateValue(BCValuePtrConst valuePtr );
void onProcessValue(); void onProcessValue();
void onStartNativeDriver(); void onStartNativeDriver();
@@ -80,11 +80,11 @@ private:
TransmitResult readByteValue( uint32_t deviceID, uint8_t registerID ); TransmitResult readByteValue( uint32_t deviceID, uint8_t registerID );
TransmitResult readWordValue( uint32_t deviceID, uint8_t registerID ); TransmitResult readWordValue( uint32_t deviceID, uint8_t registerID );
using BCDataQueue = QQueue<BCValuePtrConst>; //using BCDataQueue = QQueue<BCValuePtrConst>;
BCDataQueue _valueQueue; //BCDataQueue _valueQueue;
QMutex _mutex; //QMutex _mutex;
std::atomic<bool> _isBusy{ false }; //std::atomic<bool> _isBusy{ false };
// __fix! // __fix!
BCDriver* _canDriver{}; BCDriver* _canDriver{};

View File

@@ -44,7 +44,7 @@ BCValue::BCValue( BCDevice::ID deviceID_, BC::ID registerID_)
visibleValue = "--"; visibleValue = "--";
} }
QString BCValue::formatValue( uint32_t value ) const QString BCValue::formatValues( uint32_t value ) const
{ {
if( factor == 1 ) if( factor == 1 )
return QString::number( value ); return QString::number( value );

View File

@@ -93,7 +93,7 @@ public:
BCValue( BCDevice::ID deviceID_, BC::ID registerID_ ); BCValue( BCDevice::ID deviceID_, BC::ID registerID_ );
QString formatValue( uint32_t value ) const; QString formatValues( uint32_t value ) const;
void dumpValue() const; void dumpValue() const;
mutable States state{BCValue::State::ReadOnly}; mutable States state{BCValue::State::ReadOnly};
@@ -103,6 +103,7 @@ public:
int indexRow{-1}; int indexRow{-1};
QString label; QString label;
mutable QString visibleValue; mutable QString visibleValue;
mutable double rawValue;
bool isWord{false}; bool isWord{false};
QString unitLabel; QString unitLabel;
double factor{1}; double factor{1};

View File

@@ -85,7 +85,7 @@ void BCValueModel::onValueUpdated( int row, BCValue::State state, const QString&
{ {
if( row > -1 && row < _valueList.size() ) if( row > -1 && row < _valueList.size() )
{ {
BCValue& value = *(_valueList[row].get()); const BCValue& value = *(_valueList[row].get());
QModelIndex idx = index(row,1); QModelIndex idx = index(row,1);
//qDebug(); //qDebug();
@@ -132,7 +132,7 @@ int BCValueModel::columnCount(const QModelIndex& parent) const
{ {
if (parent.isValid()) if (parent.isValid())
return 0; 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 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 Qt::NoItemFlags;
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;

View File

@@ -59,11 +59,7 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName )
} }
qDebug().noquote() << parts.join(" "); qDebug().noquote() << parts.join(" ");
}; };
/*
QString fileName = QFileDialog::getOpenFileName(this, "XML öffnen", "", "XML Files (*.xml)");
if (fileName.isEmpty())
return;
*/
QMetaEnum bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()}; QMetaEnum bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()};
@@ -92,7 +88,7 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName )
if (token == QXmlStreamReader::StartElement) if (token == QXmlStreamReader::StartElement)
{ {
QString deviceType = _xml.attributes().value("Type"_L1).toString(); QString deviceType = _xml.attributes().value("Type"_L1).toString();
printAttrs (_xml); //printAttrs (_xml);
// Wir wollen die Device-ID aus dem XML Tag ermitteln // Wir wollen die Device-ID aus dem XML Tag ermitteln
const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData(); const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData();
bool ok; 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) void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
{ {
auto printAttrs = [](const QXmlStreamReader& xml) auto printAttrs = [](const QXmlStreamReader& xml)
@@ -138,7 +125,7 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
printAttrs (_xml); printAttrs (_xml);
Q_ASSERT(_xml.isStartElement() && _xml.name() == "Device"_L1); Q_ASSERT(_xml.isStartElement() && _xml.name() == "Device"_L1);
qDebug() << " XXX ---------------";
// temporäre Wertliste für neues Device // temporäre Wertliste für neues Device
BCValueList currentValues; BCValueList currentValues;

View File

@@ -7,15 +7,12 @@
<file alias="bionx_akku.png">resources/bionx_akku.png</file> <file alias="bionx_akku.png">resources/bionx_akku.png</file>
<file alias="bionx_console.png">resources/bionx_console.png</file> <file alias="bionx_console.png">resources/bionx_console.png</file>
<file alias="bionx_motor.png">resources/bionx_motor.png</file> <file alias="bionx_motor.png">resources/bionx_motor.png</file>
<file alias="exit.png">resources/exit.png</file> <file alias="connect.svg">resources/connect.svg</file>
<file alias="important.png">resources/important.png</file> <file alias="exit.svg">resources/exit.svg</file>
<file alias="restart.png">resources/restart.png</file> <file alias="exit_red.svg">resources/exit_red.svg</file>
<file alias="splash.png">resources/splash.png</file> <file alias="sync_green.svg">resources/sync_green.svg</file>
<file alias="connect.png">resources/connect.png</file> <file alias="sync_yellow.svg">resources/sync_yellow.svg</file>
<file alias="connected.png">resources/connected.png</file> <file alias="connect_white.svg">resources/connect_white.svg</file>
<file alias="disconnected.png">resources/disconnected.png</file> <file alias="sync.svg">resources/sync.svg</file>
<file>resources/connect_white.svg</file>
<file>resources/sync_green.svg</file>
<file>resources/sync_yellow.svg</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

View File

@@ -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 https://github.com/MHS-Elektronik/OBD-Display
sudo chgrp pi /opt sudo chgrp pi /opt
@@ -36,6 +73,7 @@ https://github.com/MHS-Elektronik/OBD-Display
tar -xzvf tiny_can_raspberry_790.tar.gz tar -xzvf tiny_can_raspberry_790.tar.gz
rm tiny_can_raspberry_790.tar.gz rm tiny_can_raspberry_790.tar.gz
sudo apt install fonts-open-sans
RANT RANT

Binary file not shown.

Binary file not shown.

View File

@@ -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<T> 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
// ----------

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -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:
-------------------------------------------------------------------------------------------------

Binary file not shown.

View File

@@ -30,7 +30,7 @@
<Value ID='Cons_Assist_Mountain_Cap' Label='Mountain Cap' UnitLabel='%' Factor='1.5625' /> <Value ID='Cons_Assist_Mountain_Cap' Label='Mountain Cap' UnitLabel='%' Factor='1.5625' />
</Device> </Device>
<!--
<Device Type="Motor"> <Device Type="Motor">
<Value ID='Motor_Rev_Hw' Label='Hardware Version' /> <Value ID='Motor_Rev_Hw' Label='Hardware Version' />
<Value ID='Motor_Rev_Sw' Label='Software Version' /> <Value ID='Motor_Rev_Sw' Label='Software Version' />
@@ -46,7 +46,7 @@
<Value ID='Battery_Rev_Hw' Label='Hardware Version' /> <Value ID='Battery_Rev_Hw' Label='Hardware Version' />
<Value ID='Battery_Rev_Sw' Label='Software Version' /> <Value ID='Battery_Rev_Sw' Label='Software Version' />
</Device> </Device>
-->
</Bike> </Bike>
<!-- <!--

View File

@@ -3,118 +3,165 @@
/* Alle QWidgets bekommen diesen Font */ /* Alle QWidgets bekommen diesen Font */
QWidget QWidget
{ {
font-size: 14px; font-family: "Calibri", "Carlito", "Open Sans", sans-serif;
font-family: Segoe UI, sans-serif; font-size: 10pt;
margin: 0px;
padding: 0px;
} }
/*
QMainWindow QLabel#_headerLabel
{ {
background-color: #272727; font-size: 14pt;
font-weight: bold;
} }
/*
QToolButton
{
background-color: white;
color: #201F1E;
border: 1px solid #8A8886;
border-radius: 4px;
min-width: 64px;
max-width: 64px;
min-height: 64px;
max-height: 64px;
}
QToolButton:hover
{
background-color: #F3F2F1;
border: 1px solid #323130;
}
QToolButton:pressed
{
background-color: #EDEBE9;
border: 1px solid #201F1E;
}
QToolButton:disabled
{
background-color: #F3F2F1;
color: #A19F9D;
border: 1px solid #EDEBE9;
}
*/ */
/* Spezifisches Styling für Buttons */ /* === QToolButton === */
QPushButton QToolButton
{ {
background-color: #0078d7; background-color: transparent;
color: white; color: #000000;
border: none;
border-radius: 4px; border-radius: 4px;
padding: 6px; padding: 6px;
min-width: 64px;
max-width: 64px;
min-height: 64px;
max-height: 64px;
} }
QPushButton:hover QToolButton:hover
{ {
background-color: #1084e3; background-color: #F9F9F9;
border: 1px solid #DDDDDD;
} }
QPushButton:pressed QToolButton:pressed
{ {
background-color: #005a9e; background-color: #E0E0E0;
} }
/* Normal */ QToolButton:checked
QToolButton { {
background-color: transparent; background-color: green;/*#0078D4;*/
border: 1px solid transparent; color: #FFFFFF;
}
QToolButton:disabled
{
color: #A19F9D;
}
/* === QTableView & QTableWidget === */
QTableView, QTableWidget
{
background-color: #FFFFFF;
color: #000000;
gridline-color: #E1DFDD;
border: 1px solid #E1DFDD;
border-radius: 4px; border-radius: 4px;
padding: 4px; selection-background-color: #0078D4;
} }
/* Hover */
QToolButton:hover {
background-color: #E3F2FD;
border: 1px solid #2196F3;
}
/* Pressed/Clicked */
QToolButton:pressed {
background-color: #BBDEFB;
}
/* Checked (bei checkable buttons) */
QToolButton:checked {
background-color: #2196F3;
color: white;
}
/* Checked + Hover */
QToolButton:checked:hover {
background-color: #1976D2;
}
/* Disabled */
QToolButton:disabled {
color: #BDBDBD;
background-color: transparent;
}
/*
QTableView
{
background-color: #404142;
border-radius: 8px;
outline: none;
show-decoration-selected: 0;
}
QTableView::item
{
border: 2px solid #2196F3;
border-radius: 8px;
padding: 8px;
margin: 4px;
background-color: white;
}
*/
QTableView::item:hover QTableView::item:hover
{ {
border-color: #FF9800; background-color: #e8f4f8;
background-color: #fff8f0;
} }
QTableView::item:selected
{ QScrollBar::handle:horizontal {
border-color: #F44336; /* Roter Rahmen */ background-color: #C8C6C4;
background-color: #ffebee; min-width: 40px;
border-radius: 6px;
margin: 2px;
} }
QTableView::item:selected:hover QScrollBar::handle:horizontal:hover {
{ background-color: #A19F9D;
border-color: #FF9800;
background-color: #ffe0b2;
} }
*/
QTableView::item:focus QScrollBar::handle:horizontal:pressed {
{ background-color: #8A8886;
/*
// outline: none; Entfernt das Focus-Rectangle
// border-color: green;
// background-color: #ffe0b2;
*/
border: 2px solid gray;
border-style: inset;
background-color: white;
color: black;
} }
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0px;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: none;
}
QScrollBar:vertical {
background-color: transparent;
width: 12px;
margin: 0;
}
QScrollBar::handle:vertical {
background-color: #C8C6C4;
min-height: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background-color: #A19F9D;
}
QScrollBar::handle:vertical:pressed {
background-color: #8A8886;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: none;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

1
resources/connect.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.25 4a.75.75 0 0 1 .743.648L9 4.75v2.773l1.874 2.815a.75.75 0 0 1 .117.306l.009.11v4.496a.75.75 0 0 1-.649.743L10.25 16H8.996v3.254a.75.75 0 0 1-1.492.101l-.007-.101L7.496 16 5.5 15.999v3.258a.75.75 0 0 1-1.493.101l-.006-.101L4 15.999 2.75 16a.75.75 0 0 1-.742-.648l-.007-.102v-4.496a.75.75 0 0 1 .071-.32l.055-.096 1.874-2.815V4.75a.75.75 0 0 1 1.493-.102l.007.102v3a.75.75 0 0 1-.072.32l-.054.096-1.874 2.815V14.5H9.5v-3.52L7.625 8.167a.75.75 0 0 1-.117-.306L7.5 7.75v-3A.75.75 0 0 1 8.25 4Zm7.004.001h4.496a.75.75 0 0 1 .743.649l.007.101L20.499 8h.75a.75.75 0 0 1 .744.648L22 8.75v4.496c0 .111-.025.22-.072.32l-.054.096L20 16.477v2.773a.75.75 0 0 1-1.494.102l-.006-.102v-3c0-.11.024-.22.071-.32l.054-.096L20.5 13.02V9.5h-5.998v3.52l1.874 2.814a.749.749 0 0 1 .118.306l.008.11v3a.75.75 0 0 1-1.493.102l-.007-.102v-2.773l-1.874-2.815a.75.75 0 0 1-.118-.306l-.008-.11V8.75a.75.75 0 0 1 .648-.743L13.752 8h.752V4.751a.75.75 0 0 1 .649-.743l.101-.007h4.496-4.496ZM19 5.501h-2.996V8h2.995V5.501Z" fill="#000000"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.25 4a.75.75 0 0 1 .743.648L9 4.75v2.773l1.874 2.815a.75.75 0 0 1 .117.306l.009.11v4.496a.75.75 0 0 1-.649.743L10.25 16H8.996v3.254a.75.75 0 0 1-1.492.101l-.007-.101L7.496 16 5.5 15.999v3.258a.75.75 0 0 1-1.493.101l-.006-.101L4 15.999 2.75 16a.75.75 0 0 1-.742-.648l-.007-.102v-4.496a.75.75 0 0 1 .071-.32l.055-.096 1.874-2.815V4.75a.75.75 0 0 1 1.493-.102l.007.102v3a.75.75 0 0 1-.072.32l-.054.096-1.874 2.815V14.5H9.5v-3.52L7.625 8.167a.75.75 0 0 1-.117-.306L7.5 7.75v-3A.75.75 0 0 1 8.25 4Zm7.004.001h4.496a.75.75 0 0 1 .743.649l.007.101L20.499 8h.75a.75.75 0 0 1 .744.648L22 8.75v4.496c0 .111-.025.22-.072.32l-.054.096L20 16.477v2.773a.75.75 0 0 1-1.494.102l-.006-.102v-3c0-.11.024-.22.071-.32l.054-.096L20.5 13.02V9.5h-5.998v3.52l1.874 2.814a.749.749 0 0 1 .118.306l.008.11v3a.75.75 0 0 1-1.493.102l-.007-.102v-2.773l-1.874-2.815a.75.75 0 0 1-.118-.306l-.008-.11V8.75a.75.75 0 0 1 .648-.743L13.752 8h.752V4.751a.75.75 0 0 1 .649-.743l.101-.007h4.496-4.496ZM19 5.501h-2.996V8h2.995V5.501Z" fill="#DDE6E8"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

1
resources/exit.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2Zm0 1.5a8.5 8.5 0 1 0 0 17 8.5 8.5 0 0 0 0-17Zm3.446 4.897.084.073a.75.75 0 0 1 .073.976l-.073.084L13.061 12l2.47 2.47a.75.75 0 0 1 .072.976l-.073.084a.75.75 0 0 1-.976.073l-.084-.073L12 13.061l-2.47 2.47a.75.75 0 0 1-.976.072l-.084-.073a.75.75 0 0 1-.073-.976l.073-.084L10.939 12l-2.47-2.47a.75.75 0 0 1-.072-.976l.073-.084a.75.75 0 0 1 .976-.073l.084.073L12 10.939l2.47-2.47a.75.75 0 0 1 .976-.072Z" fill="#000000"/></svg>

After

Width:  |  Height:  |  Size: 599 B

1
resources/exit_red.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2Zm0 1.5a8.5 8.5 0 1 0 0 17 8.5 8.5 0 0 0 0-17Zm3.446 4.897.084.073a.75.75 0 0 1 .073.976l-.073.084L13.061 12l2.47 2.47a.75.75 0 0 1 .072.976l-.073.084a.75.75 0 0 1-.976.073l-.084-.073L12 13.061l-2.47 2.47a.75.75 0 0 1-.976.072l-.084-.073a.75.75 0 0 1-.073-.976l.073-.084L10.939 12l-2.47-2.47a.75.75 0 0 1-.072-.976l.073-.084a.75.75 0 0 1 .976-.073l.084.073L12 10.939l2.47-2.47a.75.75 0 0 1 .976-.072Z" fill="#FF0000"/></svg>

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

1
resources/sync.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16 8.25a.75.75 0 0 1 1.5 0v3.25a.75.75 0 0 1-.75.75H14a.75.75 0 0 1 0-1.5h1.27A3.502 3.502 0 0 0 12 8.5c-1.093 0-2.037.464-2.673 1.23a.75.75 0 1 1-1.154-.96C9.096 7.66 10.463 7 12 7c1.636 0 3.088.785 4 2v-.75ZM8 15v.75a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 .75-.75H10a.75.75 0 0 1 0 1.5H8.837a3.513 3.513 0 0 0 5.842.765.75.75 0 1 1 1.142.972A5.013 5.013 0 0 1 8 15Zm4-13C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm8.5 10a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0Z" fill="#000000"/></svg>

After

Width:  |  Height:  |  Size: 609 B

1
resources/sync_green.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16 8.25a.75.75 0 0 1 1.5 0v3.25a.75.75 0 0 1-.75.75H14a.75.75 0 0 1 0-1.5h1.27A3.502 3.502 0 0 0 12 8.5c-1.093 0-2.037.464-2.673 1.23a.75.75 0 1 1-1.154-.96C9.096 7.66 10.463 7 12 7c1.636 0 3.088.785 4 2v-.75ZM8 15v.75a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 .75-.75H10a.75.75 0 0 1 0 1.5H8.837a3.513 3.513 0 0 0 5.842.765.75.75 0 1 1 1.142.972A5.013 5.013 0 0 1 8 15Zm4-13C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm8.5 10a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0Z" fill="#00FF00"/></svg>

After

Width:  |  Height:  |  Size: 609 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16 8.25a.75.75 0 0 1 1.5 0v3.25a.75.75 0 0 1-.75.75H14a.75.75 0 0 1 0-1.5h1.27A3.502 3.502 0 0 0 12 8.5c-1.093 0-2.037.464-2.673 1.23a.75.75 0 1 1-1.154-.96C9.096 7.66 10.463 7 12 7c1.636 0 3.088.785 4 2v-.75ZM8 15v.75a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 .75-.75H10a.75.75 0 0 1 0 1.5H8.837a3.513 3.513 0 0 0 5.842.765.75.75 0 1 1 1.142.972A5.013 5.013 0 0 1 8 15Zm4-13C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm8.5 10a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0Z" fill="#F2C511"/></svg>

After

Width:  |  Height:  |  Size: 609 B