From 5e7c076bc0e5429f088f25263b361f0a75f3c6b1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Aug 2015 15:06:41 +0530 Subject: [PATCH] A new design for the busy spinner --- .../gui2/progress_indicator/__init__.py | 124 ++++++++++++++++-- 1 file changed, 113 insertions(+), 11 deletions(-) diff --git a/src/calibre/gui2/progress_indicator/__init__.py b/src/calibre/gui2/progress_indicator/__init__.py index 163add1313..9f0c09777d 100644 --- a/src/calibre/gui2/progress_indicator/__init__.py +++ b/src/calibre/gui2/progress_indicator/__init__.py @@ -1,17 +1,119 @@ #!/usr/bin/env python2 -# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -from __future__ import with_statement +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2015, Kovid Goyal -__license__ = 'GPL v3' -__copyright__ = '2009, Kovid Goyal ' -__docformat__ = 'restructuredtext en' +from __future__ import (unicode_literals, division, absolute_import, + print_function) +import math +from PyQt5.Qt import ( + Qt, QWidget, QSizePolicy, QSize, QRect, QConicalGradient, QPen, QBrush, + QPainter, QTimer +) -from calibre.constants import plugins -pi, pi_error = plugins['progress_indicator'] +def draw_snake_spinner(painter, rect, angle, light, dark): + painter.setRenderHint(QPainter.Antialiasing) -if pi_error: - raise RuntimeError('Failed to load the Progress Indicator plugin: '+\ - pi_error) + if rect.width() > rect.height(): + delta = (rect.width() - rect.height()) // 2 + rect = rect.adjusted(delta, 0, -delta, 0) + elif rect.height() > rect.width(): + delta = (rect.height() - rect.width()) // 2 + rect = rect.adjusted(0, delta, 0, -delta) + disc_width = max(4, rect.width() // 10) -ProgressIndicator = pi.QProgressIndicator + drawing_rect = QRect(rect.x() + disc_width, rect.y() + disc_width, rect.width() - 2 * disc_width, rect.height() - 2 *disc_width) + try: + angle_for_width = math.degrees(math.atan2(1.3 * disc_width, drawing_rect.width())) + except ZeroDivisionError: + angle_for_width = 5 + + gradient = QConicalGradient(drawing_rect.center(), angle - angle_for_width) + gradient.setColorAt(1, light) + gradient.setColorAt(0, dark) + + painter.setPen(QPen(light, disc_width)) + painter.drawArc(drawing_rect, 0, 360 * 16) + pen = QPen(QBrush(gradient), disc_width) + pen.setCapStyle(Qt.RoundCap) + painter.setPen(pen) + painter.drawArc(drawing_rect, angle * 16, (360 - 2 * angle_for_width) * 16) + +class ProgressSpinner(QWidget): + + def __init__(self, parent=None, size=64, interval=10): + QWidget.__init__(self, parent) + self.setSizePolicy(QSizePolicy( + QSizePolicy.GrowFlag | QSizePolicy.ShrinkFlag, QSizePolicy.GrowFlag | QSizePolicy.ShrinkFlag)) + self._size_hint = QSize(size, size) + self.timer = t = QTimer(self) + t.setInterval(interval) + self.timer.timeout.connect(self.tick) + self.loading_angle = 0 + pal = self.palette() + self.dark = pal.color(pal.Text) + self.light = pal.color(pal.Window) + + @property + def animation_interval(self): + return self.timer.interval() + + @animation_interval.setter + def animation_interval(self, val): + self.timer.setInterval(val) + + def heightForWidth(self, w): + return w + + def set_colors(self, dark, light): + self.dark, self.light = dark, light + self.update() + + def start(self): + self.loading_angle = 0 + self.timer.start() + self.update() + startAnimation = start + + def stop(self): + self.timer.stop() + self.loading_angle = 0 + self.update() + stopAnimation = stop + + @property + def is_animated(self): + return self.timer.isActive() + + @is_animated.setter + def is_animated(self, val): + (self.start if val else self.stop)() + + def isAnimated(self): + return self.is_animated + + def sizeHint(self): + return self._size_hint + + def setSizeHint(self, val): + self._size_hint = val + self.update() + setDisplaySize = setSizeHint + + def tick(self): + self.loading_angle -= 2 + self.loading_angle %= 360 + self.update() + + def paintEvent(self, ev): + draw_snake_spinner(QPainter(self), self.rect(), self.loading_angle, self.light, self.dark) + +ProgressIndicator = ProgressSpinner +if __name__ == '__main__': + from calibre.gui2 import Application + app = Application([]) + w = ProgressSpinner() + w.show() + w.start() + app.exec_() + del app