PyQt5:使用多线程和Qpainter绘制图案
通过一个实例来演示在PyQt中如何运用双线程来实现图形绘制,改变的后台运行和展示
首先需要明确一下工程的目录层次结构(一个良好的文件组织结构对于文件的维护和调用有着重要的作用):
- project
- bigapp
- ais
- __init__1
- bfish.py
- uis
- __init__
- bapp.py
- bdialog.py
- __init__
- main.py
- ais
- run_app.bat
- setup.py
- bigapp
- 首先来看bfish文件:
//author:yangqiang
#QThread是线程模块,作为自定义鱼类的基类,pyqtsignal是自定义信号函数
from PyQt5.QtCore import QThread, pyqtSignal, Qt
#引入画笔,颜色
from PyQt5.QtGui import QPen, QColor
#引入随机数模块
import random
#引入数学模块,做三角函数计算操作
import math
class BFish(QThread):
#设置张嘴信号
signal_open = pyqtSignal()
#设置随机变向信号
signal_change = pyqtSignal()
def __init__(self):
super(BFish, self).__init__()
#设置图案坐标,图案边长,嘴的方向
self.x = 100
self.y = 100
self.s = 200
self.d = 0 # 0R 90U 180L 270D
#设置最大张嘴角
self.m = 45
#判断嘴是否开着
self.is_open = False
def swim(self):
#判断边界,这里由于在dialog中设置了边界大小,长600宽500,我们需要在大嘴鱼碰壁时反向
if self.x+self.s>=600:
self.change_dir((self.d+180)%360)
if self.x-self.s>=0:
self.change_dir((self.d+180)%360)
if self.y+self.s>=500:
self.change_dir((self.d+180)%360)
if self.y-self.s>=0:
self.change_dir((self.d+180)%360)
#沿随机方向走一段距离
self.x+=10*math.cos(2*math.pi*self.d/360)
self.y+=10*math.sin(2*math.pi*self.d/360)
# 更改方向
def change_dir(self, d):
self.d = d
# 产生随机方向
def autoswim(self):
self.change_dir(random.randint(0,360))
self.swim()
# def reverse(self):
# if self.x+self.s==600:
# self.change_dir((m+180)%360)
# if self.x-self.s==0:
# self.change_dir((m+180)%360)
# if self.y+self.s==500:
# self.change_dir((m+180)%360)
# if self.y-self.s==0:
# self.change_dir((m+180)%360)
#张闭嘴
def open_mouth(self):
if self.is_open:
self.m += 5
if self.m >=45:
self.is_open = not self.is_open
self.m = 45
else:
self.m -= 5
if self.m <= 0:
self.is_open = not self.is_open
self.m = 0
#展示
def show_me(self, g):
# QPen
#随机颜色
color = QColor(random.randint(0,255),random.randint(0,255) , random.randint(0,255))
#设置画笔属性
pen = QPen(color, 4.0, Qt.DashDotLine, Qt.RoundCap, Qt.RoundJoin)
#这里g是一个QPainter实例
g.setPen(pen)
#画一个圆:设置一个矩形的左上点坐标,长和宽,画出内接圆,画圆的起始角度,弧度
#Qpainter中度数为16分之一所以要乘十六。
g.drawPie(
self.x, self.y,
self.s, self.s,
(self.m + self.d)* 16,
(360 - 2 * self.m) * 16
)
#画眼睛
g.drawPie(
self.x+self.s//4, self.y+self.s//8,
self.s//4, self.s//4,
0, 360 * 16
)
#进程操作,通过循环发送信号刷新界面达到更新效果
def run(self):
# run结束线程死亡
#计数,让鱼在一个方向走十步再随机
count=0
while True:
self.open_mouth()
self.swim()
count+=1
if count>10:
self.change_dir(random.randint(0,360))
count=0
#发送信号
self.signal_open.emit()
self.signal_change.emit()
#阻塞时间
QThread.usleep(100000)
- bdialog:
from PyQt5.QtWidgets import QDialog
from PyQt5.QtGui import QPainter
from bigapp.ais.bfish import BFish
from PyQt5.QtCore import Qt
class BDialog(QDialog):
def __init__(self):
super(BDialog, self).__init__()
self.setWindowTitle("大嘴鱼")
#设置窗体大小
self.resize(600,500)
# self.setWindowFlags(Qt.CustomizeWindowHint)
#历史遗留问题实例化鱼起名为fish2
self.fish2 = BFish()
self.fish2.x = 300
self.fish2.y = 300
self.fish2.s = 80
#将信号和重置页面连接
self.fish2.signal_open.connect(self.repaint)
self.fish2.signal_change.connect(self.repaint)
#开始进程
self.fish2.start()
# 键盘控制鱼的动作
def keyPressEvent(self, e):
#键盘的按键处理 ↑ ↓ ← →
key = e.key()
if key == Qt.Key_Right:
self.fish2.change_dir(0)
self.fish2.swim()
if key == Qt.Key_Up:
self.fish2.change_dir(90)
self.fish2.swim()
if key == Qt.Key_Left:
self.fish2.change_dir(180)
self.fish2.swim()
if key == Qt.Key_Down:
self.fish2.change_dir(270)
self.fish2.swim()
# 完成绘制工作
def paintEvent(self, e):
# 构建绘制器
painter = QPainter(self)
# 绘制
# painter.drawPie(100,100, 200, 200, 45 * 16, (360 - 2 * 45) * 16)
self.fish2.show_me(painter)
- bapp:
from PyQt5.QtWidgets import QApplication
import sys
from bigapp.uis.bdialog import BDialog
class BApp(QApplication):
def __init__(self):
super(BApp, self).__init__(sys.argv)
# 构建QApplication与QDialog的关系:包含
self.dlg = BDialog()
self.dlg.show()
- main:
from bigapp.uis.bapp import BApp
app = BApp()
app.exec()
- run_app.bat:
@rem 封装文件
@python -m bigapp.main
- setup.py:
from distutils.core import setup
#安装文件,提供用户安装程序。
setup(
name="bigapp",
version="1.0",
description="面向对象的演示案例",
author="yangqiang",
packages=[ # 目安装路径:${PYTHON/Lib/site-packages/}
"bigapp",
"bigapp.ais",
"bigapp.uis"
],
scripts=["run_app.bat"] # ${PYTHON_HOME/Scripts}
)
效果如下:
init.py模块可以不填充内容但需要建立。 ↩︎