Exemplo n.º 1
0
class AnimatedButton(QToolButton):
    def __init__(self,
                 on_tick_func,
                 anim_start=0,
                 anim_end=255,
                 anim_duration=300,
                 parent=None):
        super().__init__(parent=parent)
        self._opacity = 0
        self.anim_start = anim_start
        self.anim_end = anim_end
        self.anim_duration = anim_duration

        self.anim = QPropertyAnimation(self, b'opacity')
        self.anim.setDuration(anim_duration)
        self.anim.setStartValue(anim_start)
        self.anim.setEndValue(anim_end)

        self.on_tick = on_tick_func

    def set_anim_start(self, val):
        self.anim_start = val
        self.anim.setStartValue(val)

    def set_anim_end(self, val):
        self.anim_end = val
        self.anim.setEndValue(val)

    def set_anim_duration(self, val):
        self.anim_duration = val
        self.anim.setDuration(val)

    def get_opacity(self):
        return self._opacity

    def set_opacity(self, value):
        self._opacity = value
        if value != 1:
            self.on_tick(self, value)

    opacity = pyqtProperty('int', get_opacity, set_opacity)

    def enterEvent(self, event):
        self.anim.stop()
        self.anim.setStartValue(self.anim.currentValue())
        self.anim.setEndValue(self.anim_end)
        self.anim.start()

        super().enterEvent(event)

    def leaveEvent(self, event):
        self.anim.stop()
        self.anim.setStartValue(self.anim.currentValue())
        self.anim.setEndValue(self.anim_start)
        self.anim.start()

        super().leaveEvent(event)
Exemplo n.º 2
0
class PushButtonFont(QPushButton):

    LoadingText = "\uf110"

    def __init__(self, *args, **kwargs):
        super(PushButtonFont, self).__init__(*args, **kwargs)
        self.fontSize = self.font().pointSize() * 2
        self._rotateAnimationStarted = False
        self._rotateAnimation = QPropertyAnimation(self)
        self._rotateAnimation.setTargetObject(self)
        self._rotateAnimation.setStartValue(1)
        self._rotateAnimation.setEndValue(12)
        self._rotateAnimation.setDuration(1000)
        self._rotateAnimation.setLoopCount(-1)  # 无限循环
        self._rotateAnimation.valueChanged.connect(self.update)
        self.clicked.connect(self._onClick)

    def paintEvent(self, _):
        option = QStyleOptionButton()
        self.initStyleOption(option)
        painter = QStylePainter(self)
        if self._rotateAnimationStarted:
            option.text = ""
        painter.drawControl(QStyle.CE_PushButton, option)
        if not self._rotateAnimationStarted:
            return
        painter.save()
        font = self.font()
        font.setPointSize(self.fontSize)
        font.setFamily("FontAwesome")
        painter.setFont(font)
        # 变换坐标为正中间
        painter.translate(self.rect().center())
        # 旋转90度
        painter.rotate(self._rotateAnimation.currentValue() * 30)
        fm = self.fontMetrics()
        # 在变换坐标后的正中间画文字
        w = fm.width(self.LoadingText)
        h = fm.height()
        painter.drawText(
            QRectF(0 - w * 2, 0 - h, w * 2 * 2, h * 2), Qt.AlignCenter,
            self.LoadingText)
        painter.restore()

    def _onClick(self):
        if self._rotateAnimationStarted:
            self._rotateAnimationStarted = False
            self._rotateAnimation.stop()
            return
        self._rotateAnimationStarted = True
        self._rotateAnimation.start()

    def update(self, _=None):
        super(PushButtonFont, self).update()
Exemplo n.º 3
0
class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        layout = QVBoxLayout(self)
        layout.addWidget(QPushButton('start', self, clicked=self.onStart))
        layout.addWidget(QPushButton('pause', self, clicked=self.onPause))
        layout.addWidget(QPushButton('resume', self, clicked=self.onResume))

        self._offset = 0
        self.pani = QPropertyAnimation(self, b'offset', self)
        self.pani.setDuration(540)
        self.pani.setLoopCount(1)
        self.pani.setStartValue(0)
        self.pani.setEndValue(360)

    def onPause(self):
        self.pani.stop()
        v = self.pani.currentValue()
        print('current value:', v, 'duration:', int(v / 360 * 540))
        self.pani.setDuration(int(v / 360 * 540))
        self.pani.setStartValue(v)
        self.pani.setEndValue(0)

    def onResume(self):
        self.pani.start()

    def onStart(self):
        self.pani.start()

    @pyqtProperty(int)
    def offset(self):
        return self._offset

    @offset.setter
    def offset(self, o):
        print(o)
        self._offset = o
Exemplo n.º 4
0
class RotateButton(QPushButton):

    STARTVALUE = 0  # 起始旋转角度
    ENDVALUE = 360  # 结束旋转角度
    DURATION = 540  # 动画完成总时间

    def __init__(self, *args, image='', **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)  # 只循环一次
        self.setPixmap(image)
        # 绑定提示框
        #ToolTip.bind(self)

    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 = min(self.width(), self.height()) - 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)
Exemplo n.º 5
0
class CAvatar(QWidget):

    Circle = 0  # 圆圈
    Rectangle = 1  # 圆角矩形
    SizeLarge = QSize(128, 128)
    SizeMedium = QSize(64, 64)
    SizeSmall = QSize(32, 32)
    StartAngle = 0  # 起始旋转角度
    EndAngle = 360  # 结束旋转角度

    def __init__(self,
                 *args,
                 shape=0,
                 url='',
                 cacheDir=False,
                 size=QSize(64, 64),
                 animation=False,
                 **kwargs):
        super(CAvatar, self).__init__(*args, **kwargs)
        self.url = ''
        self._angle = 0  # 角度
        self.pradius = 0  # 加载进度条半径
        self.animation = animation  # 是否使用动画
        self._movie = None  # 动态图
        self._pixmap = QPixmap()  # 图片对象
        self.pixmap = QPixmap()  # 被绘制的对象
        self.isGif = url.endswith('.gif')
        # 进度动画定时器
        self.loadingTimer = QTimer(self, timeout=self.onLoading)
        # 旋转动画
        self.rotateAnimation = QPropertyAnimation(self,
                                                  b'angle',
                                                  self,
                                                  loopCount=1)
        self.setShape(shape)
        self.setCacheDir(cacheDir)
        self.setSize(size)
        self.setUrl(url)

    def paintEvent(self, event):
        super(CAvatar, self).paintEvent(event)
        # 画笔
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
        # 绘制
        path = QPainterPath()
        diameter = min(self.width(), self.height())
        if self.shape == self.Circle:
            radius = int(diameter / 2)
        elif self.shape == self.Rectangle:
            radius = 4
        halfW = self.width() / 2
        halfH = self.height() / 2
        painter.translate(halfW, halfH)
        path.addRoundedRect(QRectF(-halfW, -halfH, diameter, diameter), radius,
                            radius)
        painter.setClipPath(path)
        # 如果是动画效果
        if self.rotateAnimation.state() == QPropertyAnimation.Running:
            painter.rotate(self._angle)  # 旋转
            painter.drawPixmap(
                QPointF(-self.pixmap.width() / 2, -self.pixmap.height() / 2),
                self.pixmap)
        else:
            painter.drawPixmap(-int(halfW), -int(halfH), self.pixmap)
        # 如果在加载
        if self.loadingTimer.isActive():
            diameter = 2 * self.pradius
            painter.setBrush(
                QColor(45, 140, 240, (1 - self.pradius / 10) * 255))
            painter.setPen(Qt.NoPen)
            painter.drawRoundedRect(
                QRectF(-self.pradius, -self.pradius, diameter, diameter),
                self.pradius, self.pradius)

    def enterEvent(self, event):
        """鼠标进入动画
        :param event:
        """
        if not (self.animation and not self.isGif):
            return
        self.rotateAnimation.stop()
        cv = self.rotateAnimation.currentValue() or self.StartAngle
        self.rotateAnimation.setDuration(540 if cv ==
                                         0 else int(cv / self.EndAngle * 540))
        self.rotateAnimation.setStartValue(cv)
        self.rotateAnimation.setEndValue(self.EndAngle)
        self.rotateAnimation.start()

    def leaveEvent(self, event):
        """鼠标离开动画
        :param event:
        """
        if not (self.animation and not self.isGif):
            return
        self.rotateAnimation.stop()
        cv = self.rotateAnimation.currentValue() or self.EndAngle
        self.rotateAnimation.setDuration(int(cv / self.EndAngle * 540))
        self.rotateAnimation.setStartValue(cv)
        self.rotateAnimation.setEndValue(self.StartAngle)
        self.rotateAnimation.start()

    def onLoading(self):
        """更新进度动画
        """
        if self.loadingTimer.isActive():
            if self.pradius > 9:
                self.pradius = 0
            self.pradius += 1
        else:
            self.pradius = 0
        self.update()

    def onFinished(self):
        """图片下载完成
        """
        self.loadingTimer.stop()
        self.pradius = 0
        reply = self.sender()

        if self.isGif:
            self._movie = QMovie(reply, b'gif', self)
            if self._movie.isValid():
                self._movie.frameChanged.connect(self._resizeGifPixmap)
                self._movie.start()
        else:
            data = reply.readAll().data()
            reply.deleteLater()
            del reply
            self._pixmap.loadFromData(data)
            if self._pixmap.isNull():
                self._pixmap = QPixmap(self.size())
                self._pixmap.fill(QColor(204, 204, 204))
            self._resizePixmap()

    def onError(self, code):
        """下载出错了
        :param code:
        """
        self._pixmap = QPixmap(self.size())
        self._pixmap.fill(QColor(204, 204, 204))
        self._resizePixmap()

    def refresh(self):
        """强制刷新
        """
        self._get(self.url)

    def isLoading(self):
        """判断是否正在加载
        """
        return self.loadingTimer.isActive()

    def setShape(self, shape):
        """设置形状
        :param shape:        0=圆形, 1=圆角矩形
        """
        self.shape = shape

    def setUrl(self, url):
        """设置url,可以是本地路径,也可以是网络地址
        :param url:
        """
        self.url = url
        self._get(url)

    def setCacheDir(self, cacheDir=''):
        """设置本地缓存路径
        :param cacheDir:
        """
        self.cacheDir = cacheDir
        self._initNetWork()

    def setSize(self, size):
        """设置固定尺寸
        :param size:
        """
        if not isinstance(size, QSize):
            size = self.SizeMedium
        self.setMinimumSize(size)
        self.setMaximumSize(size)
        self._resizePixmap()

    @pyqtProperty(int)
    def angle(self):
        return self._angle

    @angle.setter
    def angle(self, value):
        self._angle = value
        self.update()

    def _resizePixmap(self):
        """缩放图片
        """
        if not self._pixmap.isNull():
            self.pixmap = self._pixmap.scaled(self.width(), self.height(),
                                              Qt.IgnoreAspectRatio,
                                              Qt.SmoothTransformation)
        self.update()

    def _resizeGifPixmap(self, _):
        """缩放动画图片
        """
        if self._movie:
            self.pixmap = self._movie.currentPixmap().scaled(
                self.width(), self.height(), Qt.IgnoreAspectRatio,
                Qt.SmoothTransformation)
        self.update()

    def _initNetWork(self):
        """初始化异步网络库
        """
        if not hasattr(qApp, '_network'):
            network = QNetworkAccessManager(self.window())
            setattr(qApp, '_network', network)
        # 是否需要设置缓存
        if self.cacheDir and not qApp._network.cache():
            cache = QNetworkDiskCache(self.window())
            cache.setCacheDirectory(self.cacheDir)
            qApp._network.setCache(cache)

    def _get(self, url):
        """设置图片或者请求网络图片
        :param url:
        """
        if not url:
            self.onError('')
            return
        if url.startswith('http') and not self.loadingTimer.isActive():
            url = QUrl(url)
            request = QNetworkRequest(url)
            request.setHeader(QNetworkRequest.UserAgentHeader, b'CAvatar')
            request.setRawHeader(b'Author', b'Irony')
            request.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                 True)
            if qApp._network.cache():
                request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                                     QNetworkRequest.PreferNetwork)
                request.setAttribute(QNetworkRequest.CacheSaveControlAttribute,
                                     True)
            reply = qApp._network.get(request)
            self.pradius = 0
            self.loadingTimer.start(50)  # 显示进度动画
            reply.finished.connect(self.onFinished)
            reply.error.connect(self.onError)
            return
        self.pradius = 0
        if os.path.exists(url) and os.path.isfile(url):
            if self.isGif:
                self._movie = QMovie(url, parent=self)
                if self._movie.isValid():
                    self._movie.frameChanged.connect(self._resizeGifPixmap)
                    self._movie.start()
            else:
                self._pixmap = QPixmap(url)
                self._resizePixmap()
        else:
            self.onError('')