#!/usr/bin/env python # -*- coding: utf-8 -*- from random import randint from PyQt5.Qt import QSpinBox from PyQt5.QtCore import QTimer, Qt, QRectF, QSize from PyQt5.QtGui import QPixmap, QIcon, QPainter, QPainterPath, QColor, QFont from PyQt5.QtWidgets import QWidget, QFormLayout, QRadioButton, QPushButton,\ QColorDialog, QProgressBar import math class WaterRippleProgressBar(QProgressBar): # 浪高百分比 waterHeight = 1 # 密度 waterDensity = 1 # 样式1为矩形, 0为圆形 styleType = 1 # 文字颜色 textColor = Qt.white # 背景颜色 backgroundColor = Qt.gray # 波浪颜色1 waterColor1 = QColor(33, 178, 148) # 波浪颜色2 waterColor2 = QColor(33, 178, 148, 100) def __init__(self, *args, **kwargs): super(WaterRippleProgressBar, self).__init__(*args, **kwargs) self._offset = 0 # 每隔100ms刷新波浪(模拟波浪动态) self._updateTimer = QTimer(self, timeout=self.update) self._updateTimer.start(100) def setRange(self, minValue, maxValue): if minValue == maxValue == 0: return # 不允许设置busy状态 super(WaterRippleProgressBar, self).setRange(minValue, maxValue) def setMinimum(self, value): if value == self.maximum() == 0: return # 不允许设置busy状态 super(WaterRippleProgressBar, self).setMinimum(value) def setMaximum(self, value): if value == self.minimum() == 0: return # 不允许设置busy状态 super(WaterRippleProgressBar, self).setMaximum(value) def setWaterHeight(self, height): """设置浪高""" self.waterHeight = height self.update() def setWaterDensity(self, density): """设置密度""" self.waterDensity = density self.update() def setStyleType(self, style): """设置类型""" self.styleType = style self.update() def sizeHint(self): return QSize(100, 100) def paintEvent(self, event): if self.minimum() == self.maximum() == 0: return # 正弦曲线公式 y = A * sin(ωx + φ) + k # 当前值所占百分比 percent = 1 - (self.value() - self.minimum()) / \ (self.maximum() - self.minimum()) # w表示周期,6为人为定义 w = 6 * self.waterDensity * math.pi / self.width() # A振幅 高度百分比,1/26为人为定义 A = self.height() * self.waterHeight * 1 / 26 # k 高度百分比 k = self.height() * percent # 波浪1 waterPath1 = QPainterPath() waterPath1.moveTo(0, self.height()) # 起点在左下角 # 波浪2 waterPath2 = QPainterPath() waterPath2.moveTo(0, self.height()) # 起点在左下角 # 偏移 self._offset += 0.6 if self._offset > self.width() / 2: self._offset = 0 for i in range(self.width() + 1): # 从x轴开始计算y轴点 y = A * math.sin(w * i + self._offset) + k waterPath1.lineTo(i, y) # 相对第一条需要进行错位 y = A * math.sin(w * i + self._offset + self.width() / 2 * A) + k waterPath2.lineTo(i, y) # 封闭两条波浪,形成一个 U形 上面加波浪的封闭区间 waterPath1.lineTo(self.width(), self.height()) waterPath1.lineTo(0, self.height()) waterPath2.lineTo(self.width(), self.height()) waterPath2.lineTo(0, self.height()) # 整体形状(矩形或者圆形) bgPath = QPainterPath() if self.styleType: bgPath.addRect(QRectF(self.rect())) else: radius = min(self.width(), self.height()) bgPath.addRoundedRect(QRectF(self.rect()), radius, radius) # 开始画路径 painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) # 设置没有画笔 painter.setPen(Qt.NoPen) if not self.styleType: # 圆形 painter.setClipPath(bgPath) # 先整体绘制背景,然后再在背景上方绘制两条波浪 painter.save() painter.setBrush(self.backgroundColor) painter.drawPath(bgPath) painter.restore() # 波浪1 painter.save() painter.setBrush(self.waterColor1) painter.drawPath(waterPath1) painter.restore() # 波浪2 painter.save() painter.setBrush(self.waterColor2) painter.drawPath(waterPath2) painter.restore() # 绘制文字 if not self.isTextVisible(): return painter.setPen(self.textColor) font = self.font() or QFont() font.setPixelSize(int(min(self.width(), self.height()) / 2)) painter.setFont(font) painter.drawText(self.rect(), Qt.AlignCenter, '%d%%' % (self.value() / self.maximum() * 100)) class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.resize(800, 600) self.bar = WaterRippleProgressBar(self) self.bar.setMinimumSize(400, 400) self.bar.setMaximumSize(400, 400) layout = QFormLayout(self) layout.addWidget(QRadioButton( '矩形', self, checked=True, clicked=lambda: self.bar.setStyleType(1))) layout.addWidget( QRadioButton('圆形', clicked=lambda: self.bar.setStyleType(0))) layout.addWidget( QPushButton('设置背景颜色', self, clicked=self.chooseBackgroundColor)) layout.addWidget( QPushButton('设置文字颜色', self, clicked=self.chooseTextColor)) layout.addWidget( QPushButton('设置波浪1颜色', self, clicked=self.chooseWaterColor1)) layout.addWidget( QPushButton('设置波浪2颜色', self, clicked=self.chooseWaterColor2)) layout.addWidget( QPushButton('设置随机0-100固定值', self, clicked=self.setRandomValue)) layout.addRow('振幅(浪高)', QSpinBox(self, value=1, valueChanged=self.bar.setWaterHeight)) layout.addRow('周期(密度)', QSpinBox(self, value=1, valueChanged=self.bar.setWaterDensity)) layout.addWidget(self.bar) # 动态设置进度条的值 self._valueTimer = QTimer(self, timeout=self.updateValue) self._valueTimer.start(100) def chooseBackgroundColor(self): """设置背景颜色""" col = QColorDialog.getColor(self.bar.backgroundColor, self) if not col.isValid(): return self.bar.backgroundColor = col pix = QPixmap(16, 16) pix.fill(col) self.sender().setIcon(QIcon(pix)) def chooseTextColor(self): """设置文字颜色""" col = QColorDialog.getColor(self.bar.textColor, self) if not col.isValid(): return self.bar.textColor = col pix = QPixmap(16, 16) pix.fill(col) self.sender().setIcon(QIcon(pix)) def chooseWaterColor1(self): """设置波浪1颜色""" col = QColorDialog.getColor(self.bar.waterColor1, self) if not col.isValid(): return self.bar.waterColor1 = col pix = QPixmap(16, 16) pix.fill(col) self.sender().setIcon(QIcon(pix)) def chooseWaterColor2(self): """设置波浪2颜色""" col = QColorDialog.getColor(self.bar.waterColor2, self) if not col.isValid(): return self.bar.waterColor2 = col pix = QPixmap(16, 16) pix.fill(col) self.sender().setIcon(QIcon(pix)) def setRandomValue(self): """设置随机0-100值,并停止自增""" self._valueTimer.stop() self.bar.setValue(randint(0, 100)) def updateValue(self): value = self.bar.value() + 1 if value > self.bar.maximum(): value = 0 self.bar.setValue(value) if __name__ == '__main__': import sys from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) w = Window() w.show() sys.exit(app.exec_())