From 4ce301c1b0ee637507cc92d650585639aace5181 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Dec 2020 18:13:40 +0530 Subject: [PATCH] Work on a new spin animation --- .../progress_indicator/QProgressIndicator.h | 78 +++++++++++++++++++ .../progress_indicator/QProgressIndicator.sip | 23 ++++++ .../gui2/progress_indicator/__init__.py | 25 ++++++ 3 files changed, 126 insertions(+) diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.h b/src/calibre/gui2/progress_indicator/QProgressIndicator.h index 4fd25a5232..615197aa20 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.h +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.h @@ -1,9 +1,87 @@ #pragma once #include +#include #include #include #include +#include +#include + +class SpinAnimator: public QObject { + Q_OBJECT + Q_PROPERTY(float arc_length READ get_arc_length WRITE set_arc_length) + Q_PROPERTY(int arc_rotation READ get_arc_rotation WRITE set_arc_rotation) + Q_PROPERTY(int overall_rotation READ get_overall_rotation WRITE set_overall_rotation) +public: + SpinAnimator(QObject* parent = 0) : + QObject(parent), + m_arc_length(0), + m_arc_rotation(0), + m_overall_rotation(0), + m_has_pending_updates(false), + m_animation(this) + { + QPropertyAnimation *a; +#define S(property, duration) a = new QPropertyAnimation(this, QByteArray(#property), this); a->setEasingCurve(QEasingCurve::InOutCubic); a->setDuration(duration); a->setLoopCount(-1); m_animation.addAnimation(a); + S(arc_length, 1400); + const float arc_length_max = 0.734f, arc_length_min = 0.01f; + a->setStartValue(arc_length_min); + a->setKeyValueAt(0.25, arc_length_min); + a->setKeyValueAt(0.5, arc_length_max); + a->setKeyValueAt(0.75, arc_length_max); + a->setEndValue(arc_length_min); + + S(arc_rotation, 1400); + a->setStartValue(0); + a->setKeyValueAt(0.25, 0); + a->setKeyValueAt(0.5, 45); + a->setKeyValueAt(0.75, 45); + a->setEndValue(360); + + S(overall_rotation, 2000); + a->setStartValue(0); + a->setEndValue(360); +#undef S + } + ~SpinAnimator() { m_animation.stop(); m_animation.clear(); } + void start() { m_animation.start(); } + void stop() { m_animation.stop(); } + bool is_running() { return m_animation.state() == QAbstractAnimation::Running; } + void draw(QPainter &painter, QRect bounds, const QColor &color) { + m_has_pending_updates = false; + painter.save(); + painter.setRenderHint(QPainter::Antialiasing); + QRectF rect(bounds); + float thickness = std::max(3.f, std::min((float)rect.width() / 10.f, 24.f)); + QPen pen(color); + pen.setWidthF(thickness); + float ht = thickness / 2 + 1; + rect.adjust(ht, ht, -ht, -ht); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + int rotated_by = (m_overall_rotation + m_arc_rotation) * 16; + int arc_length = (int)(m_arc_length * 360 * 16); + painter.drawArc(rect, -rotated_by, -arc_length); + painter.restore(); + } + + float get_arc_length() const { return m_arc_length; } + int get_arc_rotation() const { return m_arc_rotation; } + int get_overall_rotation() const { return m_overall_rotation; } +public slots: + void set_arc_length(float val) { m_arc_length = val; notify_of_update(); } + void set_arc_rotation(int val) { m_arc_rotation = val; notify_of_update(); } + void set_overall_rotation(int val) { m_overall_rotation = val; notify_of_update(); } +signals: + void updated(); +private: + float m_arc_length; + int m_arc_rotation, m_overall_rotation; + bool m_has_pending_updates; + void notify_of_update() { if (!m_has_pending_updates) { m_has_pending_updates = true; emit updated(); } } + QParallelAnimationGroup m_animation; +}; /*! \class QProgressIndicator diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.sip b/src/calibre/gui2/progress_indicator/QProgressIndicator.sip index 93969dcbfb..f7dba6b5c5 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.sip +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.sip @@ -12,6 +12,29 @@ void set_no_activate_on_click(QWidget *widget); void draw_snake_spinner(QPainter &painter, QRect rect, int angle, const QColor & light, const QColor & dark); %End +class SpinAnimator : QObject { + +%TypeHeaderCode +#include +%End + +public: + SpinAnimator(QWidget *parent /TransferThis/ = 0); + float get_arc_length() const ; + int get_arc_rotation() const ; + int get_overall_rotation() const ; + void draw(QPainter &painter, QRect bounds, const QColor &color); + void start(); + void stop(); + bool is_running(); +public slots: + void set_arc_length(float val) ; + void set_arc_rotation(int val) ; + void set_overall_rotation(int val) ; +signals: + void updated(); +}; + class QProgressIndicator : QWidget { %TypeHeaderCode diff --git a/src/calibre/gui2/progress_indicator/__init__.py b/src/calibre/gui2/progress_indicator/__init__.py index 96316eebf8..5da401267d 100644 --- a/src/calibre/gui2/progress_indicator/__init__.py +++ b/src/calibre/gui2/progress_indicator/__init__.py @@ -94,6 +94,31 @@ class WaitLayout(QStackedLayout): self.wp.msg = val +def develop(): + from PyQt5.Qt import QPalette, QPainter, QRect + from calibre.gui2 import Application + from calibre_extensions.progress_indicator import SpinAnimator + + class Widget(QWidget): + + def __init__(self): + QWidget.__init__(self) + self.a = SpinAnimator(self) + self.a.updated.connect(self.update) + + def paintEvent(self, ev): + p = QPainter(self) + pal = self.palette() + self.a.draw(p, QRect(0, 0, 64, 64), pal.color(QPalette.ColorRole.WindowText)) + p.end() + + app = Application([]) + w = Widget() + w.show() + w.a.start() + app.exec_() + + if __name__ == '__main__': from calibre.gui2 import Application app = Application([])