利用Python GUI写一个简单的绘画板

文章介绍了使用Python的Tkinter库创建一个绘画板应用,包括颜色选择、橡皮擦、清屏和保存图片功能。在Tkinter版本中,通过LabelFrame、Textwidget和Button等构建界面,用滑动条调整画笔粗细。作者从零基础学习,遇到问题主要依靠CSDN资源。在转向PyQt5实现时,面临橡皮擦颜色与画笔颜色相同的问题,且画笔粗细只能设定为预设值,未能实现滑动条控制。
摘要由CSDN通过智能技术生成

先说废话,再上代码

这个绘画板主要运用了Python自带的标准GUI库Tkinter,包含了画笔颜色选择,背景布颜色选择,橡皮擦,清屏,保存图片等功能。

from tkinter import *
from tkinter.ttk import Scale
from tkinter import colorchooser, filedialog, messagebox
import PIL.ImageGrab as ImageGrab
from tkinter.colorchooser import  askcolor
# from ttkbootstrap import ttk
from tkmacosx import Button

# Defining Class and constructor of the Program
class Draw():
    def __init__(self, root):

        # Defining title and Size of the Tkinter Window GUI
        self.root = root
        self.root.title("画图工具Python")
        self.root.geometry("1000x1000")
        self.root.configure(background="white")
        #         self.root.resizable(0,0)

        # variables for pointer and Eraser
        self.pointer = "black"
        self.erase = "white"

        # Widgets for Tkinter Window

        # Configure the alignment , font size and color of the text
        text = Text(root, height=5, width=100)
        text.tag_configure("tag_name", justify='center', font=('arial', 25), background='#292826', foreground='orange')

        # Insert a Text
        text.insert("1.0", "python中画画")

        # Add the tag for following given text
        text.tag_add("tag_name", "1.0", "end")
        text.pack()

        # Pick a color for drawing from color pannel
        self.pick_color = LabelFrame(self.root, text='Colors', font=('arial', 15), bd=5, relief=RIDGE, bg="white")
        self.pick_color.place(x=0, y=40, width=135, height=525)

        colors = ['blue', 'red', 'green', 'orange', 'violet', 'black', 'yellow', 'purple', 'pink', 'gold', 'brown',
                  'indigo']
        i = j = 0
        for color in colors:

            Button(self.pick_color, bg=color, width=61,
                   command=lambda col=color: self.select_color(col)).grid(row=i, column=j)
            i += 1
            if i == 6:
                i = 0
                j = 1

        # 擦除按钮
        self.eraser_btn = Button(self.root, text="Eraser", command=self.eraser, width=120)
        self.eraser_btn.place(x=7, y=230)  # 改

        # 清屏
        self.clear_screen = Button(self.root, text="Clear Screen",width=120,
                                   command=lambda: self.background.delete('all'))
        self.clear_screen.place(x=7, y=260)

        # 保存
        self.save_btn = Button(self.root, text="ScreenShot",command=self.save_drawing,width=120)
        self.save_btn.place(x=7, y=290)

        # 改变画布背景
        self.bg_btn = Button(self.root, text="Background", command=self.canvas_color,width=120)
        self.bg_btn.place(x=7, y=320)

        # 改变画笔大小
        self.pointer_frame = LabelFrame(self.root, text='size', bd=5, bg='white', font=('arial', 15, 'bold'),
                                        relief=RIDGE)
        self.pointer_frame.place(x=33, y=380, height=200, width=70)

        self.pointer_size = Scale(self.pointer_frame, orient=VERTICAL, from_=1, to=48, length=168)
        self.pointer_size.set(1)
        self.pointer_size.grid(row=0, column=1, padx=15)

        # 定一个画布
        self.background = Canvas(self.root, bg='white', bd=5, relief=GROOVE, height=890, width=800)
        self.background.place(x=140, y=45)

        # 默认光标形状为箭头
        self.cursor_shape = "arrow"


        # 监听画布被点击
        self.background.bind("<B1-Motion>", self.paint)

    # Functions are defined here

    # 画笔函数
    def paint(self, event):
        x1, y1 = (event.x - 2), (event.y - 2)
        x2, y2 = (event.x + 2), (event.y + 2)

        self.background.create_oval(x1, y1, x2, y2, fill=self.pointer, outline=self.pointer,
                                    width=self.pointer_size.get())
    # Function for choosing the color of pointer
    def select_color(self, col):
        self.pointer = col
        self.cursor_shape: str = "pencil"  # 更改光标形状为铅笔
        self.root.configure(cursor=self.cursor_shape)
    # Function for defining the eraser
    def eraser(self):
        self.pointer = self.erase
        self.cursor_shape: str = "dotbox"  # 更改光标形状为点框
        self.root.configure(cursor=self.cursor_shape)

    # Function for choosing the background color of the Canvas
    def canvas_color(self):
        color = colorchooser.askcolor()
        self.background.configure(background=color[1])
        self.erase = color[1]
        self.cursor_shape: str = "arrow"  # 更改光标形状为箭头
        self.root.configure(cursor=self.cursor_shape)

    # Function for saving the image file in Local Computer
    def save_drawing(self):
        try:
            # self.background update()
            file_ss = filedialog.asksaveasfilename(defaultextension='jpg')
            # print(file_ss)
            x = self.root.winfo_rootx() + self.background.winfo_x()
            # print(x, self.background.winfo_x())
            y = self.root.winfo_rooty() + self.background.winfo_y()
            # print(y)

            x1 = x + self.background.winfo_width()
            # print(x1)
            y1 = y + self.background.winfo_height()
            # print(y1)
            ImageGrab.grab().crop((x, y, x1, y1)).save(file_ss)
            messagebox.showinfo('Screenshot Successfully Saved as' + str(file_ss))

        except:
            print("Error in saving the screenshot")


if __name__ == "__main__":
    root = Tk()
    p = Draw(root)
    root.mainloop()

显示结果:

 软件特色:

1.框架采用的是LabelFrame

LabelFrame 组件是 Frame 组件的变体。默认情况下,LabelFrame 会在其子组件的周围绘制一个边框以及一个标题。

2.设置了一个总标题,橘色的字体更加显眼

# Configure the alignment , font size and color of the text
text = Text(root, height=5, width=100)
text.tag_configure("tag_name", justify='center', font=('arial', 25), background='#292826', foreground='orange')

3.简化代码,可以让后面的组件从1.0->最后都逐一pack

# Add the tag for following given text
        text.tag_add("tag_name", "1.0", "end")
        text.pack()

4.在色块选择时,列举出部分颜色,并逐一放置在组件中

colors = ['blue', 'red', 'green', 'orange', 'violet', 'black', 'yellow', 'purple', 'pink', 'gold', 'brown',
                  'indigo']
        i = j = 0
        for color in colors:

            Button(self.pick_color, bg=color, width=61,
                   command=lambda col=color: self.select_color(col)).grid(row=i, column=j)
            i += 1
            if i == 6:
                i = 0
                j = 1

5.画笔粗细调节采用的是滑动条来改变

# 改变画笔大小
        self.pointer_frame = LabelFrame(self.root, text='size', bd=5, bg='white', font=('arial', 15, 'bold'),
                                        relief=RIDGE)
        self.pointer_frame.place(x=33, y=380, height=200, width=70)

        self.pointer_size = Scale(self.pointer_frame, orient=VERTICAL, from_=1, to=48, length=168)
        self.pointer_size.set(1)
        self.pointer_size.grid(row=0, column=1, padx=15)

学习历程:

本人是Python零基础,开始拿到这个课题时,纯茫然,特别是要求设计一个绘画软件时,人都是懵的,但还是硬着头皮学,最开始先学习Python基础语法,我是根据书本来学习的(书本是上学期二手淘的),因为之前学习C语言就是看视频学习,老是分心,感觉学习周期会比较长,这次就尝试看书来学习,很快就对Python产生了兴趣,觉得比C语言代码简洁,然后就开始逐步学习,了解了PYthon自带的GUI库--Tkinter库,

遇到问题全靠CSDN,但是CSDN也不是万能的,很多时候都是自己茫然的搜索资料

但是仍感觉自己学的不精,脱离了书本等辅助资料,可能就啥也不会了

临近4.9号,才看到要求是用PyQt5设计,只有临时抱佛脚,大概了解一些常用的模块和类,并靠CSDN编写一个。但是写出来就有大问题,运行的时候,橡皮擦的鼠标光标倒是改变了,但是橡皮擦的颜色却与画笔颜色一样,尝试过修改橡皮擦颜色,失败了。

这个版本的画板,还有个缺点,画笔粗细只设置了小中大三个选项,没有像tkinter版设置一个滑动条,本想利用QInputDialog来直接输入画笔粗细,但是引用QInputDialog后出现报错

Process finished with exit code -1073741819 (0xC0000005)

不知道该怎么解决

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QFileDialog, QColorDialog, QMenu
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen, QPixmap, QCursor, QColor
from PyQt5.QtCore import Qt, QPoint


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("画图软件")
        self.setGeometry(100, 100, 800, 600)

        self.image = QImage(self.size(), QImage.Format_RGB32)
        self.image.fill(Qt.white)

        self.drawing = False
        self.brush_size = 2
        self.brush_color = Qt.white
        self.last_point = QPoint()
        self.color = self.palette().color(self.backgroundRole())

        self.initUI()

    def initUI(self):
        # 创建菜单栏
        menubar = self.menuBar()

        # 创建文件菜单
        file_menu = menubar.addMenu("文件")

        # 创建打开文件动作
        open_action = QAction(QIcon("icons/open.png"), "打开", self)
        open_action.triggered.connect(self.open_image)
        file_menu.addAction(open_action)

        # 创建保存文件动作
        save_action = QAction(QIcon("icons/save.png"), "保存", self)
        save_action.triggered.connect(self.save_image)
        file_menu.addAction(save_action)

        # 创建退出动作
        exit_action = QAction(QIcon("icons/exit.png"), "退出", self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 创建画笔菜单
        brush_menu = menubar.addMenu("画笔")

        # 创建颜色选择动作
        color_action = QAction(QIcon("icons/color.png"), "颜色", self)
        color_action.triggered.connect(self.choose_color)
        brush_menu.addAction(color_action)

        # 创建粗细选择动作
        size_menu = QMenu("粗细", self)
        brush_menu.addMenu(size_menu)

        # 创建粗细选择动作组
        size_group = QAction(QIcon("icons/size.png"), "粗细", self, checkable=True)
        size_group.triggered.connect(self.choose_size)
        size_menu.addAction(size_group)

        # 创建粗细选择动作
        small_size = QAction(QIcon("icons/small.png"), "小", self, checkable=True)
        small_size.triggered.connect(lambda: self.set_size(2))
        size_menu.addAction(small_size)

        medium_size = QAction(QIcon("icons/medium.png"), "中", self, checkable=True)
        medium_size.triggered.connect(lambda: self.set_size(4))
        size_menu.addAction(medium_size)

        large_size = QAction(QIcon("icons/large.png"), "大", self, checkable=True)
        large_size.triggered.connect(lambda: self.set_size(6))
        size_menu.addAction(large_size)

        # 创建橡皮擦菜单
        eraser_menu = menubar.addMenu("橡皮擦")

        # 创建橡皮擦动作
        eraser_action = QAction(QIcon("icons/eraser.png"), "橡皮擦", self, checkable=True,)
        eraser_action.triggered.connect(self.use_eraser)
        eraser_menu.addAction(eraser_action)

        # 创建清空画布动作
        clear_action = QAction(QIcon("icons/clear.png"), "清空", self)
        clear_action.triggered.connect(self.clear_image)
        eraser_menu.addAction(clear_action)

        self.show()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawImage(self.rect(), self.image, self.image.rect())

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.color = self.palette().color(self.backgroundRole())
            self.update()
            self.drawing = True
            self.last_point = event.pos()


    def mouseMoveEvent(self, event):
        if event.buttons() and Qt.LeftButton and self.drawing:
            painter = QPainter(self.image)
            painter.setPen(QPen(self.brush_color, self.brush_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
            painter.drawLine(self.last_point, event.pos())
            self.last_point = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drawing = False

    def open_image(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "打开图片", "",
                                                   "Images (*.png *.xpm *.jpg *.bmp);;All Files (*)")
        if file_name:
            self.image = QImage(file_name)
            self.update()

    def save_image(self):
        file_name, _ = QFileDialog.getSaveFileName(self, "保存图片", "",
                                                   "Images (*.png *.xpm *.jpg *.bmp);;All Files (*)")
        if file_name:
            self.image.save(file_name)

    def choose_color(self):
        color = QColorDialog.getColor()
        if color.isValid():
            self.brush_color = color

    def choose_size(self):
        pass

    def set_size(self, size):
        self.brush_size = size

    def use_eraser(self):

        self.setCursor(QCursor(Qt.PointingHandCursor))


    def clear_image(self):
        self.image.fill(Qt.white)
        self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())

显示结果:

 
PyQt5,Tkinter都是Python中常用的GUI工具包,它们各自有优缺点,具体如下:(百度搜索结果)

PyQt5的优点:

提供了丰富的GUI控件和功能,支持多种操作系统;
具有良好的文档和社区支持,易于学习和使用;
支持Qt Designer,可以通过可视化界面设计工具快速创建GUI界面;
支持多线程和网络编程,适合开发复杂的应用程序。
PyQt5的缺点:

由于其功能丰富,学习曲线较陡峭,需要花费一定的时间和精力;
PyQt5的许可证是GPL或商业许可证,商业使用需要购买许可证;
PyQt5的安装包较大,需要下载和安装许多依赖库。
Tkinter的优点:

是Python标准库中的一部分,无需额外安装;
学习曲线较平缓,易于入门;
支持多种GUI控件和布局管理器,可以创建简单的GUI界面;
适合快速开发小型应用程序。
Tkinter的缺点:

功能相对较少,不支持一些高级功能;
界面设计较为简单,不支持可视化界面设计工具;
不支持多线程和网络编程,不适合开发复杂的应用程序。

接下来再慢慢学习PyQt5吧...

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值