class ThrobbingButton(QToolButton): @pyqtProperty(int) def icon_size(self): return self._icon_size @icon_size.setter def icon_size(self, value): self._icon_size = value def __init__(self, *args): QToolButton.__init__(self, *args) # vertically size policy must be expanding for it to align inside a # toolbar self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self._icon_size = -1 QToolButton.setIcon(self, QIcon(I('donate.png'))) self.setText('\xa0') self.animation = QPropertyAnimation(self, b'icon_size', self) self.animation.setDuration(60/72.*1000) self.animation.setLoopCount(4) self.animation.valueChanged.connect(self.value_changed) self.setCursor(Qt.PointingHandCursor) self.animation.finished.connect(self.animation_finished) def animation_finished(self): self.icon_size = self.iconSize().width() def enterEvent(self, ev): self.start_animation() def leaveEvent(self, ev): self.stop_animation() def value_changed(self, val): self.update() def start_animation(self): if config['disable_animations']: return if self.animation.state() != self.animation.Stopped or not self.isVisible(): return size = self.iconSize().width() smaller = int(0.7 * size) self.animation.setStartValue(smaller) self.animation.setEndValue(size) QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection) def stop_animation(self): self.animation.stop() self.animation_finished() def paintEvent(self, ev): size = self._icon_size if self._icon_size > 10 else self.iconSize().width() p = QPainter(self) opt = QStyleOptionToolButton() self.initStyleOption(opt) s = self.style() opt.iconSize = QSize(size, size) s.drawComplexControl(s.CC_ToolButton, opt, p, self)
class ThrobbingButton(QToolButton): def __init__(self, *args): QToolButton.__init__(self, *args) self.animation = QPropertyAnimation(self, 'iconSize', self) self.animation.setDuration(60/72.*1000) self.animation.setLoopCount(4) self.normal_icon_size = QSize(64, 64) self.animation.valueChanged.connect(self.value_changed) self.setCursor(Qt.PointingHandCursor) self.animation.finished.connect(self.animation_finished) def set_normal_icon_size(self, w, h): self.normal_icon_size = QSize(w, h) self.setIconSize(self.normal_icon_size) try: self.setMinimumSize(self.sizeHint()) except: self.setMinimumSize(QSize(w+5, h+5)) def animation_finished(self): self.setIconSize(self.normal_icon_size) def enterEvent(self, ev): self.start_animation() def leaveEvent(self, ev): self.stop_animation() def value_changed(self, val): self.update() def start_animation(self): if config['disable_animations']: return if self.animation.state() != self.animation.Stopped or not self.isVisible(): return size = self.normal_icon_size.width() smaller = int(0.7 * size) self.animation.setStartValue(QSize(smaller, smaller)) self.animation.setEndValue(self.normal_icon_size) QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection) def stop_animation(self): self.animation.stop() self.animation_finished()
class ThrobbingButton(QToolButton): def __init__(self, *args): QToolButton.__init__(self, *args) self.animation = QPropertyAnimation(self, 'iconSize', self) self.animation.setDuration(60 / 72. * 1000) self.animation.setLoopCount(4) self.normal_icon_size = QSize(64, 64) self.animation.valueChanged.connect(self.value_changed) self.setCursor(Qt.PointingHandCursor) self.animation.finished.connect(self.animation_finished) def set_normal_icon_size(self, w, h): self.normal_icon_size = QSize(w, h) self.setIconSize(self.normal_icon_size) try: self.setMinimumSize(self.sizeHint()) except: self.setMinimumSize(QSize(w + 5, h + 5)) def animation_finished(self): self.setIconSize(self.normal_icon_size) def enterEvent(self, ev): self.start_animation() def leaveEvent(self, ev): self.stop_animation() def value_changed(self, val): self.update() def start_animation(self): if config['disable_animations']: return if self.animation.state( ) != self.animation.Stopped or not self.isVisible(): return size = self.normal_icon_size.width() smaller = int(0.7 * size) self.animation.setStartValue(QSize(smaller, smaller)) self.animation.setEndValue(self.normal_icon_size) QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection) def stop_animation(self): self.animation.stop() self.animation_finished()
class SlideFlip(QWidget): # API {{{ # In addition the isVisible() and setVisible() methods must be present def __init__(self, parent): QWidget.__init__(self, parent) self.setGeometry(0, 0, 1, 1) self._current_width = 0 self.before_image = self.after_image = None self.animation = QPropertyAnimation(self, 'current_width', self) self.setVisible(False) self.animation.valueChanged.connect(self.update) self.animation.finished.connect(self.finished) self.flip_forwards = True self.setAttribute(Qt.WA_OpaquePaintEvent) @property def running(self): 'True iff animation is currently running' return self.animation.state() == self.animation.Running def initialize(self, image, forwards=True): ''' Initialize the flipper, causes the flipper to show itself displaying the full `image`. :param image: The image to display as background :param forwards: If True flipper will flip forwards, otherwise backwards ''' self.flip_forwards = forwards self.before_image = QPixmap.fromImage(image) self.after_image = None self.setGeometry(0, 0, image.width(), image.height()) self.setVisible(True) def __call__(self, image, duration=0.5): ''' Start the animation. You must have called :meth:`initialize` first. :param duration: Animation duration in seconds. ''' if self.running: return self.after_image = QPixmap.fromImage(image) if self.flip_forwards: self.animation.setStartValue(image.width()) self.animation.setEndValue(0) t = self.before_image self.before_image = self.after_image self.after_image = t self.animation.setEasingCurve(QEasingCurve(QEasingCurve.InExpo)) else: self.animation.setStartValue(0) self.animation.setEndValue(image.width()) self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo)) self.animation.setDuration(duration * 1000) self.animation.start() # }}} def finished(self): self.setVisible(False) self.before_image = self.after_image = None def paintEvent(self, ev): if self.before_image is None: return canvas_size = self.rect() p = QPainter(self) p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) p.drawPixmap(canvas_size, self.before_image, self.before_image.rect()) if self.after_image is not None: width = self._current_width iw = self.after_image.width() sh = min(self.after_image.height(), canvas_size.height()) if self.flip_forwards: source = QRect(max(0, iw - width), 0, width, sh) else: source = QRect(0, 0, width, sh) target = QRect(source) target.moveLeft(0) p.drawPixmap(target, self.after_image, source) p.end() def set_current_width(self, val): self._current_width = val current_width = pyqtProperty('int', fget=lambda self: self._current_width, fset=set_current_width )
class ThrobbingButton(QToolButton): @pyqtProperty(int) def icon_size(self): return self._icon_size @icon_size.setter def icon_size(self, value): self._icon_size = value def __init__(self, *args): QToolButton.__init__(self, *args) # vertically size policy must be expanding for it to align inside a # toolbar self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding) self._icon_size = -1 QToolButton.setIcon(self, QIcon(I('donate.png'))) self.setText('\xa0') self.animation = QPropertyAnimation(self, b'icon_size', self) self.animation.setDuration(60 / 72. * 1000) self.animation.setLoopCount(4) self.animation.valueChanged.connect(self.value_changed) self.setCursor(Qt.CursorShape.PointingHandCursor) self.animation.finished.connect(self.animation_finished) def animation_finished(self): self.icon_size = self.iconSize().width() def enterEvent(self, ev): self.start_animation() def leaveEvent(self, ev): self.stop_animation() def value_changed(self, val): self.update() def start_animation(self): if config['disable_animations']: return if self.animation.state( ) != QAbstractAnimation.State.Stopped or not self.isVisible(): return size = self.iconSize().width() smaller = int(0.7 * size) self.animation.setStartValue(smaller) self.animation.setEndValue(size) QMetaObject.invokeMethod(self.animation, 'start', Qt.ConnectionType.QueuedConnection) def stop_animation(self): self.animation.stop() self.animation_finished() def paintEvent(self, ev): size = self._icon_size if self._icon_size > 10 else self.iconSize( ).width() p = QPainter(self) opt = QStyleOptionToolButton() self.initStyleOption(opt) s = self.style() opt.iconSize = QSize(size, size) s.drawComplexControl(QStyle.ComplexControl.CC_ToolButton, opt, p, self)
def drawControl(self, element, option, painter, widget): ''' @param: element ControlElement @param: option QStyleOption @param: painter QPainter @param: widget QWidget ''' v_opt = option if element != self.CE_TabBarTab or not isinstance( v_opt, QStyleOptionTab): QProxyStyle.drawControl(element, option, painter, widget) return rect = v_opt.rect selected = v_opt.state & self.State_Selected vertical_tabs = v_opt.shape == QTabBar.RoundedWest text = v_opt.text if selected: # background painter.save() grad = QLinearGradient(rect.topLeft(), rect.topRight()) grad.setColorAt(0, QColor(255, 255, 255, 140)) grad.setColorAt(0, QColor(255, 255, 255, 210)) painter.fillRect(rect.adjusted(0, 0, 0, -1), grad) painter.restore() # shadows painter.setPen(QColor(0, 0, 0, 110)) painter.drawLine(rect.topLeft() + QPoint(1, -1), rect.topRight() - QPoint(0, 1)) painter.drawLine(rect.bottomLeft(), rect.bottomRight()) painter.setPen(QColor(0, 0, 0, 40)) painter.drawLine(rect.topLeft(), rect.bottomLeft()) # highlights painter.setPen(QColor(255, 255, 255, 50)) painter.drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0, 2)) painter.drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(0, 1)) painter.setPen(QColor(255, 255, 255, 40)) painter.drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight()) painter.drawLine(rect.topRight() + QPoint(0, 1), rect.bottomRight() - QPoint(0, 1)) painter.drawLine(rect.bottomLeft() + QPoint(0, -1), rect.bottomRight() - QPoint(0, 1)) m = QTransform() if vertical_tabs: m = QTransform.fromTranslate(rect.left(), rect.bottom()) m.rotate(-90) else: m = QTransform.fromTranslate(rect.left(), rect.top()) draw_rect = QRect(QPoint(0, 0), m.mapRect(rect).size()) painter.save() painter.setTransform(m) icon_rect = QRect(QPoint(8, 0), v_opt.iconSize) text_rect = QRect(icon_rect.topRight() + QPoint(4, 0), draw_rect.size()) text_rect.setRight(draw_rect.width()) icon_rect.translate(0, (draw_rect.height() - icon_rect.height()) / 2) boldFont = QFont(painter.font()) boldFont.setPointSizeF(styleHelper.sidebarFontSize()) boldFont.setBold(True) painter.setFont(boldFont) painter.setPen(selected and QColor(255, 255, 255, 160) or QColor(0, 0, 0, 110)) textFlags = Qt.AlignHCenter | Qt.AlignVCenter painter.drawText(text_rect, textFlags, text) painter.setPen(selected and QColor(60, 60, 60) or styleHelper.panelTextColor()) if widget: fader_key = 'tab_' + text + '_fader' animation_key = 'tab_' + text + '_animation' tab_hover = widget.property('tab_hover') # int fader = widget.property(fader_key) # QPropertyAnimation animation = widget.property(animation_key) if not animation: mut_widget = widget fader = 0 mut_widget.setProperty(fader_key, fader) animation = QPropertyAnimation(mut_widget, fader_key, mut_widget) animation.valueChanged.connect(mut_widget.update) mut_widget.setProperty(animation_key, animation) if text == tab_hover: if animation.state( ) != QAbstractAnimation.Running and fader != 40: animation.stop() animation.setDuration(80) animation.setEndValue(40) animation.start() else: if animation.state( ) != QAbstractAnimation.Running and fader != 0: animation.stop() animation.setDuration(160) animation.setEndValue(0) animation.start() if not selected: painter.save() painter.fillRect(draw_rect, QColor(255, 255, 255, fader)) painter.setPen(QPen(QColor(255, 255, 255, fader), 1.0)) painter.drawLine( draw_rect.topLeft(), vertical_tabs and draw_rect.bottomLeft() or draw_rect.topRight()) painter.drawLine( draw_rect.bottomRight(), vertical_tabs and draw_rect.topRight() or draw_rect.bottomLeft()) painter.restore() if selected: iconMode = QIcon.Selected else: iconMode = QIcon.Normal styleHelper.drawIconWithShadow(v_opt.icon, icon_rect, painter, iconMode) painter.drawText(text_rect.translated(0, -1), textFlags, text) painter.restore()
class SlideFlip(QWidget): # API {{{ # In addition the isVisible() and setVisible() methods must be present def __init__(self, parent): QWidget.__init__(self, parent) self.setGeometry(0, 0, 1, 1) self._current_width = 0 self.before_image = self.after_image = None self.animation = QPropertyAnimation(self, b'current_width', self) self.setVisible(False) self.animation.valueChanged.connect(self.update) self.animation.finished.connect(self.finished) self.flip_forwards = True self.setAttribute(Qt.WA_OpaquePaintEvent) @property def running(self): 'True iff animation is currently running' return self.animation.state() == self.animation.Running def initialize(self, image, forwards=True): ''' Initialize the flipper, causes the flipper to show itself displaying the full `image`. :param image: The image to display as background :param forwards: If True flipper will flip forwards, otherwise backwards ''' self.flip_forwards = forwards self.before_image = QPixmap.fromImage(image) self.after_image = None self.setGeometry(0, 0, image.width(), image.height()) self.setVisible(True) def __call__(self, image, duration=0.5): ''' Start the animation. You must have called :meth:`initialize` first. :param duration: Animation duration in seconds. ''' if self.running: return self.after_image = QPixmap.fromImage(image) if self.flip_forwards: self.animation.setStartValue(image.width()) self.animation.setEndValue(0) t = self.before_image self.before_image = self.after_image self.after_image = t self.animation.setEasingCurve(QEasingCurve(QEasingCurve.InExpo)) else: self.animation.setStartValue(0) self.animation.setEndValue(image.width()) self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo)) self.animation.setDuration(duration * 1000) self.animation.start() # }}} def finished(self): self.setVisible(False) self.before_image = self.after_image = None def paintEvent(self, ev): if self.before_image is None: return canvas_size = self.rect() p = QPainter(self) p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) p.drawPixmap(canvas_size, self.before_image, self.before_image.rect()) if self.after_image is not None: width = self._current_width iw = self.after_image.width() sh = min(self.after_image.height(), canvas_size.height()) if self.flip_forwards: source = QRect(max(0, iw - width), 0, width, sh) else: source = QRect(0, 0, width, sh) target = QRect(source) target.moveLeft(0) p.drawPixmap(target, self.after_image, source) p.end() def set_current_width(self, val): self._current_width = val current_width = pyqtProperty('int', fget=lambda self: self._current_width, fset=set_current_width)