From 89b8de2025f481a5b709d41d98c1afbcede9f77b Mon Sep 17 00:00:00 2001
From: Irony <892768447@qq.com>
Date: Sat, 16 Jan 2021 14:33:05 +0800
Subject: [PATCH] DWaterProgress
---
QProgressBar/Lib/DWaterProgress.py | 243 +++++++++++++++++++++++++++++
1 file changed, 243 insertions(+)
create mode 100644 QProgressBar/Lib/DWaterProgress.py
diff --git a/QProgressBar/Lib/DWaterProgress.py b/QProgressBar/Lib/DWaterProgress.py
new file mode 100644
index 0000000..0b6e69f
--- /dev/null
+++ b/QProgressBar/Lib/DWaterProgress.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/1/1
+@author: Irony
+@site: https://pyqt5.com , https://github.com/892768447
+@email: 892768447@qq.com
+@file: DWaterProgress
+@see https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp
+@description:
+"""
+import math
+
+from PyQt5.QtCore import pyqtSlot, QTimer, QSizeF, Qt, QRectF, QPointF, QRect, QPoint, QSize
+from PyQt5.QtGui import QImage, QColor, QPainter, QLinearGradient, QGradient, QPainterPath, QPixmap, QBrush, QPen
+from PyQt5.QtSvg import QSvgRenderer
+from PyQt5.QtWidgets import QProgressBar, QGraphicsDropShadowEffect
+
+WATER_FRONT = """
+"""
+WATER_BACK = """
+"""
+
+
+class Pop:
+ # https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp#L36
+
+ def __init__(self, size, xs, ys, xo=0, yo=0):
+ self.size = size
+ self.xSpeed = xs
+ self.ySpeed = ys
+ self.xOffset = xo
+ self.yOffset = yo
+
+
+class DWaterProgress(QProgressBar):
+
+ def __init__(self, *args, **kwargs):
+ super(DWaterProgress, self).__init__(*args, **kwargs)
+ self.waterFrontImage = QImage()
+ self.waterBackImage = QImage()
+ self.waterFrontSvg = QSvgRenderer(WATER_FRONT.encode())
+ self.waterBackSvg = QSvgRenderer(WATER_BACK.encode())
+ self.pops = []
+ self.initPops()
+ self.setTextVisible(True)
+ self.interval = 33
+ self.timer = QTimer(self)
+ self.timer.setInterval(self.interval)
+ self.timer.timeout.connect(self.onTimerOut)
+ self.resizePixmap(self.size())
+ self.frontXOffset = self.width()
+ self.backXOffset = 0
+ effect = QGraphicsDropShadowEffect(self)
+ effect.setOffset(0, 6)
+ effect.setColor(QColor(1, 153, 248, 255 * 5 / 20))
+ effect.setBlurRadius(12)
+ self.setGraphicsEffect(effect)
+
+ def initPops(self):
+ self.pops = [Pop(7, -1.8, 0.6), Pop(8, 1.2, 1.0), Pop(11, 0.8, 1.6)]
+
+ @pyqtSlot()
+ def start(self):
+ self.timer.start()
+
+ @pyqtSlot()
+ def stop(self):
+ self.timer.stop()
+
+ def resizePixmap(self, sz):
+ # https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp#L192
+ # resize water
+ waterWidth = 500 * sz.width() / 100
+ waterHeight = 110 * sz.height() / 100
+ waterSize = QSizeF(waterWidth, waterHeight).toSize()
+
+ if self.waterFrontImage.size() != waterSize:
+ image = QImage(waterWidth, waterHeight, QImage.Format_ARGB32)
+ image.fill(Qt.transparent)
+ waterPainter = QPainter(image)
+ self.waterFrontSvg.render(waterPainter)
+ self.waterFrontImage = image
+
+ if self.waterBackImage.size() != waterSize:
+ image = QImage(waterWidth, waterHeight, QImage.Format_ARGB32)
+ image.fill(Qt.transparent) # partly transparent red-ish background
+ waterPainter = QPainter(image)
+ self.waterBackSvg.render(waterPainter)
+ self.waterBackImage = image
+
+ def onTimerOut(self):
+ # interval can not be zero, and limit to 1
+ self.interval = max(1, self.interval)
+ # move 60% per second
+ frontXDeta = 40.0 / (1000.0 / self.interval)
+ # move 90% per second
+ backXDeta = 60.0 / (1000.0 / self.interval)
+
+ canvasWidth = int(self.width() * self.devicePixelRatioF())
+ self.frontXOffset -= frontXDeta * canvasWidth / 100
+ self.backXOffset += backXDeta * canvasWidth / 100
+
+ if self.frontXOffset > canvasWidth:
+ self.frontXOffset = canvasWidth
+
+ if self.frontXOffset < - (self.waterFrontImage.width() - canvasWidth):
+ self.frontXOffset = canvasWidth
+
+ if self.backXOffset > self.waterBackImage.width():
+ self.backXOffset = 0
+
+ # update pop
+ # move 25% per second default
+ speed = 25 / (1000.0 / self.interval) # 100 / self.height()
+ for pop in self.pops:
+ # yOffset 0 ~ 100
+ pop.yOffset += speed * pop.ySpeed
+ if pop.yOffset < 0:
+ pass
+ if pop.yOffset > self.value():
+ pop.yOffset = 0
+ pop.xOffset = math.sin((pop.yOffset / 100) * 2 * 3.14) * 18 * pop.xSpeed + 50
+ self.update()
+
+ def paint(self, painter):
+ painter.setRenderHint(QPainter.Antialiasing)
+
+ pixelRatio = self.devicePixelRatioF()
+ rect = QRectF(0, 0, self.width() * pixelRatio, self.height() * pixelRatio)
+ sz = QSizeF(self.width() * pixelRatio, self.height() * pixelRatio).toSize()
+
+ self.resizePixmap(sz)
+
+ yOffset = rect.toRect().topLeft().y() + (100 - self.value() - 10) * sz.height() / 100
+
+ # draw water
+ waterImage = QImage(sz, QImage.Format_ARGB32_Premultiplied)
+ waterPainter = QPainter()
+ waterPainter.begin(waterImage)
+ waterPainter.setRenderHint(QPainter.Antialiasing)
+ waterPainter.setCompositionMode(QPainter.CompositionMode_Source)
+
+ pointStart = QPointF(sz.width() / 2, 0)
+ pointEnd = QPointF(sz.width() / 2, sz.height())
+ linear = QLinearGradient(pointStart, pointEnd)
+ startColor = QColor('#1F08FF')
+ startColor.setAlphaF(1)
+ endColor = QColor('#50FFF7')
+ endColor.setAlphaF(0.28)
+ linear.setColorAt(0, startColor)
+ linear.setColorAt(1, endColor)
+ linear.setSpread(QGradient.PadSpread)
+ waterPainter.setPen(Qt.NoPen)
+ waterPainter.setBrush(linear)
+ waterPainter.drawEllipse(waterImage.rect().center(), sz.width() / 2 + 1, sz.height() / 2 + 1)
+
+ waterPainter.setCompositionMode(QPainter.CompositionMode_SourceOver)
+ waterPainter.drawImage(int(self.backXOffset), yOffset, self.waterBackImage)
+ waterPainter.drawImage(int(self.backXOffset) - self.waterBackImage.width(), yOffset, self.waterBackImage)
+ waterPainter.drawImage(int(self.frontXOffset), yOffset, self.waterFrontImage)
+ waterPainter.drawImage(int(self.frontXOffset) - self.waterFrontImage.width(), yOffset, self.waterFrontImage)
+
+ # draw pop
+ if self.value() > 30:
+ for pop in self.pops:
+ popPath = QPainterPath()
+ popPath.addEllipse(pop.xOffset * sz.width() / 100, (100 - pop.yOffset) * sz.height() / 100,
+ pop.size * sz.width() / 100, pop.size * sz.height() / 100)
+ waterPainter.fillPath(popPath, QColor(255, 255, 255, 255 * 0.3))
+
+ if self.isTextVisible():
+ font = waterPainter.font()
+ rectValue = QRect()
+ progressText = self.text().strip('%')
+
+ if progressText == '100':
+ font.setPixelSize(sz.height() * 35 / 100)
+ waterPainter.setFont(font)
+
+ rectValue.setWidth(sz.width() * 60 / 100)
+ rectValue.setHeight(sz.height() * 35 / 100)
+ rectValue.moveCenter(rect.center().toPoint())
+ waterPainter.setPen(Qt.white)
+ waterPainter.drawText(rectValue, Qt.AlignCenter, progressText)
+ else:
+ font.setPixelSize(sz.height() * 40 / 100)
+ waterPainter.setFont(font)
+
+ rectValue.setWidth(sz.width() * 45 / 100)
+ rectValue.setHeight(sz.height() * 40 / 100)
+ rectValue.moveCenter(rect.center().toPoint())
+ rectValue.moveLeft(rect.left() + rect.width() * 0.45 * 0.5)
+
+ waterPainter.setPen(Qt.white)
+ waterPainter.drawText(rectValue, Qt.AlignCenter, progressText)
+ font.setPixelSize(font.pixelSize() / 2)
+ waterPainter.setFont(font)
+ rectPerent = QRect(QPoint(rectValue.right(), rectValue.bottom() - rect.height() * 20 / 100),
+ QPoint(rectValue.right() + rect.width() * 20 / 100, rectValue.bottom()))
+
+ waterPainter.drawText(rectPerent, Qt.AlignCenter, '%')
+
+ waterPainter.end()
+
+ maskPixmap = QPixmap(sz)
+ maskPixmap.fill(Qt.transparent)
+ path = QPainterPath()
+ path.addEllipse(QRectF(0, 0, sz.width(), sz.height()))
+ maskPainter = QPainter()
+ maskPainter.begin(maskPixmap)
+ maskPainter.setRenderHint(QPainter.Antialiasing)
+ maskPainter.setPen(QPen(Qt.white, 1))
+ maskPainter.fillPath(path, QBrush(Qt.white))
+ maskPainter.end()
+
+ mode = QPainter.CompositionMode_SourceIn
+ contentImage = QImage(sz, QImage.Format_ARGB32_Premultiplied)
+ contentPainter = QPainter()
+ contentPainter.begin(contentImage)
+ contentPainter.setCompositionMode(QPainter.CompositionMode_Source)
+ contentPainter.fillRect(contentImage.rect(), Qt.transparent)
+ contentPainter.setCompositionMode(QPainter.CompositionMode_SourceOver)
+ contentPainter.drawImage(0, 0, maskPixmap.toImage())
+ contentPainter.setCompositionMode(mode)
+ contentPainter.drawImage(0, 0, waterImage)
+ contentPainter.setCompositionMode(QPainter.CompositionMode_DestinationOver)
+ contentPainter.end()
+
+ contentImage.setDevicePixelRatio(pixelRatio)
+ painter.drawImage(self.rect(), contentImage)
+
+ def paintEvent(self, event):
+ painter = QPainter(self)
+ self.paint(painter)
+
+ def sizeHint(self):
+ return QSize(100, 100)