Finally, animation works.

This commit is contained in:
2025-12-19 21:20:14 +01:00
parent 552fcdf8f8
commit 826e506d00
6 changed files with 204 additions and 44 deletions

View File

@@ -7,11 +7,16 @@
#include <QTimer> #include <QTimer>
#include <QListView> #include <QListView>
#include <QVariantAnimation>
#include <QPropertyAnimation>
#include <QPainter>
#include "bcitemdelegate.h" #include "bcitemdelegate.h"
#include "bcvalue.h" #include "bcvalue.h"
#include "qapplication.h" #include "qapplication.h"
BCItemDelegate::BCItemDelegate(QListView *view) BCItemDelegate::BCItemDelegate(QListView *view)
: QStyledItemDelegate(view), _view{view} : QStyledItemDelegate(view), _view{view}
{ {
@@ -136,28 +141,9 @@ 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 void BCItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ {
/*
// 1. Standard-Zeichnen (Text, Hintergrund, Selection) durchführen // 1. Standard-Zeichnen (Text, Hintergrund, Selection) durchführen
QStyledItemDelegate::paint(painter, option, index); QStyledItemDelegate::paint(painter, option, index);
@@ -179,7 +165,122 @@ void BCItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option
painter->restore(); painter->restore();
} }
*/
QStyledItemDelegate::paint(painter, option, index);
/*
if (index.row() == _highlightedRow && _opacity > 0.0)
{
painter->save();
qDebug() << " --- is highlight: " << index.row();
QColor highlightColor( 0xFF9800 );
highlightColor.setAlphaF(_opacity);
QPen pen(highlightColor, 3);
painter->setPen(pen);
painter->setBrush(Qt::NoBrush);
painter->drawRoundedRect(option.rect.adjusted(2, 2, -2, -2), 8, 8);
painter->restore();
} }
*/
int row = index.row();
if (m_rowOpacities.contains(row))
{
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 BCItemDelegate::onHighlightRow(int row)
{
qDebug() << " --- should highlight: " << row;
// Alte Animation für diese Zeile stoppen falls vorhanden
if (m_rowAnimations.contains(row))
{
m_rowAnimations[row]->stop();
m_rowAnimations[row]->deleteLater();
}
// QVariantAnimation ist flexibler als QPropertyAnimation
auto* anim = new QVariantAnimation(this);
anim->setDuration(800);
anim->setStartValue(0.0);
anim->setEndValue(1.0);
// Custom Easing für Fade-in/out Effekt
anim->setEasingCurve(QEasingCurve::OutQuad);
connect(anim, &QVariantAnimation::valueChanged, this, [this, row](const QVariant& value)
{
qreal progress = value.toReal();
qreal opacity;
// Schnelles Fade-in (20%), langsames Fade-out (80%)
if (progress < 0.2) {
opacity = progress * 5.0; // 0->1 in 20%
} else {
opacity = 1.0 - ((progress - 0.2) / 0.8); // 1->0 in 80%
}
m_rowOpacities[row] = opacity;
updateRow(row);
});
connect(anim, &QVariantAnimation::finished, this, [this, row, anim]()
{
m_rowOpacities.remove(row);
m_rowAnimations.remove(row);
updateRow(row);
anim->deleteLater();
});
m_rowAnimations[row] = anim;
anim->start(QAbstractAnimation::DeleteWhenStopped);
}
// Optional: alle Highlights sofort clearen
void BCItemDelegate::clearAllHighlights()
{
for(auto* anim : std::as_const(m_rowAnimations))
{
anim->stop();
anim->deleteLater();
}
m_rowAnimations.clear();
m_rowOpacities.clear();
if (_view)
{
_view->viewport()->update();
}
}
void BCItemDelegate::updateRow(int row)
{
if (_view && row >= 0) {
QModelIndex idx = _view->model()->index(row, 0);
QRect rect = _view->visualRect(idx);
if (!rect.isEmpty()) {
_view->viewport()->update(rect);
}
}
}
QString BCItemDelegate::formatDisplayString(const QModelIndex &index) const QString BCItemDelegate::formatDisplayString(const QModelIndex &index) const
{ {
if (!index.isValid()) if (!index.isValid())

View File

@@ -4,11 +4,14 @@
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
class QPropertyAnimation;
class QVariantAnimation;
class QListView; class QListView;
class BCItemDelegate : public QStyledItemDelegate class BCItemDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(qreal highlightOpacity READ highlightOpacity WRITE setHighlightOpacity)
public: public:
@@ -25,17 +28,45 @@ 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;
emit viewUpdateNeeded();
}
void clearAllHighlights();
public slots: public slots:
void onHighlightRow(int row); void onHighlightRow(int row);
signals:
void viewUpdateNeeded();
private: private:
void updateRow(int row);
QString formatDisplayString(const QModelIndex &index) const; QString formatDisplayString(const QModelIndex &index) const;
int _highlightedRow{-1}; int _highlightedRow{-1};
QListView* _view{nullptr}; qreal _opacity{1.0};
QListView* _view{};
QPropertyAnimation *_animation{};
private:
QHash<int, qreal> m_rowOpacities;
QHash<int, QVariantAnimation*> m_rowAnimations;
}; };

View File

@@ -31,6 +31,8 @@
#include <bcitemdelegate.h> #include <bcitemdelegate.h>
#include <ui_bcmainwindow.h> #include <ui_bcmainwindow.h>
BCMainWindow::BCMainWindow(QWidget *parent) BCMainWindow::BCMainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
{ {
@@ -40,12 +42,26 @@ BCMainWindow::BCMainWindow(QWidget *parent)
if( model) if( model)
_valueView->setModel( *model ); _valueView->setModel( *model );
BCItemDelegate* delegate = new BCItemDelegate( _valueView); BCItemDelegate* _delegate = new BCItemDelegate( _valueView);
_valueView->setItemDelegate( delegate ); //_delegate = new AnimatedDelegate(_valueView );
_valueView->setItemDelegate( _delegate );
connect( &_valueManager, &BCValueManager::valueTouched, delegate, &BCItemDelegate::onHighlightRow );
// Verwendung:
connect(_delegate, &BCItemDelegate::viewUpdateNeeded, _valueView->viewport(), QOverload<>::of(&QWidget::update));
//_valueView->setItemDelegate(_delegate);
// Highlight mit Fade-Out:
_delegate->onHighlightRow(2); // 2 Sekunden Fade
// besser: model::emit dataChanged
// also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
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 ); connect( _syncButton, &QPushButton::clicked, &_valueManager, &BCValueManager::onSyncFromDevice );
} }
BCMainWindow::~BCMainWindow() BCMainWindow::~BCMainWindow()

View File

@@ -36,7 +36,7 @@
#include <bcvaluemanager.h> #include <bcvaluemanager.h>
class AnimatedDelegate;
class BCMainWindow : public QMainWindow, public Ui_BCMainWindow class BCMainWindow : public QMainWindow, public Ui_BCMainWindow
{ {
@@ -54,6 +54,7 @@ public slots:
protected: protected:
BCValueManager _valueManager; BCValueManager _valueManager;
AnimatedDelegate* _delegate;
}; };

View File

@@ -35,6 +35,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QStatusBar> #include <QStatusBar>
#include <QApplication> #include <QApplication>
#include <QElapsedTimer>
#include <bcvaluemanager.h> #include <bcvaluemanager.h>
@@ -42,6 +43,16 @@
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
void processEventsFor(int milliseconds)
{
QElapsedTimer timer;
timer.start();
while (timer.elapsed() < milliseconds) {
QApplication::processEvents(QEventLoop::AllEvents, 50);
}
}
BCValueManager::BCValueManager(QObject *parent) BCValueManager::BCValueManager(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
@@ -54,11 +65,11 @@ 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::enqueueValue); connect(this, &BCValueManager::valueCreated, &_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);
connect(&_transmitter, &BCTransmitter::messageLogged, this, &BCValueManager::onRunnerMessage); //connect(&_transmitter, &BCTransmitter::messageLogged, this, &BCValueManager::onRunnerMessage);
// C) Aufräumen: Wenn Thread endet, lösche den Runner // C) Aufräumen: Wenn Thread endet, lösche den Runner
connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
@@ -77,14 +88,6 @@ BCValueManager::~BCValueManager()
} }
void BCValueManager::transmitValue(const BCValue& value)
{
// Wir rufen keine Methode am Runner direkt auf!
// Wir emitten ein Signal. Qt kümmert sich um den Thread-Wechsel.
emit newCommandArrived(value);
}
void BCValueManager::onCommandFinished(int id, bool success) void BCValueManager::onCommandFinished(int id, bool success)
{ {
@@ -144,14 +147,25 @@ void BCValueManager::onSyncFromDevice()
{ {
BCValueList& currentList = _valueModels[_currentDeviceID]->getValueList(); BCValueList& currentList = _valueModels[_currentDeviceID]->getValueList();
for( BCValue& value : currentList )
BCValue& value = currentList[4];
for( const BCValue& value : currentList )
{ {
qDebug() << " --- value: " << value.label; qDebug() << " --- value: " << value.label;
_transmitter.enqueueValue( value );
// statt '_transmitter.enqueueValue( value )' entkoppeln
// wir das eleganter über emit valueCreated()
//_transmitter.enqueueValue( value );
emit valueCreated(value);
emit valueTouched( value.rowInModel ); emit valueTouched( value.rowInModel );
QApplication::processEvents();
processEventsFor(500);
//QApplication::processEvents();
// Thread schlafen lassen (Simulation einer blockierenden Operation) // Thread schlafen lassen (Simulation einer blockierenden Operation)
QThread::msleep(500); //QThread::msleep(500);
} }

View File

@@ -54,9 +54,6 @@ public:
BCValue makeValue(BCDevice::ID deviceID, const BCValueParams& params ); BCValue makeValue(BCDevice::ID deviceID, const BCValueParams& params );
// Public API für die Applikation
void transmitValue( const BCValue& value);
public slots: public slots:
void loadBikeData(); void loadBikeData();
@@ -67,7 +64,7 @@ public slots:
signals: signals:
// Internes Signal, um Daten an den Worker Thread zu senden // Internes Signal, um Daten an den Worker Thread zu senden
void newCommandArrived(const BCValue& cmd); void valueCreated(const BCValue& cmd);
//void valuedTouched(const BCValue& cmd); //void valuedTouched(const BCValue& cmd);
void valueTouched(int rowInModel ); void valueTouched(int rowInModel );