Python12 PyQt5实现鼠标移动绘制图(自定义画笔粗细和颜色,橡皮擦功能,保存文件)

本文主要是介绍Python12 PyQt5实现鼠标移动绘制图(自定义画笔粗细和颜色,橡皮擦功能,保存文件),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作业要求

在这里插入图片描述
下载后直接用python运行软件打开就行了(Tensorflow不支持)
附件链接://download.csdn.net/download/weixin_44382897/12036092

可供参考实现的学习文件

老师给出的例子是可以实现图片的转换,然后让我们按照例子来实现画板程序的制作。老师例子的主要代码如下:
附件链接:
链接:https://pan.baidu.com/s/19CRNcntfriRGlP62_0cZIQ
提取码:ikqp

import os
import platform
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *class myMainWindow(QMainWindow):def __init__(self, parent=None):super().__init__(parent)self.image = QImage()self.dirty = Falseself.filename = Noneself.mirroredvertically = Falseself.mirroredhorizontally = False#图像self.imageLabel = QLabel()self.imageLabel.setAlignment(Qt.AlignCenter)self.imageLabel.setContextMenuPolicy(Qt.ActionsContextMenu)self.setCentralWidget(self.imageLabel)#右侧停靠窗口logDockWidget = QDockWidget("Log", self)logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea)self.listWidget = QListWidget()logDockWidget.setWidget(self.listWidget)self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)#状态栏self.sizeLabel = QLabel()self.sizeLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken)status = self.statusBar()status.setSizeGripEnabled(False)status.addPermanentWidget(self.sizeLabel)status.showMessage("Ready", 5000)#各种动作self.fileOpenAction = QAction(QIcon("images/fileopen.png"), "&Open", self)self.fileOpenAction.setShortcut(QKeySequence.Open)self.fileOpenAction.setToolTip("Open an image.")self.fileOpenAction.setStatusTip("Open an image.")self.fileOpenAction.triggered.connect(self.fileOpen)self.fileSaveAction = QAction(QIcon("images/filesave.png"), "&Save", self)self.fileSaveAction.setShortcut(QKeySequence.Save)self.fileSaveAction.setToolTip("Save an image.")self.fileSaveAction.setStatusTip("Save an image.")self.fileSaveAction.triggered.connect(self.fileSaveAs)self.editUnMirrorAction = QAction(QIcon("images/editunmirror.png"), "&Unmirror", self)self.editUnMirrorAction.setShortcut("Ctrl+U")self.editUnMirrorAction.setToolTip("Unmirror the image")self.editUnMirrorAction.setStatusTip("Unmirror the image")self.editUnMirrorAction.setCheckable(True)self.editUnMirrorAction.setChecked(True)self.editUnMirrorAction.toggled.connect(self.editUnMirror)editMirrorHorizontalAction = QAction(QIcon("images/editmirrorhoriz.png"), "Mirror &Horizontally", self)editMirrorHorizontalAction.setShortcut("Ctrl+H")editMirrorHorizontalAction.setToolTip("Horizontally mirror the image")editMirrorHorizontalAction.setStatusTip("Horizontally mirror the image")editMirrorHorizontalAction.setCheckable(True)editMirrorHorizontalAction.toggled.connect(self.editMirrorHorizontal)editMirrorVerticalAction = QAction(QIcon("images/editmirrorvert.png"), "Mirror &Vertically", self)editMirrorVerticalAction.setShortcut("Ctrl+V")editMirrorVerticalAction.setToolTip("Vertically mirror the image")editMirrorVerticalAction.setStatusTip("Vertically mirror the image")editMirrorVerticalAction.setCheckable(True)editMirrorVerticalAction.toggled.connect(self.editMirrorVertical)mirrorGroup = QActionGroup(self)mirrorGroup.addAction(self.editUnMirrorAction)mirrorGroup.addAction(editMirrorHorizontalAction)mirrorGroup.addAction(editMirrorVerticalAction)#菜单栏self.fileMenu = self.menuBar().addMenu("&File")            self.fileMenu.addAction(self.fileOpenAction)self.fileMenu.addAction(self.fileSaveAction)editMenu = self.menuBar().addMenu("&Edit")editMenu.addAction(self.editUnMirrorAction)editMenu.addAction(editMirrorHorizontalAction)editMenu.addAction(editMirrorVerticalAction)#工具栏fileToolbar = self.addToolBar("File")fileToolbar.addAction(self.fileOpenAction)fileToolbar.addAction(self.fileSaveAction)editToolbar = self.addToolBar("Edit")editToolbar.addAction(self.editUnMirrorAction)editToolbar.addAction(editMirrorHorizontalAction)editToolbar.addAction(editMirrorVerticalAction)self.recentFiles = []self.setWindowTitle("Image Changer")      def okToContinue(self): #警告当前图像未保存if self.dirty:reply = QMessageBox.question(self,"Image Changer - Unsaved Changes","Save unsaved changes?",QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel)if reply == QMessageBox.Cancel:return Falseelif reply == QMessageBox.Yes:return self.fileSaveAs()return Truedef fileOpen(self):if not self.okToContinue():returndir = (os.path.dirname(self.filename)if self.filename is not None else ".")formats = (["*.{}".format(format.data().decode("ascii").lower())for format in QImageReader.supportedImageFormats()])fname = QFileDialog.getOpenFileName(self,"Image Changer - Choose Image", dir,"Image files ({})".format(" ".join(formats)))if fname:print(fname[0])self.loadFile(fname[0])self.updateFileMenu()def loadFile(self, fname=None):if fname is None:action = self.sender()if isinstance(action, QAction):fname = action.data()if not self.okToContinue():returnelse:returnif fname:self.filename = Noneimage = QImage(fname)if image.isNull():message = "Failed to read {}".format(fname)else:self.addRecentFile(fname)self.image = QImage()self.editUnMirrorAction.setChecked(True)self.image = imageself.filename = fnameself.showImage()self.dirty = Falseself.sizeLabel.setText("{} x {}".format(image.width(), image.height()))message = "Loaded {}".format(os.path.basename(fname))self.updateStatus(message)def updateStatus(self, message):self.statusBar().showMessage(message, 5000)self.listWidget.addItem(message)if self.filename:self.setWindowTitle("Image Changer - {}[*]".format(os.path.basename(self.filename)))elif not self.image.isNull():self.setWindowTitle("Image Changer - Unnamed[*]")else:self.setWindowTitle("Image Changer[*]")self.setWindowModified(self.dirty)def updateFileMenu(self):self.fileMenu.clear()self.fileMenu.addAction(self.fileOpenAction)self.fileMenu.addAction(self.fileSaveAction)current = self.filenamerecentFiles = []print(self.recentFiles)for fname in self.recentFiles:if fname != current and QFile.exists(fname):recentFiles.append(fname)if recentFiles:self.fileMenu.addSeparator()for i, fname in enumerate(recentFiles):action = QAction(QIcon("images/icon.png"),"&{} {}".format(i + 1, QFileInfo(fname).fileName()), self)action.setData(fname)action.triggered.connect(lambda: self.loadFile(fname))self.fileMenu.addAction(action)def addRecentFile(self, fname):if fname is None:returnif fname not in self.recentFiles:                 if len(self.recentFiles) < 10:self.recentFiles = [fname] + self.recentFileselse:self.recentFiles = [fname] + self.recentFiles[:8]print(len(self.recentFiles))def fileSaveAs(self):if self.image.isNull():return Truefname = self.filename if self.filename is not None else "."formats = (["*.{}".format(format.data().decode("ascii").lower())for format in QImageWriter.supportedImageFormats()])fname = QFileDialog.getSaveFileName(self,"Image Changer - Save Image", fname,"Image files ({})".format(" ".join(formats)))fname = fname[0]if fname:print(fname)if "." not in fname:fname += ".png"self.addRecentFile(fname)self.filename = fnameif self.image.save(self.filename, None):self.updateStatus("Saved as {}".format(self.filename))self.dirty = Falsereturn Trueelse:self.updateStatus("Failed to save {}".format(self.filename))return False                  return Falsedef editUnMirror(self, on):if self.image.isNull():returnif self.mirroredhorizontally:self.editMirrorHorizontal(False)if self.mirroredvertically:self.editMirrorVertical(False)def editMirrorHorizontal(self, on):if self.image.isNull():returnself.image = self.image.mirrored(True, False)self.showImage()self.mirroredhorizontally = not self.mirroredhorizontallyself.dirty = Trueself.updateStatus(("Mirrored Horizontally"if on else "Unmirrored Horizontally"))def editMirrorVertical(self, on):if self.image.isNull():returnself.image = self.image.mirrored(False, True)self.showImage()self.mirroredvertically = not self.mirroredverticallyself.dirty = Trueself.updateStatus(("Mirrored Vertically"if on else "Unmirrored Vertically"))def showImage(self, percent=None):if self.image.isNull():returnself.imageLabel.setPixmap(QPixmap.fromImage(self.image))app = QApplication(sys.argv)
form = myMainWindow()
form.setMinimumSize(1000, 1000)
form.show()
app.exec_()

代码的运行效果:
可以实现图片的倒转
根据老师提出的问题,我的解决方案参考了网上其他博客的代码:

在这里插入代码片
# 参考本节例程,实现一个鼠标绘图程序:
# 可以更改笔刷的颜色和粗细,具体方式自己设计,合理即可
# 可以读取和保存图片import platform
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *from PyQt5.QtWidgets import QApplication
import sys
from PyQt5.QtWidgets import QWidget
from PyQt5.Qt import QPixmap, QPainter, QPoint, QPaintEvent, QMouseEvent, QPen,\QColor, QSize
from PyQt5.QtCore import Qt
from PyQt5.Qt import QWidget, QColor, QPixmap, QIcon, QSize, QCheckBox
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QPushButton, QSplitter,\QComboBox, QLabel, QSpinBox, QFileDialogdef main():app = QApplication(sys.argv)mainWidget = MainWidget() #新建一个主界面mainWidget.show()    #显示主界面exit(app.exec_()) #进入消息循环class PaintBoard(QWidget):def __init__(self, Parent=None):'''Constructor'''super().__init__(Parent)self.__InitData()  # 先初始化数据,再初始化界面self.__InitView()self.setWindowTitle("画笔")def __InitData(self):self.__size = QSize(480, 460)# 新建QPixmap作为画板,尺寸为__sizeself.__board = QPixmap(self.__size)self.__board.fill(Qt.white)  # 用白色填充画板self.__IsEmpty = True  # 默认为空画板self.EraserMode = False  # 默认为禁用橡皮擦模式self.__lastPos = QPoint(0, 0)  # 上一次鼠标位置self.__currentPos = QPoint(0, 0)  # 当前的鼠标位置self.__painter = QPainter()  # 新建绘图工具self.__thickness = 10  # 默认画笔粗细为10pxself.__penColor = QColor("black")  # 设置默认画笔颜色为黑色self.__colorList = QColor.colorNames()  # 获取颜色列表def __InitView(self):# 设置界面的尺寸为__sizeself.setFixedSize(self.__size)def Clear(self):# 清空画板self.__board.fill(Qt.white)self.update()self.__IsEmpty = Truedef ChangePenColor(self, color="black"):# 改变画笔颜色self.__penColor = QColor(color)def ChangePenThickness(self, thickness=10):# 改变画笔粗细self.__thickness = thicknessdef IsEmpty(self):# 返回画板是否为空return self.__IsEmptydef GetContentAsQImage(self):# 获取画板内容(返回QImage)image = self.__board.toImage()return imagedef paintEvent(self, paintEvent):# 绘图事件# 绘图时必须使用QPainter的实例,此处为__painter# 绘图在begin()函数与end()函数间进行# begin(param)的参数要指定绘图设备,即把图画在哪里# drawPixmap用于绘制QPixmap类型的对象self.__painter.begin(self)# 0,0为绘图的左上角起点的坐标,__board即要绘制的图self.__painter.drawPixmap(0, 0, self.__board)self.__painter.end()def mousePressEvent(self, mouseEvent):# 鼠标按下时,获取鼠标的当前位置保存为上一次位置self.__currentPos = mouseEvent.pos()self.__lastPos = self.__currentPosdef mouseMoveEvent(self, mouseEvent):# 鼠标移动时,更新当前位置,并在上一个位置和当前位置间画线self.__currentPos = mouseEvent.pos()self.__painter.begin(self.__board)if self.EraserMode == False:# 非橡皮擦模式self.__painter.setPen(QPen(self.__penColor, self.__thickness))  # 设置画笔颜色,粗细else:# 橡皮擦模式下画笔为纯白色,粗细为10self.__painter.setPen(QPen(Qt.white, 10))# 画线self.__painter.drawLine(self.__lastPos, self.__currentPos)self.__painter.end()self.__lastPos = self.__currentPosself.update()  # 更新显示def mouseReleaseEvent(self, mouseEvent):self.__IsEmpty = False  # 画板不再为空class MainWidget(QWidget):def __init__(self, Parent=None):'''Constructor'''super().__init__(Parent)self.__InitData()  # 先初始化数据,再初始化界面self.__InitView()def __InitData(self):'''初始化成员变量'''self.__paintBoard = PaintBoard(self)# 获取颜色列表(字符串类型)self.__colorList = QColor.colorNames()def __InitView(self):'''初始化界面'''self.setFixedSize(640, 480)self.setWindowTitle("Paint brush")# 新建一个水平布局作为本窗体的主布局main_layout = QHBoxLayout(self)# 设置主布局内边距以及控件间距为10pxmain_layout.setSpacing(10)# 在主界面左侧放置画板main_layout.addWidget(self.__paintBoard)# 新建垂直子布局用于放置按键sub_layout = QVBoxLayout()# 设置此子布局和内部控件的间距为10pxsub_layout.setContentsMargins(10, 10, 10, 10)self.__btn_Clear = QPushButton("清空画板")self.__btn_Clear.setParent(self)  # 设置父对象为本界面# 将按键按下信号与画板清空函数相关联self.__btn_Clear.clicked.connect(self.__paintBoard.Clear)sub_layout.addWidget(self.__btn_Clear)self.__btn_Quit = QPushButton("退出")self.__btn_Quit.setParent(self)  # 设置父对象为本界面self.__btn_Quit.clicked.connect(self.Quit)sub_layout.addWidget(self.__btn_Quit)self.__btn_Save = QPushButton("保存作品")self.__btn_Save.setParent(self)self.__btn_Save.clicked.connect(self.on_btn_Save_Clicked)sub_layout.addWidget(self.__btn_Save)self.__cbtn_Eraser = QCheckBox("  使用橡皮擦")self.__cbtn_Eraser.setParent(self)self.__cbtn_Eraser.clicked.connect(self.on_cbtn_Eraser_clicked)sub_layout.addWidget(self.__cbtn_Eraser)splitter = QSplitter(self)  # 占位符sub_layout.addWidget(splitter)self.__label_penThickness = QLabel(self)self.__label_penThickness.setText("画笔粗细")self.__label_penThickness.setFixedHeight(20)sub_layout.addWidget(self.__label_penThickness)self.__spinBox_penThickness = QSpinBox(self)self.__spinBox_penThickness.setMaximum(20)self.__spinBox_penThickness.setMinimum(2)self.__spinBox_penThickness.setValue(10)  # 默认粗细为10self.__spinBox_penThickness.setSingleStep(2)  # 最小变化值为2self.__spinBox_penThickness.valueChanged.connect(self.on_PenThicknessChange)  # 关联spinBox值变化信号和函数on_PenThicknessChangesub_layout.addWidget(self.__spinBox_penThickness)self.__label_penColor = QLabel(self)self.__label_penColor.setText("画笔颜色")self.__label_penColor.setFixedHeight(20)sub_layout.addWidget(self.__label_penColor)self.__comboBox_penColor = QComboBox(self)self.__fillColorList(self.__comboBox_penColor)  # 用各种颜色填充下拉列表self.__comboBox_penColor.currentIndexChanged.connect(self.on_PenColorChange)  # 关联下拉列表的当前索引变更信号与函数on_PenColorChangesub_layout.addWidget(self.__comboBox_penColor)main_layout.addLayout(sub_layout)  # 将子布局加入主布局def __fillColorList(self, comboBox):index_black = 0index = 0for color in self.__colorList:if color == "black":index_black = indexindex += 1pix = QPixmap(70, 20)pix.fill(QColor(color))comboBox.addItem(QIcon(pix), None)comboBox.setIconSize(QSize(70, 20))comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents)comboBox.setCurrentIndex(index_black)def on_PenColorChange(self):color_index = self.__comboBox_penColor.currentIndex()color_str = self.__colorList[color_index]self.__paintBoard.ChangePenColor(color_str)def on_PenThicknessChange(self):penThickness = self.__spinBox_penThickness.value()self.__paintBoard.ChangePenThickness(penThickness)def on_btn_Save_Clicked(self):savePath = QFileDialog.getSaveFileName(self, 'Save Your Paint', '.\\', '*.png')print(savePath)if savePath[0] == "":print("Save cancel")returnimage = self.__paintBoard.GetContentAsQImage()image.save(savePath[0])def on_cbtn_Eraser_clicked(self):if self.__cbtn_Eraser.isChecked():self.__paintBoard.EraserMode = True  # 进入橡皮擦模式else:self.__paintBoard.EraserMode = False  # 退出橡皮擦模式def Quit(self):self.close()if __name__ == '__main__':main()

代码实现效果:
画板实现效果图

这篇关于Python12 PyQt5实现鼠标移动绘制图(自定义画笔粗细和颜色,橡皮擦功能,保存文件)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/358666

相关文章

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

grom设置全局日志实现执行并打印sql语句

《grom设置全局日志实现执行并打印sql语句》本文主要介绍了grom设置全局日志实现执行并打印sql语句,包括设置日志级别、实现自定义Logger接口以及如何使用GORM的默认logger,通过这些... 目录gorm中的自定义日志gorm中日志的其他操作日志级别Debug自定义 Loggergorm中的

Spring Boot整合消息队列RabbitMQ的实现示例

《SpringBoot整合消息队列RabbitMQ的实现示例》本文主要介绍了SpringBoot整合消息队列RabbitMQ的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录RabbitMQ 简介与安装1. RabbitMQ 简介2. RabbitMQ 安装Spring

Gin框架中的GET和POST表单处理的实现

《Gin框架中的GET和POST表单处理的实现》Gin框架提供了简单而强大的机制来处理GET和POST表单提交的数据,通过c.Query、c.PostForm、c.Bind和c.Request.For... 目录一、GET表单处理二、POST表单处理1. 使用c.PostForm获取表单字段:2. 绑定到结

springMVC返回Http响应的实现

《springMVC返回Http响应的实现》本文主要介绍了在SpringBoot中使用@Controller、@ResponseBody和@RestController注解进行HTTP响应返回的方法,... 目录一、返回页面二、@Controller和@ResponseBody与RestController

nginx中重定向的实现

《nginx中重定向的实现》本文主要介绍了Nginx中location匹配和rewrite重定向的规则与应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下... 目录一、location1、 location匹配2、 location匹配的分类2.1 精确匹配2

Nginx之upstream被动式重试机制的实现

《Nginx之upstream被动式重试机制的实现》本文主要介绍了Nginx之upstream被动式重试机制的实现,可以通过proxy_next_upstream来自定义配置,具有一定的参考价值,感兴... 目录默认错误选择定义错误指令配置proxy_next_upstreamproxy_next_upst