diff --git a/QPushButton/Data/Images/avatar.jpg b/QPushButton/Data/Images/avatar.jpg new file mode 100644 index 0000000..32856da Binary files /dev/null and b/QPushButton/Data/Images/avatar.jpg differ diff --git a/QPushButton/README.md b/QPushButton/README.md index 2328a51..1d9556c 100644 --- a/QPushButton/README.md +++ b/QPushButton/README.md @@ -5,6 +5,8 @@ - [按钮底部线条进度](#2按钮底部线条进度) - [按钮文字旋转进度](#3按钮文字旋转进度) - [按钮常用信号](#4按钮常用信号) + - [旋转动画按钮](#5旋转动画按钮) + - [弹性动画按钮](#6弹性动画按钮) ## 1、普通样式 [运行 NormalStyle.py](NormalStyle.py) @@ -33,4 +35,14 @@ 根据官网文档 https://doc.qt.io/qt-5/qabstractbutton.html#signals 中的信号介绍编写 按钮的点击、按下、释放、选中信号演示 -![SignalsExample](ScreenShot/SignalsExample.gif) \ No newline at end of file +![SignalsExample](ScreenShot/SignalsExample.gif) + +## 5、旋转动画按钮 +[运行 RotateButton.py](RotateButton.py) + +![RotateButton](ScreenShot/RotateButton.gif) + +## 6、弹性动画按钮 +[运行 RubberBandButton.py](RubberBandButton.py) + +![RubberBandButton](ScreenShot/RubberBandButton.gif) \ No newline at end of file diff --git a/QPushButton/RotateButton.py b/QPushButton/RotateButton.py new file mode 100644 index 0000000..e9b9f08 --- /dev/null +++ b/QPushButton/RotateButton.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on 2019年1月2日 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: Widgets.RotateButton +@description: +""" + +import os +import sys + +try: + from PyQt5.QtCore import QPointF, QPropertyAnimation, QRectF, Qt, pyqtProperty + from PyQt5.QtGui import QColor, QImage, QPainter, QPainterPath, QPixmap + from PyQt5.QtWidgets import ( + QGraphicsDropShadowEffect, + QPushButton, + QStyle, + QStyleOptionButton, + QStylePainter, + QApplication, + QWidget, + QHBoxLayout, + ) +except ImportError: + from PySide2.QtCore import QPointF, QPropertyAnimation, QRectF, Qt, pyqtProperty + from PySide2.QtGui import QColor, QImage, QPainter, QPainterPath, QPixmap + from PySide2.QtWidgets import ( + QGraphicsDropShadowEffect, + QPushButton, + QStyle, + QStyleOptionButton, + QStylePainter, + QApplication, + QWidget, + QHBoxLayout, + ) + + +class RotateButton(QPushButton): + + STARTVALUE = 0 # 起始旋转角度 + ENDVALUE = 360 # 结束旋转角度 + DURATION = 540 # 动画完成总时间 + + def __init__(self, *args, **kwargs): + super(RotateButton, self).__init__(*args, **kwargs) + self.setCursor(Qt.PointingHandCursor) + self._angle = 0 # 角度 + self._padding = 10 # 阴影边距 + self._image = "" # 图片路径 + self._shadowColor = QColor(33, 33, 33) # 阴影颜色 + self._pixmap = None # 图片对象 + # 属性动画 + self._animation = QPropertyAnimation(self, b"angle", self) + self._animation.setLoopCount(1) # 只循环一次 + + def paintEvent(self, event): + """绘制事件""" + text = self.text() + option = QStyleOptionButton() + self.initStyleOption(option) + option.text = "" # 不绘制文字 + painter = QStylePainter(self) + painter.setRenderHint(QStylePainter.Antialiasing) + painter.setRenderHint(QStylePainter.HighQualityAntialiasing) + painter.setRenderHint(QStylePainter.SmoothPixmapTransform) + painter.drawControl(QStyle.CE_PushButton, option) + # 变换坐标为正中间 + painter.translate(self.rect().center()) + painter.rotate(self._angle) # 旋转 + + # 绘制图片 + if self._pixmap and not self._pixmap.isNull(): + w = self.width() + h = self.height() + pos = QPointF(-self._pixmap.width() / 2, -self._pixmap.height() / 2) + painter.drawPixmap(pos, self._pixmap) + elif text: + # 在变换坐标后的正中间画文字 + fm = self.fontMetrics() + w = fm.width(text) + h = fm.height() + rect = QRectF(0 - w * 2, 0 - h, w * 2 * 2, h * 2) + painter.drawText(rect, Qt.AlignCenter, text) + else: + super(RotateButton, self).paintEvent(event) + + def enterEvent(self, _): + """鼠标进入事件""" + # 设置阴影 + # 边框阴影效果 + effect = QGraphicsDropShadowEffect(self) + effect.setBlurRadius(self._padding * 2) + effect.setOffset(0, 0) + effect.setColor(self._shadowColor) + self.setGraphicsEffect(effect) + + # 开启旋转动画 + self._animation.stop() + cv = self._animation.currentValue() or self.STARTVALUE + self._animation.setDuration( + self.DURATION if cv == 0 else int(cv / self.ENDVALUE * self.DURATION) + ) + self._animation.setStartValue(cv) + self._animation.setEndValue(self.ENDVALUE) + self._animation.start() + + def leaveEvent(self, _): + """鼠标离开事件""" + # 取消阴影 + self.setGraphicsEffect(None) + + # 旋转动画 + self._animation.stop() + cv = self._animation.currentValue() or self.ENDVALUE + self._animation.setDuration(int(cv / self.ENDVALUE * self.DURATION)) + self._animation.setStartValue(cv) + self._animation.setEndValue(self.STARTVALUE) + self._animation.start() + + def setPixmap(self, path): + if not os.path.exists(path): + self._image = "" + self._pixmap = None + return + self._image = path + size = ( + max( + min(self.width(), self.height()), + min(self.minimumWidth(), self.minimumHeight()), + ) + - self._padding + ) # 需要边距的边框 + radius = int(size / 2) + image = QImage(size, size, QImage.Format_ARGB32_Premultiplied) + image.fill(Qt.transparent) # 填充背景为透明 + pixmap = QPixmap(path).scaled( + size, size, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation + ) + # QPainter + painter = QPainter() + painter.begin(image) + painter.setRenderHint(QPainter.Antialiasing, True) + painter.setRenderHint(QPainter.SmoothPixmapTransform, True) + # QPainterPath + path = QPainterPath() + path.addRoundedRect(0, 0, size, size, radius, radius) + # 切割圆 + painter.setClipPath(path) + painter.drawPixmap(0, 0, pixmap) + painter.end() + self._pixmap = QPixmap.fromImage(image) + self.update() + + def pixmap(self): + return self._pixmap + + @pyqtProperty(str) + def image(self): + return self._image + + @image.setter + def image(self, path): + self.setPixmap(path) + + @pyqtProperty(int) + def angle(self): + return self._angle + + @angle.setter + def angle(self, value): + self._angle = value + self.update() + + @pyqtProperty(int) + def padding(self): + return self._padding + + @padding.setter + def padding(self, value): + self._padding = value + + @pyqtProperty(QColor) + def shadowColor(self): + return self._shadowColor + + @shadowColor.setter + def shadowColor(self, color): + self._shadowColor = QColor(color) + + +class TestWindow(QWidget): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + layout = QHBoxLayout(self) + + btn = RotateButton("pyqt.site", self) + btn.setMinimumHeight(96) + btn.setToolTip("旋转按钮") + layout.addWidget(btn) + + btn = RotateButton("", self) + btn.setMinimumHeight(96) + btn.setObjectName("imageLabel1") + btn.setPixmap("./Data/Images/avatar.jpg") + layout.addWidget(btn) + + btn = RotateButton("", self) + btn.setMinimumHeight(96) + btn.setObjectName("imageLabel2") + layout.addWidget(btn) + + +if __name__ == "__main__": + import cgitb + + cgitb.enable(1, None, 5, "text") + + # cd to current dir + os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) + + app = QApplication(sys.argv) + app.setStyleSheet( + """ + RotateButton { + font-size: 48px; + } + #imageLabel1, #imageLabel2 { + background: transparent; + } + #imageLabel2 { + qproperty-image: "./Data/Images/avatar.jpg"; + } + """ + ) + w = TestWindow() + w.show() + sys.exit(app.exec_()) diff --git a/QPushButton/RubberBandButton.py b/QPushButton/RubberBandButton.py new file mode 100644 index 0000000..75af92e --- /dev/null +++ b/QPushButton/RubberBandButton.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on 2019年1月3日 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: Widgets.RubberBandButton +@description: +""" + +try: + from PyQt5.QtCore import ( + QEasingCurve, + QParallelAnimationGroup, + QPropertyAnimation, + QRectF, + Qt, + pyqtProperty, + ) + from PyQt5.QtGui import QColor, QPainter + from PyQt5.QtWidgets import ( + QPushButton, + QStyle, + QStyleOptionButton, + QStylePainter, + QWidget, + QApplication, + QHBoxLayout, + ) +except ImportError: + from PySide2.QtCore import ( + QEasingCurve, + QParallelAnimationGroup, + QPropertyAnimation, + QRectF, + Qt, + Property as pyqtProperty, + ) + from PySide2.QtGui import QColor, QPainter + from PySide2.QtWidgets import ( + QPushButton, + QStyle, + QStyleOptionButton, + QApplication, + QStylePainter, + QWidget, + QHBoxLayout, + ) + + +class RubberBandButton(QPushButton): + + def __init__(self, *args, **kwargs): + self._bgcolor = QColor(kwargs.pop("bgcolor", Qt.green)) + super(RubberBandButton, self).__init__(*args, **kwargs) + self.setFlat(True) + self.setCursor(Qt.PointingHandCursor) + self._width = 0 + self._height = 0 + + def paintEvent(self, event): + self._initAnimate() + painter = QStylePainter(self) + painter.setRenderHint(QPainter.Antialiasing, True) + painter.setRenderHint(QPainter.HighQualityAntialiasing, True) + painter.setRenderHint(QPainter.SmoothPixmapTransform, True) + painter.setBrush(QColor(self._bgcolor)) + painter.setPen(QColor(self._bgcolor)) + painter.drawEllipse( + QRectF( + (self.minimumWidth() - self._width) / 2, + (self.minimumHeight() - self._height) / 2, + self._width, + self._height, + ) + ) + # 绘制本身的文字和图标 + options = QStyleOptionButton() + options.initFrom(self) + size = options.rect.size() + size.transpose() + options.rect.setSize(size) + options.features = QStyleOptionButton.Flat + options.text = self.text() + options.icon = self.icon() + options.iconSize = self.iconSize() + painter.drawControl(QStyle.CE_PushButton, options) + event.accept() + + def _initAnimate(self): + if hasattr(self, "_animate"): + return + self._width = self.minimumWidth() * 7 / 8 + self._height = self.minimumHeight() * 7 / 8 + # self._width=175 + # self._height=175 + + # 宽度动画 + wanimate = QPropertyAnimation(self, b"rWidth") + wanimate.setEasingCurve(QEasingCurve.OutElastic) + wanimate.setDuration(700) + wanimate.valueChanged.connect(self.update) + + # 插入宽度线性值 + wanimate.setKeyValueAt(0, self._width) + # wanimate.setKeyValueAt(0.1, 180) + # wanimate.setKeyValueAt(0.2, 185) + # wanimate.setKeyValueAt(0.3, 190) + # wanimate.setKeyValueAt(0.4, 195) + wanimate.setKeyValueAt(0.5, self._width + 6) + # wanimate.setKeyValueAt(0.6, 195) + # wanimate.setKeyValueAt(0.7, 190) + # wanimate.setKeyValueAt(0.8, 185) + # wanimate.setKeyValueAt(0.9, 180) + wanimate.setKeyValueAt(1, self._width) + + # 高度动画 + hanimate = QPropertyAnimation(self, b"rHeight") + hanimate.setEasingCurve(QEasingCurve.OutElastic) + hanimate.setDuration(700) + + # 插入高度线性值 + hanimate.setKeyValueAt(0, self._height) + # hanimate.setKeyValueAt(0.1, 170) + # hanimate.setKeyValueAt(0.3, 165) + hanimate.setKeyValueAt(0.5, self._height - 6) + # hanimate.setKeyValueAt(0.7, 165) + # hanimate.setKeyValueAt(0.9, 170) + hanimate.setKeyValueAt(1, self._height) + + # 设置动画组 + self._animate = QParallelAnimationGroup(self) + self._animate.addAnimation(wanimate) + self._animate.addAnimation(hanimate) + + def enterEvent(self, event): + super(RubberBandButton, self).enterEvent(event) + self._animate.stop() + self._animate.start() + + @pyqtProperty(int) + def rWidth(self): + return self._width + + @rWidth.setter + def rWidth(self, value): + self._width = value + + @pyqtProperty(int) + def rHeight(self): + return self._height + + @rHeight.setter + def rHeight(self, value): + self._height = value + + @pyqtProperty(QColor) + def bgColor(self): + return self._bgcolor + + @bgColor.setter + def bgColor(self, color): + self._bgcolor = QColor(color) + + +class TestWindow(QWidget): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + layout = QHBoxLayout(self) + layout.addWidget(RubberBandButton("d", self, bgcolor="#01847E")) + layout.addWidget(RubberBandButton("v", self, bgcolor="#7FA91F")) + layout.addWidget(RubberBandButton("N", self, bgcolor="#FC8416")) + layout.addWidget(RubberBandButton("U", self, bgcolor="#66D3c0")) + layout.addWidget(RubberBandButton("a", self, bgcolor="#F28195")) + + +if __name__ == "__main__": + import cgitb, sys + + cgitb.enable(1, None, 5, "text") + + app = QApplication(sys.argv) + app.setStyleSheet( + """ + RubberBandButton { + min-width: 100px; + max-width: 100px; + min-height: 100px; + max-height: 100px; + border: none; + color: white; + outline: none; + margin: 4px; + font-family: webdings; + font-size: 60px; + } + """ + ) + w = TestWindow() + w.show() + sys.exit(app.exec_()) diff --git a/QPushButton/ScreenShot/RotateButton.gif b/QPushButton/ScreenShot/RotateButton.gif new file mode 100644 index 0000000..6b1c3aa Binary files /dev/null and b/QPushButton/ScreenShot/RotateButton.gif differ diff --git a/QPushButton/ScreenShot/RubberBandButton.gif b/QPushButton/ScreenShot/RubberBandButton.gif new file mode 100644 index 0000000..dcf1e81 Binary files /dev/null and b/QPushButton/ScreenShot/RubberBandButton.gif differ