diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 4bfe9d2..b5aaf85 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,6 @@ eclipse.preferences.version=1 encoding//Demo/AutoRestart.py=utf-8 +encoding//Demo/CircleLine.py=utf-8 encoding//Demo/EmbedWindow.py=utf-8 encoding//Demo/FacePoints.py=utf-8 encoding//Demo/FollowWindow.py=utf-8 @@ -20,6 +21,7 @@ encoding//QChart/LineChart.py=utf-8 encoding//QFont/AwesomeFont.py=utf-8 encoding//QFont/Lib/FontAwesome.py=utf-8 encoding//QGraphicsDropShadowEffect/ShadowEffect.py=utf-8 +encoding//QGraphicsView/WorldMap.py=utf-8 encoding//QListView/CustomWidgetSortItem.py=utf-8 encoding//QListView/SortItemByRole.py=utf-8 encoding//QMessageBox/CustomColorIcon.py=utf-8 diff --git a/Demo/CircleLine.py b/Demo/CircleLine.py new file mode 100644 index 0000000..76fe4ee --- /dev/null +++ b/Demo/CircleLine.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2019年3月19日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: CircleLine +@description: +""" + +from math import floor, pi, cos, sin +from random import random, randint +from time import time + +from PyQt5.QtCore import QTimer, Qt +from PyQt5.QtGui import QColor, QPainter, QPainterPath, QPen +from PyQt5.QtWidgets import QWidget + + +__Author__ = 'Irony' +__Copyright__ = 'Copyright (c) 2019' + +# 最小和最大半径、半径阈值和填充圆的百分比 +radMin = 10 +radMax = 80 +filledCircle = 30 # 填充圆的百分比 +concentricCircle = 60 # 同心圆百分比 +radThreshold = 25 # IFF special, over this radius concentric, otherwise filled +# 最小和最大移动速度 +speedMin = 0.3 +speedMax = 0.6 +# 每个圆和模糊效果的最大透明度 +maxOpacity = 0.6 + +colors = [ + QColor(52, 168, 83), + QColor(117, 95, 147), + QColor(199, 108, 23), + QColor(194, 62, 55), + QColor(0, 172, 212), + QColor(120, 120, 120) +] +circleBorder = 10 +backgroundLine = colors[0] +backgroundColor = QColor(38, 43, 46) +backgroundMlt = 0.85 + +lineBorder = 2.5 + +# 最重要的是:包含它们的整个圆和数组的数目 +maxCircles = 8 +points = [] + +# 实验变量 +circleExp = 1 +circleExpMax = 1.003 +circleExpMin = 0.997 +circleExpSp = 0.00004 +circlePulse = False + +# 生成随机整数 a<=x<=b + + +def randint(a, b): + return floor(random() * (b - a + 1) + a) + +# 生成随机小数 + + +def randRange(a, b): + return random() * (b - a) + a + +# 生成接近a的随机小数 + + +def hyperRange(a, b): + return random() * random() * random() * (b - a) + a + + +class Circle: + + def __init__(self, background, width, height): + self.background = background + self.x = randRange(-width / 2, width / 2) + self.y = randRange(-height / 2, height / 2) + self.radius = hyperRange(radMin, radMax) + self.filled = (False if randint( + 0, 100) > concentricCircle else 'full') if self.radius < radThreshold else ( + False if randint(0, 100) > concentricCircle else 'concentric') + self.color = colors[randint(0, len(colors) - 1)] + self.borderColor = colors[randint(0, len(colors) - 1)] + self.opacity = 0.05 + self.speed = randRange(speedMin, speedMax) # * (radMin / self.radius) + self.speedAngle = random() * 2 * pi + self.speedx = cos(self.speedAngle) * self.speed + self.speedy = sin(self.speedAngle) * self.speed + spacex = abs((self.x - (-1 if self.speedx < 0 else 1) * + (width / 2 + self.radius)) / self.speedx) + spacey = abs((self.y - (-1 if self.speedy < 0 else 1) * + (height / 2 + self.radius)) / self.speedy) + self.ttl = min(spacex, spacey) + + +class CircleLineWindow(QWidget): + + def __init__(self, *args, **kwargs): + super(CircleLineWindow, self).__init__(*args, **kwargs) + geometry = QApplication.instance().desktop().availableGeometry() + self.screenWidth = geometry.width() + self.screenHeight = geometry.height() + self._canDraw = True + self._firstDraw = True + self._timer = QTimer(self, timeout=self.update) + + def init(self): + points.clear() + # 链接的最小距离 + self.linkDist = min(self.screenWidth, self.screenHeight) / 2.4 + # 初始化点 + for _ in range(maxCircles * 3): + points.append(Circle('', self.screenWidth, self.screenHeight)) + self.update() + + def resizeEvent(self, event): + super(CircleLineWindow, self).resizeEvent(event) + self.init() + + def showEvent(self, event): + super(CircleLineWindow, self).showEvent(event) + self._canDraw = True + + def hideEvent(self, event): + super(CircleLineWindow, self).hideEvent(event) + # 窗口最小化要停止绘制, 减少cpu占用 + self._canDraw = False + + def paintEvent(self, event): + super(CircleLineWindow, self).paintEvent(event) + if not self._canDraw: + return + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + painter.setRenderHint(QPainter.SmoothPixmapTransform) + painter.save() + painter.fillRect(self.rect(), backgroundColor) + painter.restore() + self.draw(painter) + + def draw(self, painter): + if circlePulse: + if circleExp < circleExpMin or circleExp > circleExpMax: + circleExpSp *= -1 + circleExp += circleExpSp + + painter.translate(self.screenWidth / 2, self.screenHeight / 2) + + if self._firstDraw: + t = time() + self.renderPoints(painter, points) + if self._firstDraw: + self._firstDraw = False + # 此处有个比例关系用于设置timer的时间,如果初始窗口很小,没有比例会导致动画很快 + t = (time() - t) * 1000 + # 比例最大不能超过1920/800 + t = int(min(2.4, self.screenHeight / self.height()) * t) - 1 + print('start timer(%d msec)' % t) + # 开启定时器 + self._timer.start(t) + + def drawCircle(self, painter, circle): + # circle.radius *= circleExp + if circle.background: + circle.radius *= circleExp + else: + circle.radius /= circleExp + radius = circle.radius + + r = radius * circleExp + # 边框颜色设置透明度 + c = QColor(circle.borderColor) + c.setAlphaF(circle.opacity) + + painter.save() + if circle.filled == 'full': + # 设置背景刷 + painter.setBrush(c) + painter.setPen(Qt.NoPen) + else: + # 设置画笔 + painter.setPen( + QPen(c, max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax)))) + + # 画实心圆或者圆圈 + painter.drawEllipse(circle.x - r, circle.y - r, 2 * r, 2 * r) + painter.restore() + + if circle.filled == 'concentric': + r = radius / 2 + # 画圆圈 + painter.save() + painter.setBrush(Qt.NoBrush) + painter.setPen( + QPen(c, max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax)))) + painter.drawEllipse(circle.x - r, circle.y - r, 2 * r, 2 * r) + painter.restore() + + circle.x += circle.speedx + circle.y += circle.speedy + if (circle.opacity < maxOpacity): + circle.opacity += 0.01 + circle.ttl -= 1 + + def renderPoints(self, painter, circles): + for i, circle in enumerate(circles): + if circle.ttl < -20: + # 重新初始化一个 + circle = Circle('', self.screenWidth, self.screenHeight) + circles[i] = circle + self.drawCircle(painter, circle) + + circles_len = len(circles) + for i in range(circles_len - 1): + for j in range(i + 1, circles_len): + deltax = circles[i].x - circles[j].x + deltay = circles[i].y - circles[j].y + dist = pow(pow(deltax, 2) + pow(deltay, 2), 0.5) + # if the circles are overlapping, no laser connecting them + if dist <= circles[i].radius + circles[j].radius: + continue + # otherwise we connect them only if the dist is < linkDist + if dist < self.linkDist: + xi = (1 if circles[i].x < circles[j].x else - + 1) * abs(circles[i].radius * deltax / dist) + yi = (1 if circles[i].y < circles[j].y else - + 1) * abs(circles[i].radius * deltay / dist) + xj = (-1 if circles[i].x < circles[j].x else 1) * \ + abs(circles[j].radius * deltax / dist) + yj = (-1 if circles[i].y < circles[j].y else 1) * \ + abs(circles[j].radius * deltay / dist) + path = QPainterPath() + path.moveTo(circles[i].x + xi, circles[i].y + yi) + path.lineTo(circles[j].x + xj, circles[j].y + yj) +# samecolor = circles[i].color == circles[j].color + c = QColor(circles[i].borderColor) + c.setAlphaF(min(circles[i].opacity, circles[j].opacity) + * ((self.linkDist - dist) / self.linkDist)) + painter.setPen(QPen(c, ( + lineBorder * backgroundMlt if circles[i].background else lineBorder) * ( + (self.linkDist - dist) / self.linkDist))) + painter.drawPath(path) + + +if __name__ == '__main__': + import sys + from PyQt5.QtWidgets import QApplication + app = QApplication(sys.argv) + w = CircleLineWindow() + w.resize(800, 600) + w.show() + sys.exit(app.exec_()) diff --git a/Demo/Data/背景连线动画.html b/Demo/Data/背景连线动画.html new file mode 100644 index 0000000..e6d25d7 --- /dev/null +++ b/Demo/Data/背景连线动画.html @@ -0,0 +1,208 @@ + + + + + 背景连线动画 + + + +
+ +
+ + + diff --git a/Demo/README.md b/Demo/README.md index 6b203bc..c983c2f 100644 --- a/Demo/README.md +++ b/Demo/README.md @@ -16,6 +16,7 @@ - [验证码控件](#14、验证码控件) - [人脸特征点](#15、人脸特征点) - [使用Threading](#16、使用Threading) + - [背景连线动画](#17、背景连线动画) ## 1、重启窗口Widget [运行 RestartWindow.py](RestartWindow.py) @@ -162,4 +163,11 @@ PyQt 结合 Opencv 进行人脸检测; 在PyQt中使用Theading线程 -![QtThreading](ScreenShot/QtThreading.gif) \ No newline at end of file +![QtThreading](ScreenShot/QtThreading.gif) + +## 17、背景连线动画 +[运行 CircleLine.py](CircleLine.py) + +主要参考 [背景连线动画.html](Data/背景连线动画.html) + +![CircleLine](ScreenShot/CircleLine.gif) \ No newline at end of file diff --git a/Demo/ScreenShot/CircleLine.gif b/Demo/ScreenShot/CircleLine.gif new file mode 100644 index 0000000..4672973 Binary files /dev/null and b/Demo/ScreenShot/CircleLine.gif differ diff --git a/README.md b/README.md index c6851f8..8f9f04a 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,7 @@ https://pyqt5.com 社区是专门针对PyQt5学习和提升开设的博客网站 - [验证码控件](Demo/VerificationCode.py) - [人脸特征点](Demo/FacePoints.py) - [使用Threading](Demo/QtThreading.py) + - [背景连线动画](Demo/CircleLine.py) # QQ群