Hightlight lines when touched.

This commit is contained in:
2025-12-19 17:37:24 +01:00
parent 3e5f616461
commit 552fcdf8f8
11 changed files with 140 additions and 40 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.1, 2025-12-19T10:26:05. --> <!-- Written by QtCreator 18.0.1, 2025-12-19T17:13:02. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@@ -2,6 +2,5 @@
<qresource prefix="/"> <qresource prefix="/">
<file alias="bikeinfo.xml">data/bikeinfo.xml</file> <file alias="bikeinfo.xml">data/bikeinfo.xml</file>
<file>bionxcontrol.qss</file> <file>bionxcontrol.qss</file>
<file alias="typeinfo.xml">data/typeinfo.xml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -50,22 +50,30 @@ void BCValueModel::setValueList(const BCValueList& valueList)
endResetModel(); endResetModel();
} }
BCValueList& BCValueModel::getValueList()
{
return _valueList;
}
int BCValueModel::rowCount(const QModelIndex &parent) const int BCValueModel::rowCount(const QModelIndex &parent) const
{ {
if (parent.isValid()) return 0; if (parent.isValid())
return 0;
return _valueList.size(); return _valueList.size();
} }
QVariant BCValueModel::data(const QModelIndex &index, int role) const QVariant BCValueModel::data(const QModelIndex& index, int role) const
{ {
if (!index.isValid() || index.row() >= _valueList.size()) int row = index.row();
if (!index.isValid() || row >= _valueList.size())
return QVariant(); return QVariant();
const BCValue &item = _valueList.at(index.row()); BCValue& value = const_cast<BCValue&>(_valueList.at( row ));
value.rowInModel = row;
if (role == Qt::DisplayRole || role == Qt::EditRole)
return QVariant::fromValue(value);
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return QVariant::fromValue(item);
}
return QVariant(); return QVariant();
} }

View File

@@ -46,6 +46,8 @@ public:
void addValue(const BCValue &val); void addValue(const BCValue &val);
void setValueList(const BCValueList& valueList); void setValueList(const BCValueList& valueList);
BCValueList& getValueList();
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

View File

@@ -3,14 +3,17 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QWidget> #include <QWidget>
#include <QDebug> #include <QDebug>
#include <QPainter>
#include <QTimer>
#include <QListView>
#include "bcitemdelegate.h" #include "bcitemdelegate.h"
#include "bcvalue.h" #include "bcvalue.h"
#include "qapplication.h" #include "qapplication.h"
BCItemDelegate::BCItemDelegate(QObject *parent) BCItemDelegate::BCItemDelegate(QListView *view)
: QStyledItemDelegate(parent) : QStyledItemDelegate(view), _view{view}
{ {
} }
@@ -26,7 +29,7 @@ QString BCItemDelegate::displayText(const QVariant& dataValue, const QLocale& lo
// Hier bauen wir den String zusammen, den man sieht, // Hier bauen wir den String zusammen, den man sieht,
// wenn KEIN Editor offen ist. // wenn KEIN Editor offen ist.
// Format: "Label: Wert Einheit" // Format: "Label: Wert Einheit"
return QString("%1: %2 %3").arg(bc.label, bc.value.toString(), "mm1"); return QString("%1: %2 %3").arg(bc.label, bc.value.toString(), "mmX");
} }
// Fallback für normale Strings/Zahlen // Fallback für normale Strings/Zahlen
@@ -132,6 +135,51 @@ QSize BCItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelI
*/ */
} }
void BCItemDelegate::onHighlightRow(int row)
{
qDebug() << " --- should highlight: " << row;
_highlightedRow = row;
// Trigger Repaint der View (damit der Rahmen sofort erscheint)
_view->viewport()->update();
// Timer: Nach 1 Sekunde wieder ausschalten (Temporär)
QTimer::singleShot(1000, this, [=, this]()
{
if(_highlightedRow == row)
{ // Nur zurücksetzen, wenn es noch dieselbe Zeile ist
_highlightedRow = -1;
if (_view)
_view->viewport()->update();
}
});
}
void BCItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
// 1. Standard-Zeichnen (Text, Hintergrund, Selection) durchführen
QStyledItemDelegate::paint(painter, option, index);
// 2. Unser Custom-Overly: Oranger Rahmen, wenn Zeile passt
if (index.row() == _highlightedRow)
{
painter->save();
qDebug() << " --- is highlight: " << index.row();
// Setup Stift
QPen pen(QColor(255, 165, 0)); // Orange
pen.setWidth(2);
painter->setPen(pen);
// Rechteck zeichnen (leicht eingezogen, damit es nicht abgeschnitten wird)
QRect rect = option.rect.adjusted(1, 1, -1, -1);
painter->drawRect(rect);
painter->restore();
}
}
QString BCItemDelegate::formatDisplayString(const QModelIndex &index) const QString BCItemDelegate::formatDisplayString(const QModelIndex &index) const
{ {
if (!index.isValid()) if (!index.isValid())

View File

@@ -2,18 +2,18 @@
#ifndef BCITEMDELEGATE_H #ifndef BCITEMDELEGATE_H
#define BCITEMDELEGATE_H #define BCITEMDELEGATE_H
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
class QListView;
class BCItemDelegate : public QStyledItemDelegate class BCItemDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit BCItemDelegate(QObject *parent = nullptr); explicit BCItemDelegate(QListView *view );
// WICHTIG: Zuständig für die normale Anzeige (ohne Editor)
QString displayText(const QVariant& dataValue, const QLocale& locale) const override; QString displayText(const QVariant& dataValue, const QLocale& locale) const override;
// Zuständig für den Edit-Modus (Doppelklick) // Zuständig für den Edit-Modus (Doppelklick)
@@ -23,9 +23,19 @@ public:
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
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;
public slots:
void onHighlightRow(int row);
private: private:
QString formatDisplayString(const QModelIndex &index) const; QString formatDisplayString(const QModelIndex &index) const;
int _highlightedRow{-1};
QListView* _view{nullptr};
}; };

View File

@@ -36,12 +36,16 @@ BCMainWindow::BCMainWindow(QWidget *parent)
{ {
setupUi(this); setupUi(this);
_valueManager.loadBikeData(); _valueManager.loadBikeData();
auto model = _valueManager.getModel( "Console"_L1 ); auto model = _valueManager.getModel( BCDevice::ID::Console );
if( model) if( model)
_valueView->setModel( *model ); _valueView->setModel( *model );
_valueView->setItemDelegate( new BCItemDelegate( _valueView) );
BCItemDelegate* delegate = new BCItemDelegate( _valueView);
_valueView->setItemDelegate( delegate );
connect( &_valueManager, &BCValueManager::valueTouched, delegate, &BCItemDelegate::onHighlightRow );
connect( _connectButton, &QPushButton::clicked, &_valueManager, &BCValueManager::onToggleConnectionState ); connect( _connectButton, &QPushButton::clicked, &_valueManager, &BCValueManager::onToggleConnectionState );
connect( _syncButton, &QPushButton::clicked, &_valueManager, &BCValueManager::onSyncFromDevice );
} }
BCMainWindow::~BCMainWindow() BCMainWindow::~BCMainWindow()

View File

@@ -27,7 +27,7 @@
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum> <enum>QFrame::Shadow::Raised</enum>
</property> </property>
<widget class="QPushButton" name="_pushButton"> <widget class="QPushButton" name="_syncButton">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>30</x> <x>30</x>
@@ -37,7 +37,7 @@
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string>PushButton</string> <string>Sync</string>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="_connectButton"> <widget class="QPushButton" name="_connectButton">

View File

@@ -106,9 +106,8 @@ public:
BCDevice::ID deviceID{BCDevice::ID::Invalid}; BCDevice::ID deviceID{BCDevice::ID::Invalid};
BC::ID targetID{BC::ID::Invalid}; BC::ID targetID{BC::ID::Invalid};
BCValueType::TypeID typeID{BCValueType::TypeID::Invalid};; BCValueType::TypeID typeID{BCValueType::TypeID::Invalid};;
int rowInModel{-1};
QString label; QString label;
QVariant value; QVariant value;
QVariant defaultValue; QVariant defaultValue;

View File

@@ -34,6 +34,7 @@
#include <QPushButton> #include <QPushButton>
#include <QMessageBox> #include <QMessageBox>
#include <QStatusBar> #include <QStatusBar>
#include <QApplication>
#include <bcvaluemanager.h> #include <bcvaluemanager.h>
@@ -53,7 +54,7 @@ BCValueManager::BCValueManager(QObject *parent)
// 4. Verbindungen herstellen (Signal/Slot über Thread-Grenzen) // 4. Verbindungen herstellen (Signal/Slot über Thread-Grenzen)
// A) Befehl senden (Manager -> Runner) // A) Befehl senden (Manager -> Runner)
connect(this, &BCValueManager::newCommandArrived, &_transmitter, &BCTransmitter::addCommand); connect(this, &BCValueManager::newCommandArrived, &_transmitter, &BCTransmitter::enqueueValue);
// B) Ergebnisse empfangen (Runner -> Manager) // B) Ergebnisse empfangen (Runner -> Manager)
connect(&_transmitter, &BCTransmitter::commandFinished, this, &BCValueManager::onCommandFinished); connect(&_transmitter, &BCTransmitter::commandFinished, this, &BCValueManager::onCommandFinished);
@@ -136,11 +137,31 @@ void BCValueManager::onToggleConnectionState( bool connect )
} }
void BCValueManager::onSyncFromDevice()
std::optional<BCValueModel*> BCValueManager::getModel(const QString& key )
{ {
if( _valueModels.contains( key) ) qDebug() << " ---Syncing";
return _valueModels[key]; if( _currentDeviceID != BCDevice::ID::Invalid)
{
BCValueList& currentList = _valueModels[_currentDeviceID]->getValueList();
for( BCValue& value : currentList )
{
qDebug() << " --- value: " << value.label;
_transmitter.enqueueValue( value );
emit valueTouched( value.rowInModel );
QApplication::processEvents();
// Thread schlafen lassen (Simulation einer blockierenden Operation)
QThread::msleep(500);
}
}
}
std::optional<BCValueModel*> BCValueManager::getModel(BCDevice::ID deviceID )
{
if( _valueModels.contains( deviceID) )
return _valueModels[deviceID];
return std::nullopt; return std::nullopt;
} }
@@ -185,25 +206,28 @@ void BCValueManager::loadBikeData()
if (token == QXmlStreamReader::StartElement) if (token == QXmlStreamReader::StartElement)
{ {
QString deviceType = _xml.attributes().value("Type"_L1).toString(); QString deviceType = _xml.attributes().value("Type"_L1).toString();
qDebug() << " --- Device: " << _xml.name() << ": " << deviceType;
printAttrs (_xml); printAttrs (_xml);
const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData(); const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData();
auto deviceID = _bcDeviceEnum.keyToValue64(deviceKey); auto deviceID = _bcDeviceEnum.keyToValue64(deviceKey);
//_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) ); //_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) );
_currentDeviceID = deviceID.has_value() ? BCDevice::ID( deviceID.value() ) : BCDevice::ID::Invalid;
if(deviceID.has_value()) if(deviceID.has_value())
{ {
BCValueList parsedValues; qDebug() << " --- Device: " << _xml.name() << ": " << deviceType << " : " << deviceID;
_currentDeviceID = BCDevice::ID( deviceID.value() );
BCValueList parsedValues;
loadDeviceData( parsedValues ); loadDeviceData( parsedValues );
if( parsedValues.count() ) if( parsedValues.count() )
{ {
BCValueModel* valueModel = new BCValueModel( this ); BCValueModel* valueModel = new BCValueModel( this );
valueModel->setValueList(parsedValues); valueModel->setValueList(parsedValues);
_valueModels.insert( deviceType, valueModel ); _valueModels.insert( _currentDeviceID, valueModel );
} }
} }
} }
_currentDeviceID = BCDevice::ID::Console;
} }
/* /*

View File

@@ -50,7 +50,7 @@ public:
BCValueManager( QObject* parent = nullptr); BCValueManager( QObject* parent = nullptr);
virtual ~BCValueManager(); virtual ~BCValueManager();
std::optional<BCValueModel*> getModel(const QString& key ); std::optional<BCValueModel*> getModel(BCDevice::ID deviceID );
BCValue makeValue(BCDevice::ID deviceID, const BCValueParams& params ); BCValue makeValue(BCDevice::ID deviceID, const BCValueParams& params );
@@ -62,11 +62,14 @@ public slots:
void loadBikeData(); void loadBikeData();
void saveBikeData(); void saveBikeData();
void onToggleConnectionState( bool connect ); void onToggleConnectionState( bool connect );
void onSyncFromDevice();
signals: signals:
// Internes Signal, um Daten an den Worker Thread zu senden // Internes Signal, um Daten an den Worker Thread zu senden
void newCommandArrived(BCValue cmd); void newCommandArrived(const BCValue& cmd);
//void valuedTouched(const BCValue& cmd);
void valueTouched(int rowInModel );
private slots: private slots:
@@ -79,16 +82,19 @@ protected:
void loadTypeData(); void loadTypeData();
void loadDeviceData( BCValueList& parsedValues ); void loadDeviceData( BCValueList& parsedValues );
QXmlStreamReader _xml; using BCDeviceModels = QMap<BCDevice::ID, BCValueModel*>;
QMap<QString,BCValueType> _valueTypes; using BCValueTypes = QMap<QString,BCValueType>;
QMap<QString,BCValueModel*> _valueModels;
BCDevice::ID _currentDeviceID{BCDevice::ID::Invalid};
QMetaEnum _bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()};
BCCanDriverTinyCan _canDriver; QXmlStreamReader _xml;
BCValueTypes _valueTypes;
BCDeviceModels _valueModels;
BCDevice::ID _currentDeviceID{BCDevice::ID::Invalid};
QMetaEnum _bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()};
QThread _worker; BCCanDriverTinyCan _canDriver;
BCTransmitter _transmitter;
QThread _worker;
BCTransmitter _transmitter;
}; };