Added smiles.

This commit is contained in:
2026-01-10 16:38:52 +01:00
parent cb553cf928
commit aa4b2a1b84
27 changed files with 302 additions and 21 deletions

View File

@@ -47,6 +47,7 @@ windows
SOURCES += \ SOURCES += \
bc.cpp \ bc.cpp \
bcanimateddelegate.cpp \ bcanimateddelegate.cpp \
bcdelightpmwidget.cpp \
bcdeviceview.cpp \ bcdeviceview.cpp \
bcdriver.cpp \ bcdriver.cpp \
bcdriverstatewidget.cpp \ bcdriverstatewidget.cpp \
@@ -66,6 +67,7 @@ SOURCES += \
HEADERS += \ HEADERS += \
bc.h \ bc.h \
bcanimateddelegate.h \ bcanimateddelegate.h \
bcdelightpmwidget.h \
bcdeviceview.h \ bcdeviceview.h \
bcdriver.h \ bcdriver.h \
bcdriverstatewidget.h \ bcdriverstatewidget.h \

209
bcdelightpmwidget.cpp Normal file
View File

@@ -0,0 +1,209 @@
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include <QParallelAnimationGroup>
#include <QRandomGenerator>
#include <QTimer>
#include <QDebug>
#include <QGraphicsOpacityEffect>
#include <QPainterPath>
#include <QDir>
#include <QFileInfo>
#include <QIcon>
#include <QDirIterator>
#include <QPushButton>
#include <bcdelightpmwidget.h>
BCDelightPMWidget::BCDelightPMWidget(QWidget *parent)
: QObject(parent), _playGround{parent}
{
loadWidgetsFromResources();
}
// Die Methode zum automatischen Einlesen
void BCDelightPMWidget::loadWidgetsFromResources()
{
QString resourcePath = ":/resources/smile";
// 2. Iterator erstellen
// QDir::Files sorgt dafür, dass wir nur echte Dateien finden, keine Ordner wie "." oder ".."
// QDirIterator::Subdirectories würde auch in Unterordnern suchen (falls nötig)
QDirIterator it(resourcePath, QDir::Files);
//QFileInfoList fileList:
// 3. Schleife: Solange es noch Einträge gibt
while (it.hasNext())
{
// Iteriert zum nächsten Eintrag und gibt den kompletten virtuellen Pfad zurück
// z.B. ":/icons/ball_red.png"
QString fullPath = it.next();
// Optional: Nur den Dateinamen holen (z.B. "ball_red.png")
// QString fileName = it.fileName();
qDebug() << "Gefundenes Icon:" << fullPath;
// Eine Zufallsfarbe für den Button-Hintergrund generieren
QStringList colors = {"#ff5555", "#50fa7b", "#8be9fd", "#ffb86c"};
QString randomColor = colors.at(QRandomGenerator::global()->bounded(colors.size()));
// Ihre Funktion aufrufen und den Pfad übergeben
createFlyingWidget(fullPath, randomColor);
}
}
void BCDelightPMWidget::createFlyingWidget(const QString& iconPath, const QString &color)
{
// 1. Button als Kind des Playground erstellen
QPushButton *btn = new QPushButton(_playGround);
// 2. Das Icon laden und setzen
QPixmap pixmap(iconPath);
QIcon icon(pixmap);
btn->setIcon(icon);
// 3. WICHTIG: Icon-Größe und Button-Größe synchronisieren
// Damit das Bild den Button voll ausfüllt
QSize size(128, 128);
btn->setFixedSize(size); // Die Klick-Fläche
btn->setIconSize(size); // Das Bild darin
// 4. Stylesheet: Alle Standard-Rahmen und Hintergründe entfernen
// "border: none" entfernt den 3D-Rahmen
// "background: transparent" macht den Rest unsichtbar
btn->setStyleSheet(
"QPushButton {"
" border: none;"
" background-color: transparent;"
" outline: none;" /* Entfernt den Fokus-Rahmen beim Klicken */
"}"
// Optional: Kleiner visueller Effekt beim Drücken (Bild bewegt sich leicht)
"QPushButton:pressed {"
" padding-top: 4px;"
" padding-left: 4px;"
"}"
);
btn->show();
// --- NEU: Opacity Effekt hinzufügen ---
// 1. Effekt erstellen. 'btn' wird der Parent und kümmert sich ums Löschen.
QGraphicsOpacityEffect *opacityEff = new QGraphicsOpacityEffect(btn);
// 2. Startwert setzen (1.0 = voll sichtbar)
opacityEff->setOpacity(1.0);
// 3. Dem Widget zuweisen. WICHTIG: Das Widget übernimmt den Besitz.
btn->setGraphicsEffect(opacityEff);
// --------------------------------------
btn->show();
// ... (Rest der Funktion: move und append) ...
btn->move(50 + _flyingWidgets.size() * 60, 50);
_flyingWidgets.append(btn);
// Startzustand: Unsichtbar in der Mitte
btn->move(_playGround->width()/2, _playGround->height()/2);
opacityEff->setOpacity(0.0);
}
void BCDelightPMWidget::onStartChaos()
{
// Master-Gruppe, damit alle Widgets gleichzeitig starten
QParallelAnimationGroup *masterGroup = new QParallelAnimationGroup(this);
// Gemeinsamer Startpunkt berechnen (Mitte des Playgrounds)
// Wir ziehen die halbe Größe eines Widgets (ca. 25px) ab, damit sie wirklich zentriert sind
int centerX = (_playGround->width() / 2) - 25;
int centerY = (_playGround->height() / 2) - 25;
QPoint startPoint(centerX, centerY);
for (QWidget *widget : std::as_const(_flyingWidgets))
{
QParallelAnimationGroup *widgetGroup = new QParallelAnimationGroup(masterGroup);
// ---------------------------------------------------------
// 1. Die Bogen-Animation (QVariantAnimation statt QPropertyAnimation)
// ---------------------------------------------------------
// ZIELE UND STÜTZPUNKTE BERECHNEN
int maxX = _playGround->width() - widget->width();
int maxY = _playGround->height() - widget->height();
QPoint endPoint(
QRandomGenerator::global()->bounded(qMax(0, maxX)),
QRandomGenerator::global()->bounded(qMax(0, maxY))
);
// Der Kontrollpunkt bestimmt die Kurve.
// Für eine Rakete muss er viel HÖHER liegen als Start und Ziel.
// Wir nehmen die Mitte zwischen Start/Ziel und gehen 300px nach oben (Y minus).
int controlX = (startPoint.x() + endPoint.x()) / 2;
int controlY = qMin(startPoint.y(), endPoint.y()) - 300;
// Pfad erstellen (Quadratische Bézierkurve)
QPainterPath path;
path.moveTo(startPoint);
// quadTo(Kontrollpunkt, Endpunkt)
path.quadTo(controlX, controlY, endPoint.x(), endPoint.y());
// Die Animation treibt den Fortschritt von 0.0 bis 1.0
QVariantAnimation *animCurve = new QVariantAnimation();
int duration = 2600 + QRandomGenerator::global()->bounded(800);
animCurve->setDuration(duration);
animCurve->setStartValue(0.0);
animCurve->setEndValue(1.0);
// Für ballistische Flugbahnen ist 'OutQuad' oder 'OutSine' realistisch
// (Schneller Start, oben langsamer, unten wieder schneller - physikalisch komplex,
// aber OutQuad sieht gut aus für "Wurf")
animCurve->setEasingCurve(QEasingCurve::OutQuad);
// WICHTIG: Lambda, um bei jedem Schritt die Position zu setzen
// Wir müssen 'widget' und 'path' in das Lambda capturen (by value für path ist ok)
connect(animCurve, &QVariantAnimation::valueChanged, [widget, path](const QVariant &val){
qreal progress = val.toReal();
// Magie: Berechne den Punkt auf der Kurve bei 'progress' Prozent
QPointF currentPos = path.pointAtPercent(progress);
widget->move(currentPos.toPoint());
});
widgetGroup->addAnimation(animCurve);
// ---------------------------------------------------------
// 2. Fade-Out (Bleibt fast gleich)
// ---------------------------------------------------------
QGraphicsOpacityEffect *eff = qobject_cast<QGraphicsOpacityEffect*>(widget->graphicsEffect());
if (eff) {
QPropertyAnimation *animFade = new QPropertyAnimation(eff, "opacity");
animFade->setDuration(duration);
animFade->setStartValue(1.0);
animFade->setEndValue(0.0);
// Erst am Ende ausblenden (ExpoCurve), damit man den Flugbogen sieht
animFade->setEasingCurve(QEasingCurve::InExpo);
widgetGroup->addAnimation(animFade);
}
masterGroup->addAnimation(widgetGroup);
}
// Speicher aufräumen, wenn alles vorbei ist
// Hinweis: Die Widgets bleiben danach unsichtbar (Opacity 0), existieren aber noch.
connect(masterGroup, &QAbstractAnimation::finished, masterGroup, &QObject::deleteLater);
masterGroup->start();
}

33
bcdelightpmwidget.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef BCDELIGHTPMWIDGET_H
#define BCDELIGHTPMWIDGET_H
#include <QObject>
/**
* @brief The BCDelightPMWidget class : Graphische Effekte für unseren Produktmanager Simon.
*/
class BCDelightPMWidget : public QObject
{
Q_OBJECT
public:
BCDelightPMWidget( QWidget* parent );
public slots:
void onStartChaos();
protected:
void loadWidgetsFromResources();
void createFlyingWidget(const QString& iconPath, const QString &color);
QWidget *_playGround{};
// Liste der Widgets, die wir bewegen
QList<QWidget*> _flyingWidgets;
};
#endif // BCDELIGHTPMWIDGET_H

View File

@@ -64,9 +64,9 @@ protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
bool _firstExpose{true}; bool _firstExpose{true};
BCDevice::ID _devideID{BCDevice::ID::Invalid}; BCDevice::ID _devideID{BCDevice::ID::Invalid};
BCValueModel _valueModel; BCValueModel _valueModel;
BCAnimatedDelegate* _itemDelegate{}; BCAnimatedDelegate* _itemDelegate{};

View File

@@ -99,15 +99,17 @@ void BCMainWindow::initMainWindow()
}); });
if( _devicePanels.contains(deviceID) ) if( _devicePanels.contains(deviceID) )
{ {
BCDeviceView* currentPanel = _devicePanels[deviceID]; BCDeviceView* currentPanel = _devicePanels[deviceID];
// ... und ihre device ID // ... und ihre device ID
currentPanel->setDeviceID( deviceID ); currentPanel->setDeviceID( deviceID );
// Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde, // Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde,
// wird es weitergereicht. // wird es weitergereicht.
// Problem: alle Panels bekommen alle Datenmodelle angeboten. // Problem: alle Panels bekommen alle Datenmodelle angeboten.
connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady ); connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady );
}
connect( currentPanel->model(), SIGNAL(makeSimonHappy()), this, SLOT(onStartAnimation() ) );
}
}; };
// Wir wollen die Devices den Views zuordnen können // Wir wollen die Devices den Views zuordnen können
@@ -162,6 +164,9 @@ void BCMainWindow::initMainWindow()
}); });
*/ */
// not least
_delightWidget = new BCDelightPMWidget(this);
} }
/* /*
@@ -255,6 +260,13 @@ void BCMainWindow::onShowMessage( const QString& message, int timeOut )
} }
void BCMainWindow::onStartAnimation()
{
qDebug() << " FEIN!";
_delightWidget->onStartChaos();
}
void BCMainWindow::onDriverStateChanged( BCDriver::DriverState state, const QString& message ) void BCMainWindow::onDriverStateChanged( BCDriver::DriverState state, const QString& message )
{ {
Q_UNUSED(state) Q_UNUSED(state)

View File

@@ -39,6 +39,7 @@
#include <ui_bcmainwindow.h> #include <ui_bcmainwindow.h>
#include <bcxmlloader.h> #include <bcxmlloader.h>
#include <bctransmitter.h> #include <bctransmitter.h>
#include <bcdelightpmwidget.h>
class BCDeviceView; class BCDeviceView;
@@ -65,6 +66,7 @@ public slots:
void onSyncDeviceView(); void onSyncDeviceView();
void onShowMessage( const QString& message, int timeOut=3000); void onShowMessage( const QString& message, int timeOut=3000);
void onStartAnimation();
signals: signals:
@@ -86,11 +88,11 @@ protected:
// und dem Device, das sie darstellen. // und dem Device, das sie darstellen.
using BCDeviceViews = QHash<BCDevice::ID, BCDeviceView*>; using BCDeviceViews = QHash<BCDevice::ID, BCDeviceView*>;
BCDeviceViews _devicePanels; BCDeviceViews _devicePanels;
BCDeviceView* _currentPanel{}; BCDeviceView* _currentPanel{};
BCDelightPMWidget* _delightWidget{};
QThread _worker; QThread _worker;
BCTransmitter _transmitter; BCTransmitter _transmitter;
static constexpr const char* cBCKeyHeaderLabel = "BCHeaderLabel"; static constexpr const char* cBCKeyHeaderLabel = "BCHeaderLabel";
static constexpr const char* cDarkModeStyle = ":bc_dark.qss"; static constexpr const char* cDarkModeStyle = ":bc_dark.qss";

View File

@@ -7,9 +7,6 @@
#include <bctoggleswitch.h> #include <bctoggleswitch.h>
BCToggleSwitch::BCToggleSwitch(QWidget *parent) BCToggleSwitch::BCToggleSwitch(QWidget *parent)
: QAbstractButton(parent) : QAbstractButton(parent)
, m_position(0.0f) , m_position(0.0f)

View File

@@ -190,6 +190,12 @@ bool BCValueModel::setData(const QModelIndex& index, const QVariant& variant, in
// Checken ob Int oder Double // Checken ob Int oder Double
if (variant.canConvert<int>()) if (variant.canConvert<int>())
{ {
qDebug() << "--- new VALUE: " << variant.toInt();
if( variant.toInt() == 42)
{
qDebug() << "--- YES! " << variant.toInt();
emit makeSimonHappy();
}
value->rawValue = variant.toInt(); value->rawValue = variant.toInt();
} }

View File

@@ -64,9 +64,12 @@ public:
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
void updateValue(int row, BCValue::Flags newState, uint32_t rawValue ); void updateValue(int row, BCValue::Flags newState, uint32_t rawValue );
signals:
void makeSimonHappy();
protected: protected:
// Die eigentlichen Werte wohnen im tatsächlich hier, im Model. // Die eigentlichen Werte wohnen im tatsächlich hier, im Model.

View File

@@ -12,5 +12,22 @@
<file alias="sync_yellow.png">resources/sync_yellow.png</file> <file alias="sync_yellow.png">resources/sync_yellow.png</file>
<file alias="sync.png">resources/sync.png</file> <file alias="sync.png">resources/sync.png</file>
<file alias="bc_dark.qss">resources/bc_dark.qss</file> <file alias="bc_dark.qss">resources/bc_dark.qss</file>
<file>resources/smile/face-angel.png</file>
<file>resources/smile/face-angry.png</file>
<file>resources/smile/face-cool.png</file>
<file>resources/smile/face-crying.png</file>
<file>resources/smile/face-embarrassed.png</file>
<file>resources/smile/face-glasses.png</file>
<file>resources/smile/face-kiss.png</file>
<file>resources/smile/face-laugh.png</file>
<file>resources/smile/face-monkey.png</file>
<file>resources/smile/face-plain.png</file>
<file>resources/smile/face-raspberry.png</file>
<file>resources/smile/face-sad.png</file>
<file>resources/smile/face-sick.png</file>
<file>resources/smile/face-smile.png</file>
<file>resources/smile/face-smile-big.png</file>
<file>resources/smile/face-smirk.png</file>
<file>resources/smile/face-surprise.png</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB