pyside6学习专栏(十一):在PySide6中实现一简易的画板程序

pyside6学习专栏(十一):在PySide6中实现一简易的画板程序,实现画直线、矩形、填充矩形、圆、椭圆、随手画、文本。为减少代码量,所画形状的颜色、线宽、线型、填充形状、字体、字号等采用随机方式,只作为学习在Python中绘画的基本操作。

主界面上设置两个绘画区,左侧画板(实际就是PySide6的标签控件)支持鼠标操作当画板用来绘制各种形状图形,右侧画板(实际也是PySide6的标签控件)采用多线程自动绘制彩色同心圆和彩色文本。这是从我初学PYTHON时练习QT5时的一个示例代码转换为PySide6的代码。

运行界面如下:

简易画板的完整代码如下:

# -*- coding: utf-8 -*-
import sys,os,time,random,copy,math
from math import *
from PySide6 import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
#from PySide6.QtGui import *      #如果运行时没有任何界面调出,也不报错,请屏蔽此行,原因不详
import PySide6.QtCharts
from PySide6.QtCore import Signal, QEvent,Property, QSize
from PySide6.QtGui import (QCursor,QIcon,QImage,QDesktopServices, QGuiApplication,QKeySequence, QShortcut, QStandardItem,QStandardItemModel)
from PySide6.QtGui import (QPen,QBrush,QColor,QFont, QPainter,QGradient,QMatrix4x4,QPixmap,QAction,QPlatformSurfaceEvent, QSurface, QWindow,QSurfaceFormat)
from PySide6.QtWidgets import (QDialog,QWidget, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox,QCommandLinkButton, QDateTimeEdit, QDial,QDialog, QDialogButtonBox, QFileSystemModel,QGridLayout, QGroupBox, QHBoxLayout, QLabel,QLineEdit, QListView, QMenu, QPlainTextEdit,QProgressBar, QPushButton, QRadioButton,QScrollBar, QSizePolicy, QSlider, QSpinBox,QStyleFactory, QTableWidget, QTabWidget,QTextBrowser, QTextEdit, QToolBox, QToolButton,QTreeView, QVBoxLayout)#定义主窗口类(继承自QMainWindow)
class MypaintWindow(QMainWindow):  def __init__(self):super().__init__()self.resize(1600, 905)self.setMaximumSize(1428,905)self.setWindowTitle('PySide6简易画板')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_functionself.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):self.thread[0].start()    #线程0中画同心圆DEMO示例self.threadOpen[0]=True#将标签控件画板对象传入线程中def setThreadObj(self,index,obj):self.thread[index].setObj(obj)#绑定线程类中的signal_ID信号对应的槽函数,得到各线程中的变量ID的值(仅示例线程同窗体数据交互,同本示例无关)def my_function(self,counter):ID = counterindex = self.sender().index   #在槽函数中被调用,用于获取发出信号的对象,的索引号print(f'主窗体接收线程槽函数:{index} 返回整数值{ID}')if index == 0:pass       elif index == 1:passelif index == 2:pass#......#在菜单事件中:定义一右键菜单def contextMenuEvent(self, event):cmenu = QMenu(self)act_New = cmenu.addAction("新建")   #仅示例,新建时不对已画图形作保存等处理,直接清空画板,余同print(act_New)act_Open = cmenu.addAction("打开")print(act_Open)act_Quit = cmenu.addAction("退出")print(act_Quit)#关联菜单信号对象action = cmenu.exec_(self.mapToGlobal(event.pos()))#处理对应菜单的响应函数if action == act_Quit:qApp.quit()            #仅示例,退出时不对是否保存图像作细化处理elif action == act_Open:print('打开')           #仅示例,没有具体功能代码,自行完善elif action == act_New:print('新建')self.label_Draw.pix.fill(Qt.white)self.label_Draw.repaint()   #清空标签画板上的图形#对应主菜单:视图-->显示/隐藏状态栏的信号槽函数def toggleMenu(self, state):if state:self.statusbar.show()else:self.statusbar.hide()####################################################################################槽函数:画直线def draw_Line(self):print('画直线')self.label_Draw.draw_Type=1cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#槽函数:画矩形def draw_Rect(self):print('画矩形') self.label_Draw.draw_Type=2cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#槽函数:画矩形def draw_FillRect(self):print('画填充矩形')   self.label_Draw.draw_Type=3cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#槽函数:画园def draw_Circle(self):print('画圆')    self.label_Draw.draw_Type=4cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#槽函数:画椭圆def draw_Ellptic(self):print('画椭圆') self.label_Draw.draw_Type=5cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#槽函数:随手画def Draw_Free(self): print('随手画') self.label_Draw.draw_Type=6cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#槽函数:画文本def draw_Text(self): print('画文本') self.label_Draw.draw_Type=7cursor = QCursor(Qt.CrossCursor)self.setCursor(cursor)#打开文本录入子窗口self.subTxtWindow = MyDrawTextWindow()                  #定义的子窗体必须有self,否则会一闪而过self.subTxtWindow.sig_ToMian.connect(self.getDrawTXT)   #同时定义对应子窗体发送的自定义信号'sig_ToMian',以便接收子窗体传来的数据self.subTxtWindow.exec_()                               #会模态化显示对话框,直至关闭它self.update()                             #槽函数:退出绘图模式    def draw_Quit(self): print('退出绘图模式') self.label_Draw.draw_Type=0cursor = QCursor(Qt.ArrowCursor)self.setCursor(cursor)bDrawOK=True           #自定义槽函数,用于接收画文本内容的子窗口发送信号sig_ToMian和接收传来的数据def getDrawTXT(self,dic_data):print(f'主窗体接收到sig_ToMian信号和字典变量值:{dic_data}')#将数值传给子窗体self.label,以便进行绘画文本self.label_Draw.bDrawOK=Trueself.label_Draw.setDrawTXT(dic_data)
####################################################################################################################
#重载标签类1,把标签区域作为画板区域(主窗体左绘画区域)        
class MyLabel(QLabel):#鼠标起点,鼠标终点lastPoint = QPoint()    #在类函数体外可以不加self前缀,但在函数体名类对象引用时,必须要加self的前缀endPoint = QPoint()bDrawOK=True           #处理因重载绘图事件过快,可能会多画一此不可预料的杂图,初始化应True,让画布画白底一次#初始化def __init__(self, text=''):super(MyLabel, self).__init__(text)        self.setFont(QFont('宋体', 16))  # 设置字体和大小self.bLeftMouseKey=False   #定义只鼠标左键才能进行绘画#定义绘图的设置(字典数据方式)self.dic_Set={'线型':'SolidLine',               #SolidLine=实线   DashLine=虚线  DotLine=点线   DashDotLine=点划线  DashDotDotLine=双点划线  CustomDashLine=自定义线'线宽':2,'线颜色':'black',                   #black=黑色   red=红色  blue=蓝色.......'画刷类型':'SolidPattern',        #SolidPattern=纯色填充  Dense1Pattern=密度样式1   Dense2Pattern=密度样式2... HorPattern=水平线样式 VerPattern=垂直线样式  CrossPattern=交通叉线样式   DiagCrossPattern=倾斜交叉线样式      BDiagPattern=反斜线样式     FDiagPattern倾斜样式 '填充色':'blue','文本':'示例文本内容','字体名称':'宋体','字号':16,'粗体':False,'斜体':False,'下划线':False,'删除线':False,'字体颜色':'red','其他设置自行扩展':''}#定义文本绘图的设置(字典数据方式)self.dic_txt={'文本':'示例文本内容','字体名称':'宋体','字号':16,'粗体':False,'斜体':False,'下划线':False,'删除线':False,'字体颜色':'red','其他设置自行扩展':''}self.edtCtlPosX=0   #保存画文本图形时,调出对话框或编辑框控件的左上角位置 self.edtCtrlPosY=0self.lst_LineType=['实线','虚线','点线','点划线','双点划线','自定义线']self.lst_Col=["black", "red", "green", "blue", "purple", "orange", "MediumSlateBlue", "CornflowerBlue","DodgerBlue", "DeepskyBlue", "LightSkyBlue", "SkyBlue", "LightBlue","请自行增加..."]self.lst_FillType=['纯色','密度样式1','密度样式2','密度样式3','密度样式4','密度样式5','密度样式6','密度样式7','水平线样式','垂直线样式','交叉线样式','倾斜交叉线样式','反斜线样式','倾斜样式']cursor = QCursor(Qt.CrossCursor)  #光标类型#定义当前绘画类型self.draw_Type=0      #PY中没有枚举,这里采用数字方式比对 0=非绘画模式   1=画线模式 2=画矩形模式 3=画填充矩形模式 4=画圆模式 5=画椭圆模式 6=随手画模式 7=画文本模式#在窗体上设置一区域为画布,画布大小为600*600,背景为白色self.pix = QPixmap(600, 800)    #实例化QPixmap类self.pix.fill(Qt.white)# 把pix_img传递给labelself.setPixmap(self.pix)self.noPatter =  QPainter(self.pix).brush()#得到画线类型def getLineType(self,typeTxt='实线'):   linetype=Qt.SolidLineif(typeTxt==self.lst_LineType[0]):linetype=Qt.SolidLineelif(typeTxt==self.lst_LineType[1]):linetype=Qt.DashLineelif(typeTxt==self.lst_LineType[2]):linetype=Qt.DotLineelif(typeTxt==self.lst_LineType[3]):linetype=Qt.DashDotLineelif(typeTxt==self.lst_LineType[4]):linetype=Qt.DashDotLineelif(typeTxt==self.lst_LineType[5]):linetype=Qt.DashDotDotLineelse: linetype=Qt.SolidLinereturn linetype#得到填充类型def getFillType(self,typeTxt='纯色'):   filltype=Qt.SolidPatternif(typeTxt==self.lst_FillType[0]):filltype=Qt.SolidPatternelif(typeTxt==self.lst_FillType[1]):filltype=Qt.Dense1Patternelif(typeTxt==self.lst_FillType[2]):filltype=Qt.Dense2Patternelif(typeTxt==self.lst_FillType[3]):filltype=Qt.Dense3Patternelif(typeTxt==self.lst_FillType[4]):filltype=Qt.Dense4Patternelif(typeTxt==self.lst_FillType[5]):filltype=Qt.Dense5Patternelif(typeTxt==self.lst_FillType[6]):filltype=Qt.Dense6Patternelif(typeTxt==self.lst_FillType[7]):filltype=Qt.Dense7Patternelif(typeTxt==self.lst_FillType[8]):filltype=Qt.HorPatternelif(typeTxt==self.lst_FillType[9]):filltype=Qt.VerPatternelif(typeTxt==self.lst_FillType[10]):filltype=Qt.CrossPatternelif(typeTxt==self.lst_FillType[11]):filltype=Qt.DiagCrossPatternelif(typeTxt==self.lst_FillType[12]):filltype=Qt.BDiagPatternelif(typeTxt==self.lst_FillType[13]):filltype=Qt.FDiagPattern else: filltype=Qt.SolidPatterreturn filltype#设置要绘画的文本内容def setDrawTXT(self,dic_data):print(f'设置到标签画板类的相绘制的文本数据为:{dic_data}')self.dic_txt = dic_datafor key in dic_data.keys():    #将从子窗体得到的数据更新到设置字典中print(dic_data[key])self.SetValue(key,dic_data[key])#得到设置def getSet(self,setName):if(setName=='线型'): defValue='SolidLine'elif(setName=='线宽'): defValue=2elif(setName=='线颜色'): defValue='black'elif(setName=='画刷类型'): defValue='SolidPattern'elif(setName=='填充色'): defValue='blue'elif(setName=='文本'): defValue='示例文本内容'elif(setName=='字体名称'): defValue='宋体'elif(setName=='字号'): defValue=16elif(setName=='粗体'): defValue=Falseelif(setName=='斜体'): defValue=Falseelif(setName=='下划线'): defValue=Falseelif(setName=='删除线'): defValue=Falseelif(setName=='字体颜色'): defValue='red'else: print('无此设置项')value = str(self.dic_Set.get(setName, defValue))return value#运行中设置配置值,此函数没有对异常的处理,可能运行中有BUGdef SetValue(self,setName,value):self.dic_Set[setName]=valuedef setBackgroundColor(self, color):pal = self.palette()pal.setColor(self.backgroundRole(), QColor(color))self.setPalette(pal)#重载绘图函数:根据选择设置,画不同的图形  #0=非绘画模式   1=画线模式 2=画矩形模式 3=画填充矩形模式 4=画圆模式 5=画椭圆模式 6=随手画模式 7=画文本模式def paintEvent(self, event):pen = QPen()                     # 创建画笔对象brush = QBrush()                # 创建画刷对象pp = QPainter(self.pix)if(self.draw_Type==6):  #随手画模式:固定画笔,不用随机模式用设置值pencol = QColor(self.getSet('线颜色'))pen.setWidth(2)   pp.setPen(pen)                  # 设置画笔                 else:    lineType = self.getLineType(self.lst_LineType[random.randrange(0,5)])      #画线类型,随机选一种fillType = self.getFillType(self.lst_FillType[random.randrange(0,13)])     #填充类型:随机选一种#pencol = QColor(self.getSet('线颜色')) #不用设置色,用随机色pencol = QColor(self.lst_Col[random.randrange(0,12)])                      #画笔颜色:随机选一种pen.setColor(pencol)                 # 设置画笔颜色为红色pen.setStyle(lineType)               # 设置画笔类型pen.setWidth(random.randrange(1,8)) # 设置画笔宽度#pp.setBrush(QColor(self.getSet('填充色')))  #不用设置的初始值brush.setColor(QColor(self.lst_Col[random.randrange(0,12)]))              #画刷颜色为随机选一种brush.setStyle(fillType)   pp.setPen(pen)                  #设置画笔pp.setBrush(brush)              #设置画刷x1 = self.lastPoint.x()y1 = self.lastPoint.y()x2 = self.endPoint.x()y2 = self.endPoint.y()point1=QPoint(x1,y1)point2=QPoint(x2,y2)if(self.bDrawOK==False):  #此绘画开关没打开前,避免误画图形returnif(self.draw_Type==1):  #画直线模式pp.drawLine(point1, point2)elif(self.draw_Type==2):  #画矩形模式pp.setBrush(self.noPatter)   #画非填充矩形时:暂没找到透明画刷的得到方法,用的保存了一初始画刷没有加载颜色等时的值if(x1<=x2):  #从上往下拖pp.drawRect(x1,y1,abs(x2-x1),abs(y2-y1))  else:  #从下往上拖pp.drawRect(x2,y2,abs(x1-x2),abs(y1-y2)) elif(self.draw_Type==3):  #画填充矩形模式if(x1<=x2):  #从上往下拖pp.drawRect(x1,y1,abs(x2-x1),abs(y2-y1)) else:  #从下往上拖pp.drawRect(x2,y2,abs(x1-x2),abs(y1-y2)) elif(self.draw_Type==4):  #画圆模式pp.setBrush(self.noPatter)   #画非填充圆时#计算出圆半径,并更改结束点坐标已满足最大圆D=min(abs(x2-x1),abs(y2-y1))if(x1<=x2):x2 = x1+Dy2 = y1+Delse:x2 = x1 - Dy2 = y1 - Dif(x1<=x2):  #从上往下拖pp.drawEllipse(x1,y1,abs(x2-x1),abs(y2-y1))  else:  #从下往上拖pp.drawEllipse(x2,y2,abs(x1-x2),abs(y1-y2)) elif(self.draw_Type==5):  #椭圆模式pp.setBrush(self.noPatter)   #画非填充圆时if(x1<=x2):  #从上往下拖pp.drawEllipse(x1,y1,abs(x2-x1),abs(y2-y1))  else:  #从下往上拖pp.drawEllipse(x2,y2,abs(x1-x2),abs(y1-y2))       elif(self.draw_Type==6):  #随手画模式# 根据鼠标指针前后两个位置绘制直线self.bDrawOK=Truepp.drawLine(self.lastPoint, self.endPoint)# 让前一个坐标值等于后一个坐标值,这样就能实现画出连续的线self.lastPoint = self.endPointelif(self.draw_Type==7):  #画文本模式#打开一个自定义子窗体,初始化录入文本,也可采用类似CAD方式,在鼠标按下位置创建一编辑框控件实时进行编辑print('开始画文本')pp.setPen(QColor(self.lst_Col[random.randrange(0,12)]))   # 设置笔的颜色: 随机选一种# 设置字体pp.setFont( QFont(self.getSet('字体名称'), int(self.getSet('字号'))))   #按设置值if(x1<=x2):  #从上往下拖从X1,Y1开始画文本#画出文本pp.drawText(QRect(x1,y1,abs(x2-x1),abs(y2-y1)), Qt.AlignLeft, self.getSet('文本'))else:  #从下往上拖,从X2,Y2开始画文本pp.drawText(QRect(x2,y2,abs(x2-x1),abs(y2-y1)), Qt.AlignLeft, self.getSet('文本'))painter = QPainter(self)#绘制画布到窗口指定位置处painter.drawPixmap(0, 0, self.pix)if(self.draw_Type!=6 or self.draw_Type!=7):self.bDrawOK=False#鼠标按下事件重载   def mousePressEvent(self, event):if(self.draw_Type!=6  or self.draw_Type!=7):self.bDrawOK=False   #关闭重绘事件print(f'当前坐标:x={event.pos().x()},y={event.pos().y()}')# 鼠标左键按下if event.button() == Qt.LeftButton:self.bLeftMouseKey=Trueself.lastPoint = event.pos()self.endPoint = self.lastPointself.edtCtlPosX = event.pos().x()self.edtCtlPosY = event.pos().y()+30  #没对超出屏幕时作坐标处理。。。else:self.bLeftMouseKey=False#鼠标移动事件重载          def mouseMoveEvent(self, event):  print(f'当前坐标:x={event.pos().x()},y={event.pos().y()}')if event.buttons() and self.bLeftMouseKey:if(self.draw_Type==6):  #仅随手画时要实时得到坐标位置并画出self.endPoint = event.pos()# 进行重新绘制self.bDrawOK=True   #打开重绘事件self.update()else:pass#print('非随手画模式请自行增加代码来画一临时虚框显示绘图过程,但不真正绘会出来')# 鼠标左键释放        def mouseReleaseEvent(self, event):print(f'当前坐标:x={event.pos().x()},y={event.pos().y()}')if event.button() == Qt.LeftButton:self.endPoint = event.pos()print(f'当前坐标:x={event.pos().x()},y={event.pos().y()}')# 进行重新绘制self.bDrawOK=Trueself.update()#画文本时,返回新建窗体或编辑框控件左上角坐标def getTxtEdtPos(self):return self.edtCtlPosX,self.edtCtrlPosY####################################################################################################
#定义一个录入画文本的子窗口
class MyDrawTextWindow(QDialog):sig_ToMian = Signal(object)   #窗体间数据通讯用自定义信号def __init__(self):#定义绘图的设置(字典数据方式)self.dic_txt={'文本':'示例文本内容','字体名称':'宋体','字号':16,'粗体':False,'斜体':False,'下划线':False,'删除线':False,'字体颜色':'red','其他设置自行扩展':''}super().__init__()self.setWindowTitle('设置要在画板中绘制文本的属性')self.resize(500,200)layout = QVBoxLayout()layout = QFormLayout()self.edt_drawTxt = QLineEdit()self.edt_drawTxt.setText('示例文本内容')self.edt_drawTxt.resize(200,50)self.edt_fontname = QLineEdit()self.edt_fontname.setText('宋体')self.edt_fontname.resize(200,50)self.edt_fontsize = QLineEdit()self.edt_fontsize.setText('16')self.edt_fontsize.resize(200,50)self.btn_OK = QPushButton()self.btn_OK.setText("确定")layout.addRow("请输入所绘文本的内容->",self.edt_drawTxt)layout.addRow("请输入所绘文本的字体名称->",self.edt_fontname)layout.addRow("请输入所绘文本的字号(整数)->", self.edt_fontsize)layout.addRow("仅为示例其他属性设置没有设置对应控件,请自行完善增加->", None)layout.addRow("", self.btn_OK)self.btn_OK.clicked.connect(self.btnok)#仅为示例,其他参数,不再增加控件了,看示例的可自行增加相应控件和对应的变量关联向主窗口中传递即可。。。。。。。。。。。。。。self.setLayout(layout)#得到字典中的值def getValue(self,setName):if(setName=='文本'): defValue='示例文本内容'elif(setName=='字体名称'): defValue='宋体'elif(setName=='字号'): defValue=16elif(setName=='粗体'): defValue=Falseelif(setName=='斜体'): defValue=Falseelif(setName=='下划线'): defValue=Falseelif(setName=='删除线'): defValue=Falseelif(setName=='字体颜色'): defValue='red'else: print('无此字典数据项')value = str(self.dic_txt.get(setName, defValue))return value    #运行中设置配置值,此函数没有对异常的处理,可能运行中有BUGdef SetValue(self,setName,value):self.dic_txt[setName]=value#窗体事件重载def event(self, event):if event.type() == QEvent.Type.Close:print("窗体关闭事件被触发:自动发信号给主窗体接收当前窗体中的变量值")self.SetValue('文本',self.edt_drawTxt.text())self.SetValue('字体名称',self.edt_fontname.text())self.SetValue('字号',self.edt_fontsize.text())self.sig_ToMian.emit(self.dic_txt)   #发出信号及数据,等待主窗口的槽函数接收数据event.accept()        else:passreturn super().event(event)#单击OK按纽时,关闭窗体,自然触发窗体关闭事件,在关闭事件中发送数据给主窗口接收def btnok(self):self.close()
#################################################################################################################
##重载标签类2,把标签区域作为画板区域(主窗体右绘画区域),此区域作为画板DEMO示例多线程绘画展示区域        
class MyLabelDemo(QLabel):#鼠标起点,鼠标终点lastPoint = QPoint(0,0)    #在类函数体外可以不加self前缀,但在函数体名类对象引用时,必须要加self的前缀endPoint = QPoint(0,0)bDrawOK=True           #处理因重载绘图事件过快,可能会多画一此不可预料的杂图signal_clicked = Signal(str)    #自定标签类的信号,即标签被单击时发出(传回参数为标签文本)#线条或文本的颜色:实际可在界面用颜色选择框来选择lst_col = ["black", "red", "green", "blue", "purple", "orange", "MediumSlateBlue", "CornflowerBlue","DodgerBlue", "DeepskyBlue", "LightSkyBlue", "SkyBlue", "LightBlue"]#字体名称:实际可在界面中用字体选择框来选择lst_fontname=['宋体','仿宋','黑体','楷体','方正行楷简体','微软雅黑','其他字体...']    #初始化def __init__(self, text=''):super(MyLabelDemo, self).__init__(text)        cursor = QCursor(Qt.CrossCursor)  #光标类型self.MyDrawText="本示例为在右侧画板(标签控件)画直线、圆、椭圆、矩形、填充矩形等图形的\
PYTHON+PySide6示例,绘制的线型、线宽、颜色、文字类型、字号等均采用随机方式得到,有兴趣的可以用指定\
的方式来完善此简易画板程序,窗口右侧的另一标签控件画板采用多线程上画出复杂同心圆及多彩文字。本代码\
仅为初学PYTHON时学习最基本的PYTHON在窗口上的绘图操作。"self.lst_drawText=[[]]   #将 MyDrawText文本分解此列表中,每个列表对象中的数据仍是列表,格式为['多','宋体',16,'red',True......][.......]  示例只使用了部份属性功能self.splitDrawText(self.MyDrawText)  #调用函数分解字符#定义当前绘画类型self.pix = QPixmap(800,800)    #实例化QPixmap类self.pix.fill(Qt.white)self.setPixmap(self.pix)     # 把pix_img传递给labelpp0 = QPainter(self.pix)pp0.drawText(QRect(0,0,500,35),0,'多线程画同心圆和彩色文本的示例,请单击标签画板绘图区域打开多线程查看演示。') self.noPatter =  QPainter(self.pix).brush()        #重载绘图函数:根据选择设置,画不同的图形  #0=非绘画模式   1=画线模式 2=画矩形模式 3=画填充矩形模式 4=画圆模式 5=画椭圆模式 6=随手画模式 7=画文本模式def paintEvent(self, event):if(self.bDrawOK==False):  #处理因此函数调用过于频凡造成多画的现象returnx1 = self.lastPoint.x()y1 = self.lastPoint.y()x2 = self.endPoint.x()y2 = self.endPoint.y()point1=QPoint(x1,y1)point2=QPoint(x2,y2)#以下语句运行正常,修改成全局变量bPaint=True  #窗体要执行以下代码,线程中暂时规避对painter操作painter0 = QPainter(self)   #此QPainter只能有paintEvent中定义,不能定义成类的self成员对象,也不能在其地方(如其他窗口,线程中)定义,否则没有绘画功能显示#绘制画布到窗口指定位置处#painter0.begin(self)pp0 = QPainter(self.pix)painter0.drawPixmap(0, 0, self.pix)#painter0.end()bPaint=False#鼠标按下事件重载   def mousePressEvent(self, event):bDrawOK=Trueprint(f'当前鼠标压下坐标:x={event.pos().x()},y={event.pos().y()}')# 鼠标左键按下if event.button() == Qt.LeftButton:self.lastPoint = event.pos()self.endPoint = self.lastPointself.signal_clicked.emit(self.text())   #在DEMO标签上按下鼠标键后,发送此信号出去,主窗体接收此信号,调用对应定义的槽函数响应print(' MyLabelDemo标签类的鼠标被按下,发出信号signal_clicked')# 鼠标左键释放        def mouseReleaseEvent(self, event):print(f'当前鼠标释放坐标:x={event.pos().x()},y={event.pos().y()}')if event.button() == Qt.LeftButton:self.endPoint = event.pos()# 进行重新绘制self.update()bDrawOK=True#分解要画出的文本到列表中def splitDrawText(self,drawStr):count = len(drawStr)x=5   #文字绘制起始点坐标        y=30row=0  #文字当前行数rowMaxY=10 #本行文字中最大的字高sumposX=x  #绘制文本累计已占用当前行位置像素 sumposY=yspaceX=3  #文字间横向间隔spaceY=6 #文字竖向间隔dpi=96#        内容     字号  颜色 加粗 倾斜 下划线 删除线,文字矩形左上角x,y,文字矩形宽度,高度,对齐方式.....#         0   1    2   3     4   5    6     7  8 9 10 11  12...            lst_one=['','宋体',16,'red',True,True,True,True,0,0,0,0,'AlignLeft']if count>0:self.lst_drawText.clear()lst_str = list(drawStr)  #按单字分解字符串到列表对象for i in range(count):lst_one[0]=lst_str[i]lst_one[1]=self.lst_fontname[random.randint(0, 5)]  #随机得到字体名称#仅示例:对字体的x,y,w,h进行处理lst_one[2]=random.randint(15, 24)  #随机得到字号lst_one[8]=sumposXlst_one[9]=sumposYlst_one[10]=self.points_to_pixels(lst_one[2],dpi)+spaceX    #没有详细分析字号同屏幕像素及DPI的关系。。。lst_one[11]=lst_one[10]if(rowMaxY<lst_one[11]):rowMaxY = lst_one[11]lst_one[12]='AlignLeft'sumposX=sumposX+lst_one[2]+spaceXif(sumposX>770): #本画布宽度是800,故设成790换行,row=row+1sumposX=0x=5y=row*40+spaceYsumposY=sumposY+rowMaxY+spaceYif(i==2 or(i>=8 and i<=11)):  #这几个是数字要处理passelif(i>=4 and i<=7):          #这几个是bool值要处理pass else:passself.lst_drawText.append(copy.deepcopy(lst_one))  #必须用深度copy防止列表中的内容都是最后一个字的内容现象print(self.lst_drawText)#字号同像素点的转换def points_to_pixels(self,points, dpi):return points * dpi / 72.0def pixels_to_points(self,pixels, dpi):return pixels * 72.0 / dpi#########################################################################################################################
#自定义线程类(继承QT的多线程类QtCore.QThread,不是PYTHON的线程类)
class ThreadClass(QtCore.QThread):signal_ID = Signal(int) #自定义线程中的信号,名称为signal_IDID=0x0=400y0=400bRealseAll=Falsedef __init__(self,parent=None,index=0):super(ThreadClass,self).__init__(parent)self.index = indexself.is_running = Trueself.ID=0#重载开始线程对应的run函数:本例根据鼠标点击画板(标签控件)的次数来决定运行几个多线程DEMOdef run(self):self.obj.bDrawOK=Trueprint(f'开始线程...:线程索引号:{self.index}')self.is_running = Truewhile(True):   #线程重复不断的循环来if self.ID>99: self.ID=0   #ID对本代码无用,仅为信号槽传回整数作示例self.signal_ID.emit(self.ID)   #将本线程中的ID值(0-99)通过信号signal_ID槽发送,接收端通过if(self.index==0):      #线程0画以下DEM0代码 self.dem1_drawText()         #画彩色文本self.dem0_drawCircle()   #画同心圆self.dem1_RealseText(QColor(255,255,255))  #用白色画笔擦除彩色文本(擦除不是太干净待优化)time.sleep(3)self.obj.pix.fill(Qt.white)  #全部擦除画板elif(self.index==1):            #线程1画以下DEM0代码,只能在一个线程中依次画,两个及以上线程同时画没成功??)time.sleep(1)  elif(self.index==2):            #线程2画以下DEM0代码time.sleep(1)                #自行扩展对各线程的响应代码......else:time.sleep(1)                 #1000毫秒间隔#多线程绘画DEMO1:参数bRealseAll表示调用前是否擦除全部画布内容def dem0_drawCircle(self):print('多线程0绘画DEM0:绘制同心圆') #painter = QPainter(self.obj)  #paniter对象只能在重绘事件中,在多线程中不会作用pp = QPainter(self.obj.pix)pencol = QColor(255,0,255)pp.setPen(pencol)pLeft=0pRight=0cx0=0cy0=0#绘制画布到窗口指定位置处:以下为在序号为0的线程中绘制一复杂图像代码#先擦除窗口上的图(本例擦除全部)#self.obj.pix = QPixmap(800,800) R=30  #绘圆半径   count = 60  #第一圈绘60个,以后每圈翻倍for j in range(12):     #绘制的圈数pencol=QColor(self.obj.lst_col[j])pp.setPen(pencol)for i in range(count*(j+1)):   #每圈绘制的圆个数time.sleep(0.01)   #画每个圆的时间间隔为10毫秒#计算每个圆的矩形左上角位置dx0,dy0=self.getRectPoint(R*j,i,count*(j+1),R)pp.drawEllipse(dx0,dy0,2*R,2*R)self.ID+=self.ID    #本线程绘的图形计数self.obj.update()       #刷新调用画板的重绘事件函数self.obj.update()       #刷新调用画板的重绘事件函数#多线程绘画DEM1def dem1_drawText(self):print('多线程1绘画DEM1:绘制彩色文本')    #绘制画布到窗口指定位置处:以下为在序号为0的线程中绘制一复杂图像代码#先擦除窗口上的图(本例擦除全部)#self.obj.pix = QPixmap(800,800) pp = QPainter(self.obj.pix)pencol = QColor(255,0,255)pp.setPen(pencol)pLeft=0pRight=0cx0=0cy0=0self.ID=0#self.obj.pix.fill(Qt.white)for j in range(len(self.obj.lst_drawText)):     #绘制的文字个数time.sleep(0.2)pencol=QColor(self.obj.lst_col[random.randint(0,12)])  #颜色随机并没有用字的列表中的属性,请自行完善pp.setFont(QFont(self.obj.lst_drawText[j][1], int(self.obj.lst_drawText[j][2])))pp.setPen(pencol)#pp.drawText(self.obj.lst_drawText[j][0])pp.drawText(QRect(self.obj.lst_drawText[j][8],self.obj.lst_drawText[j][9],self.obj.lst_drawText[j][10],self.obj.lst_drawText[j][11]), Qt.AlignLeft, self.obj.lst_drawText[j][0])self.obj.update()       #刷新调用画板的重绘事件函数self.ID+=self.ID    #本线程绘的图形计数self.obj.update()       #刷新调用画板的重绘事件函数#将dem1_drawText画的文本擦除(默认采用白色擦除,即擦除色同画板背景色一致),擦除的不是太干净未处理。。。def dem1_RealseText(self,pencol=QColor(255,255,255)):print('多线程1绘画DEM1:绘制彩色文本')    #绘制画布到窗口指定位置处:以下为在序号为0的线程中绘制一复杂图像代码#先擦除窗口上的图(本例擦除全部)#self.obj.pix = QPixmap(800,800) pp = QPainter(self.obj.pix)pp.setPen(pencol)pLeft=0pRight=0cx0=0cy0=0self.ID=0#self.obj.pix.fill(Qt.white)for j in range(len(self.obj.lst_drawText)):     #绘制的文字个数time.sleep(0.01) pp.setFont(QFont(self.obj.lst_drawText[j][1], int(self.obj.lst_drawText[j][2])))#pp.drawText(self.obj.lst_drawText[j][0])pp.drawText(QRect(self.obj.lst_drawText[j][8],self.obj.lst_drawText[j][9],self.obj.lst_drawText[j][10],self.obj.lst_drawText[j][11]), Qt.AlignLeft, self.obj.lst_drawText[j][0])self.ID+=self.ID    #本线程绘的图形计数self.obj.update()       #刷新调用画板的重绘事件函数self.obj.update()       #刷新调用画板的重绘事件函数#停止指定线程def stop(self):self.is_running=Falseprint('停止线程...',self.index)self.terminate()#根据圆心轨迹半径,序号,圆数量,圆半径计算矩形左上角坐标def getRectPoint(sef,dR,index,count,r):a=float(360/count)ang1=math.radians(360/count)curAng=ang1*indexrectLeft=0rectTop=0L=float(dR*math.sin(curAng))H=float(dR*math.cos(curAng))cx0=400+Lcy0=400-HrectLeft=cx0-rrectTop=cy0-rreturn rectLeft,rectTop   #返回画圆的左角坐标点#线程中自定义函数供外部调用线程中的变量值def getID(self):return self.index,self.ID#在线程中导入需要操作的对象def setObj(self,frmobj):self.obj = frmobj########################################################################################
if __name__ == '__main__':app = QApplication(sys.argv)form = MypaintWindow()form.show()sys.exit(app.exec())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/30941.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 屏幕适配 Tips

概念 屏幕尺寸&#xff1a;屏幕的对角线的长度屏幕分辨率&#xff1a;屏幕分辨率是指在横纵向上的像素点数&#xff0c;单位是px&#xff0c;1px1个像素点。一般以纵向像素x横向像素&#xff0c;如1960x1080屏幕像素密度&#xff1a;每英寸上的像素点数&#xff0c;单位是dpi …

uniapp或者vue 使用serialport

参考https://blog.csdn.net/ykee126/article/details/90440499 版本是第一位&#xff1a;否则容易编译失败 node 版本 18.14.0 npm 版本 9.3.1 electron 版本 30.0.8 electron-rebuild 版本 3.2.9 serialport 版本 10.0.0 需要python环境 main.js // Modules to control app…

从零开始的 Kafka 学习(二)| 集群启动

1. 相关概念 1.1 代理&#xff1a;Broker 使用Kafka前&#xff0c;我们都会启动Kafka服务进程&#xff0c;这里的Kafka服务进程我们一般会称之为Kafka Broker 或 Kafka Server。因为Kafka是分布式消息系统所以再实际的生产环境中&#xff0c;是需要多个服务进程形成集群提供消…

综合使用pandas、numpy、matplotlib、seaborn库做数据分析、挖掘、可视化项目

目录 1.结构化数据挖掘 1.1依赖库导入和数据读取 1.2各品牌机型及售价统计 1.3视频录制规格与价格关联性分析 2.结构化数据预处理 2.1筛选特征 2.2特征标签归一化及编码 1.结构化数据挖掘 1.1依赖库导入和数据读取 导入必要的依赖库&#xff0c;读取 csv 格式数据集转化为 Data…

蓝桥杯题型

蓝桥杯 蓝桥杯题型分类语法基础艺术与篮球&#xff08;日期问题&#xff09;时间显示&#xff08;时间问题&#xff09;跑步计划&#xff08;日期问题&#xff09;偶串(字符&#xff09;最长子序列&#xff08;字符&#xff09;字母数&#xff08;进制转换&#xff09;6个0&…

Linux常见指令

Linux常见指令 1、ls指令2、pwd命令3、cd指令4、touch指令5、mkdir指令6、rmdir指令和rm指令7、man指令8、cp指令9、mv指令10、cat指令11、重定向12、more指令13、less指令14、head指令15、tail指令16、管道17、时间相关指令18、cal指令19、find指令20、grep指令21、zip/unzip指…

C++:入门详解(关于C与C++基本差别)

目录 一.C的第一个程序 二.命名空间&#xff08;namespace&#xff09; 1.命名空间的定义与使用&#xff1a; &#xff08;1&#xff09;命名空间里可以定义变量&#xff0c;函数&#xff0c;结构体等多种类型 &#xff08;2&#xff09;命名空间调用&#xff08;&#xf…

智能机器人学习机WT3000A AI芯片方案-自然语音交互 打造沉浸式学习体验

一、概述 当AI浪潮席卷全球&#xff0c;教育领域也未能幸免。AI学习机&#xff0c;这个打着“个性化学习”、“精准提分”旗号的新兴产品&#xff0c;正以惊人的速度占领市场。从一线城市到偏远乡镇&#xff0c;从学龄前儿童到高考备考生&#xff0c;AI学习机的广告铺天盖地&am…

字符串相乘——力扣

给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2", num2 "3" …

C/C++蓝桥杯算法真题打卡(Day3)

一、P8598 [蓝桥杯 2013 省 AB] 错误票据 - 洛谷 算法代码&#xff1a; #include<bits/stdc.h> using namespace std;int main() {int N;cin >> N; // 读取数据行数unordered_map<int, int> idCount; // 用于统计每个ID出现的次数vector<int> ids; …

关于OceanBase与CDH适配的经验分享

CDH是Cloudera早期推出的一个开源平台版本&#xff0c;它实质上成为了Apache Hadoop生态系统内公认的安装与管理平台&#xff0c;专为企业级需求量身打造。CDH为用户提供了即装即用的企业级解决方案。通过整合Hadoop与另外十多项关键开源项目&#xff0c;Cloudera构建了一个功能…

【CSS3】筑基篇

目录 复合选择器后代选择器子选择器并集选择器交集选择器伪类选择器 CSS 三大特性继承性层叠性优先级 背景属性背景色背景图背景图平铺方式背景图位置背景图缩放背景图固定背景复合属性 显示模式显示模式块级元素行内元素行内块元素 转换显示模式 结构伪类选择器结构伪类选择器…

假设检验与置信区间在机器学习中的应用

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 &#x1f4da; 引言 在机器学…

鸿蒙Next-应用检测、安装以及企业内部商店的实现

一、企业内部应用检测和更新升级 A应用检测是否安装B应用 canOpenApp():boolean{ try { let link schB://com.example.test/open; // 替换成你目标应用的link串儿 let canOpen bundleManager.canOpenLink(link); console.log("canOpen:"canOpen…

Locker 是 Godot 的一个开源插件,它提供了一种快速且可扩展的方式来使用不同的策略保存和加载数据,并且具有开箱即用的 JSON 和加密功能。

一、软件介绍 文末提供下载 Locker 插件是在 Godot 4.3 中创建的框架&#xff0c;旨在简化在 Godot 项目中保存、加载和管理数据的过程。该插件的主要目标之一是对用户自定义开放&#xff0c;允许使用不同的用户定义策略来访问数据。并且具有开箱即用的 JSON 和加密功能。 二、…

(更新完)LPZero: Language Model Zero-cost Proxy Search from Zero

LPZero代码 摘要 神经架构搜索 (NAS) 有助于自动执行有效的神经网络搜索&#xff0c;同时需要大量的计算资源&#xff0c;尤其是对于语言模型。零样本 NAS 利用零成本 (ZC) 代理来估计模型性能&#xff0c;从而显着降低计算需求。然而&#xff0c;现有的 ZC 代理严重依赖于深…

Varlens(手机上的单反)Ver.1.9.3 高级版.apk

Varlens 是一款专业级手机摄影软件&#xff0c;旨在通过丰富的功能和高自由度参数调节&#xff0c;让手机拍摄效果媲美微单相机。以下是核心功能总结&#xff1a; 一、核心功能 专业拍摄模式 支持手动/自动/程序模式&#xff0c;可调节ISO、快门速度、EV、白平衡等参数27 提供…

Unity2017打包出来后的场景一片红

bug展示&#xff1a; 解决办法&#xff1a; Edit——Project Settings——Graphics——添加下面这俩Shader(用于UI展示)即可。

VS Code C++ 开发环境配置

VS Code 是当前非常流行的开发工具. 本文讲述如何配置 VS Code 作为 C开发环境. 本文将按照如下步骤来介绍如何配置 VS Code 作为 C开发环境. 安装编译器安装插件配置工作区 第一个步骤的具体操作会因为系统不同或者方案不同而有不同的选择. 环境要求 首先需要立即 VS Code…

html-表格标签

一、表格标签 1. 表格的主要作用 表格主要用于显示&#xff64;展示数据,因为它可以让数据显示的非常的规整,可读性非常好&#xff61;特别是后台展示数据 的时候,能够熟练运用表格就显得很重要&#xff61;一个清爽简约的表格能够把繁杂的数据表现得很有条理&#xff61; 总…