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

相关文章

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM