结合我的上一篇多线程画板DEMO小程序,我又边学边作了可实现画直线、矩形、填充矩形、圆、椭圆、随手画、文本的画板程序。为减少代码量,所画形状的颜色、线宽、线型、填充形状、字体、字号等采用随机方式,主要是看效果,框架已搭好有兴趣的朋友继续深化代码完成即可。
主界面上设置两个绘画区,左侧画板(实际就是QT5或QT6的标签控件)支持鼠标操作当画板用来绘制各种型号图形,右侧画板(实际也是QT5或QT6的标签控件)采用多线程自动绘制彩色同心圆和彩色文本。
本程序有完整的界面框架:主窗体、主窗体上的菜单栏、工具栏、状态栏。子窗体及子窗体同主窗体交互,各种事件的重载,信号槽的定义和使用,多线程的定义和多线程同窗体数据的通信等都有涉及,仅作为练手学习用,细节没有太深化,没有详细检测BUG,有兴趣的朋友可以继续完善代码和界面。
代码中的t1.png等图片名称在运行时自行准备和更改替换代码,为方便小白运行,自定义的几个类都放在一个文件里了便于复制和运行,可以按类名称拆分到几个模块中方便管理
运行界面如下:
# -*- coding: utf-8 -*-
import sys,random,time,math,copy
import PyQt5
from PyQt5 import *
from PyQt5 import QtCore #不加此行要报错,上行不是全导入了嘛,没懂
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
#from turtle import * #本示例没有使用turtle库无需导入
#定义主窗口类(继承自QMainWindow)
class MypaintWindow(QMainWindow):
def __init__(self):
super().__init__()
#self.setGeometry(300, 300, 300, 200)
self.resize(1600, 905)
self.setMaximumSize(1428,905)
self.setWindowTitle('PYQT简易画板')
self.initUi()
def initUi(self):
# 创建状态栏
self.statusbar = self.statusBar()
self.statusbar.showMessage('准备')
#创建标签画板(本类要重载继承QLabel),在窗体右侧,作为画板区域)
self.label_Draw = MyLabel(self)
# 设置label的尺寸
self.label_Draw.setMaximumSize(600,800)
self.label_Draw.setGeometry(5, 76, 600, 800)
#创建标签画板DEMO(本类要重载继承QLabel,在窗体右侧,演示绘画动画)
self.label_Demo = MyLabelDemo(self)
self.label_Demo.setMaximumSize(800,800)
self.label_Demo.setGeometry(620, 76, 800, 800)
# 把pix_img传递给label
#self.label_Draw.setPixmap(self.pix)
# 设置pix_img填充满Label
# self.label_Draw.label_DrawsetScaledContents(True)
self.label_Draw.setScaledContents(True)
self.label_Demo.setScaledContents(True)
#self.label_Draw.setPixmap(QPixmap("1.jpg")) #此语句无效,标签同画板绑定后,不会加载外部图像了
#初始化工具栏
self.initToolBar()
#定义一菜单对象
menubar = self.menuBar()
fileMenu = menubar.addMenu('文件')
menu_New = QAction('保存画板', self)
menu_New.setStatusTip('在状态栏上显示内容:将当前画板中的图像保存到文件')
fileMenu.addAction(menu_New)
menu_Load = QMenu('导入', self)
subMenu_LoadImg= QAction('导入图像', self)
subMenu_LoadImg.setStatusTip('在状态栏上显示内容:导入一外部图形文件到画板')
menu_Load.addAction(subMenu_LoadImg)
fileMenu.addMenu(menu_Load)
mnu_Exit = QAction(QIcon("t08.png"),'退出程序',self)
mnu_Exit.setShortcut("ctrl+Q")
mnu_Exit.setStatusTip('在状态栏上显示内容:退出程序')
mnu_Exit.triggered.connect(qApp.quit)
fileMenu.addAction(mnu_Exit)
sta = self.statusBar()
# 本例创建了一个行为菜单。这个行为/动作能切换状态栏显示或者隐藏。
menu_View = menubar.addMenu('选项')
viewStatAct = QAction('显示/隐藏状态栏', self, checkable=True)
viewStatAct.setStatusTip('在状态栏上显示内容:视图菜单->显示/隐藏状态栏') # 用checkable选项创建一个能选中的菜单。
viewStatAct.setChecked(True) # 默认设置为选中状态
menu_View.addAction(viewStatAct)
##############################################################
self.threadMaxCount=2 #本程序最大可开多线程的数量
self.demoClickNum=0 #DEMO标签控件被单击的次数,超出threadMaxCount归0循环(0值即线程全停)
#定义槽函数,标签DEMO被单击的次数决定启动线程的数量
#定义线程数组
self.thread={}
self.threadOpen=[] #0索引起用,0对应首个线程
for i in range(self.threadMaxCount):
self.threadOpen.append(False) #定义n个线程打开的状况,供计时器函数中取线程值时使用
self.thread[i] = ThreadClass(parent=None,index=i)
self.threadOpen[i]=False #设置线程暂不打开,单击标签画板后打开
#self.thread[i].start()
self.setThreadObj(i,self.label_Demo) #向线程中传入标签控件实例对象
self.thread[i].signal_ID.connect(self.my_function) #将线程1中的自定义信号signal_ID绑定槽函数self.my_function
self.label_Demo.signal_clicked.connect(self.labDemoClick) #将自定义的DEMO标签类信号
#初始化工具栏
def initToolBar(self):
self.toolBar_main = QToolBar("绘图工具栏")
self.addToolBar(self.toolBar_main )
#self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.toolBar_main)
self.toolBar_main.setObjectName(u"toolBar_main")
self.toolBar_main.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
self.toolBar_main.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextUnderIcon)
self.setIconSize(QSize(24, 24))
self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextUnderIcon)
self.act_DrawLine = QAction(QIcon("./res/t01.png"), "画线", self)
self.act_DrawLine.setObjectName(u"act_DrawLine")
self.act_DrawLine.triggered.connect(self.draw_Line)
self.toolBar_main.addAction(self.act_DrawLine)
self.act_DrawRect = QAction(QIcon("./res/t02.png"), "画矩形", self)
self.act_DrawRect.setObjectName(u"act_DrawRect")
self.act_DrawRect.triggered.connect(self.draw_Rect)
self.toolBar_main.addAction(self.act_DrawRect)
self.act_DrawFillRect = QAction(QIcon("./res/t03.png"), "画填充矩形", self)
self.act_DrawFillRect.setObjectName(u"act_DrawFillRect")
self.act_DrawFillRect.triggered.connect(self.draw_FillRect)
self.toolBar_main.addAction(self.act_DrawFillRect)
self.act_DrawCircle = QAction(QIcon("./res/t04.png"), "画圆", self)
self.act_DrawCircle.setObjectName(u"act_DrawCircle")
self.act_DrawCircle.triggered.connect(self.draw_Circle)
self.toolBar_main.addAction(self.act_DrawCircle)
self.act_DrawEllptic = QAction(QIcon("./res/t05.png"), "画椭圆", self)
self.act_DrawEllptic.setObjectName(u"act_DrawEllptic")
self.act_DrawEllptic.triggered.connect(self.draw_Ellptic)
self.toolBar_main.addAction(self.act_DrawEllptic)
self.act_DrawFree = QAction(QIcon("./res/t06.png"), "涂手画", self)
self.act_DrawFree.setObjectName(u"act_DrawFree")
self.act_DrawFree.triggered.connect(self.Draw_Free)
self.toolBar_main.addAction(self.act_DrawFree)
self.act_DrawText = QAction(QIcon("./res/t07.png"), "画文本", self)
self.act_DrawText.setObjectName(u"act_DrawText")
self.act_DrawText.triggered.connect(self.draw_Text)
self.toolBar_main.addAction(self.act_DrawText)
self.act_DrawQuit = QAction(QIcon("./res/t08.png"), "退出绘画", self)
self.act_DrawQuit.setObjectName(u"self.act_DrawExit")
self.act_DrawQuit.triggered.connect(self.draw_Quit)
self.toolBar_main.addAction(self.act_DrawQuit)
#DEMO绘图标签被点击时
def labDemoClick(self):
"""if(self.demoClickNum>=self.threadMaxCount):
self.demoClickNum=0
for n in range(self.threadMaxCount):
self.threadOpen[n]=False
self.thread[n].stop() #关闭全部线程,原线程发送的信号,对应槽函数未必已经处理完毕,可能还在接收信号
time.sleep(1)
for i in range(self.threadMaxCount):
if(i <= (self.demoClickNum)):
if(self.threadOpen[i]==False): #对应线程还没打开,打开此序号线程
self.threadOpen[i]=True
print(f'单击标签画板:打开线程{i}')
self.thread[i].start()
self.demoClickNum = self.demoClickNum + 1 #DEMO标签控件单击次数+1,好同线程打开数量对应"""
#上面的行功能没法实现,故又回到只运行一个线程来看绘图演示,多个线程不能实现同时绘图?
s