"怎么使用Python和Tkinter实现简单的画图软件"
时间:2023-05-10 06:50
为什么突然想搞这个画图软件呢 不瞒各位,是因为最近接到了一个很小很小很小小得不能再小的小项目 就是基于Tkinter,做一个简易的画图软件,要求不高,能画就行,能保存就行,能撤回就行,能导入就行! 于是,遇到项目就精神抖擞的俺,三下五除二的就夸夸夸的写,终于!花了将近两个小时多的时间,写出来了一个还用得过去得画图软件,虽然这个画图软件是写出来了,但是,俺意犹未尽呀(贪婪!太贪婪了!),于是想搞一个更加NB一点的画图软件,于是我打开浏览器(哦不),打开俺滴大脑,想着提升一下画图软件的功能,于是就写了以下画图软件 基本介绍:构造一个GUI图形界面,主菜单有导入图片、保存截图、清屏、撤销、工具栏等功能,工具栏中有铅笔画图、画直线、画矩形、画圆形、添加文本、橡皮擦、颜色填充、设置前景色和设置背景色等功能。 用到的模块:Tkinter、PIL 画不多说,展示一波 话不多说,介绍一波 导入。可导入后缀名为jpg、png、gif的图片,可在软件上呈现图片,可进行绘画。 保存。任意截取屏幕上的部分,截取好后按下回车键,即可保存,若想退出,则按下esc键 清屏。顾名思义,咱就不多说了 撤销。即返回上一步,但是里面有一个小bug,具体是啥,各位猜猜 工具栏。工具栏里有啥呢?进来看看就知道咯 以上就是"怎么使用Python和Tkinter实现简单的画图软件"的详细内容,更多请关注Gxl网其它相关文章!一起画图吧
画图软件
实现代码
import tkinter as tkfrom tkinter import *import tkinter.simpledialogimport tkinter.colorchooserimport tkinter.filedialogfrom PIL import Image, ImageTk, ImageGrabfrom tkinter.colorchooser import askcolorfrom win32 import win32api, win32gui, win32printfrom win32.lib import win32confrom win32.win32api import GetSystemMetricsclass Draw_designs(tk.Frame): def __init__(self, master=None): tk.Frame.__init__(self, master) self.master = master self.pack() self.temp = [] # 保存图形的类型 self.li = [] # 保存所画图形的坐标 self.fill_color = None # 保存填充的颜色 self.lastDraw = 0 self.end = [0] self.size = "12" # 字体大小 self.yesno = 0 self.function = 1 # 默认铅笔 self.X = 0 self.Y = 0 self.foreColor = '#000000' self.backColor = '#FFFFFF' self.create_widget() self.setMenu() def create_widget(self): self.image = PhotoImage() self.canvas = Canvas(root, bg='white', width=x, height=y) # 创建画布 self.canvas.create_image(x, y, image=self.image) self.canvas.bind('<Button-1>', self.onLeftButtonDown) self.canvas.bind('<B1-Motion>', self.onLeftButtonMove) self.canvas.bind('<ButtonRelease-1>', self.onLeftButtonUp) self.canvas.bind('<ButtonRelease-3>', self.onRightButtonUp) self.canvas.pack(fill=tk.BOTH, expand=tk.YES) def setMenu(self): '''主菜单及其关联的函数''' self.menu = tk.Menu(self, bg="red") root.config(menu=self.menu) self.menu.add_command(label='导入', command=self.Import) self.menu.add_command(label='保存', command=self.SavePicture) self.menu.add_command(label='清屏', command=self.Clear) self.menu.add_command(label='撤销', command=self.Back) '''子菜单及其关联的函数''' self.menuType = tk.Menu(self.menu, tearoff=0) # tearoff=0 - 表示无法将下拉菜单从“工具栏”窗口分离 self.menu.add_cascade(label='工具栏', menu=self.menuType) # add_cascade建立菜单类别对象 # 在"工具栏"内建立菜单列表 self.menuType.add_command(label='铅笔', command=self.drawCurve) self.menuType.add_command(label='直线', command=self.drawLine) self.menuType.add_command(label='矩形', command=self.drawRectangle) self.menuType.add_command(label='圆形', command=self.drawCircle) self.menuType.add_command(label='文本', command=self.drawText) self.menuType.add_command(label='橡皮擦', command=self.onErase) self.menuType.add_command(label='颜色填充', command=self.fill) self.menuType.add_separator() # 建立分隔线 self.menuType.add_command(label='选择前景色', command=self.chooseForeColor) self.menuType.add_command(label='选择背景色', command=self.chooseBackColor) def Import(self): # 导入文件 filename = tk.filedialog.askopenfilename(title='导入图片', filetypes=[('image', '*.jpg *.png *.gif')]) if filename: self.image = Image.open(filename) self.image = self.image.resize((800, 600), Image.ANTIALIAS) self.image = ImageTk.PhotoImage(self.image) self.canvas.create_image(400, 300, image=self.image) def SavePicture(self): # 保存画布 ScreenShot() def Clear(self): # 清屏 for item in self.canvas.find_all(): self.canvas.delete(item) # 清屏后对数据进行初始化 self.end = [0] self.lastDraw = 0 def Back(self): # 撤回 try: for i in range(self.end[-2], self.end[-1] + 1): self.canvas.delete(i) self.end.pop() self.li.pop() except: self.end = [0] def onLeftButtonDown(self, event): # 点击鼠标左键后运行此函数 self.yesno = 1 self.X = event.x self.Y = event.y if self.function == 7: # 颜色填充 for i in range(len(self.li)): if (self.X >= self.li[i][0] and self.X <= self.li[i][2]) and (self.Y >= self.li[i][1] and self.Y <= self.li[i][3]): if self.temp[i] == 'rect': rect = self.canvas.create_rectangle(self.li[i][0], self.li[i][1], self.li[i][2], self.li[i][3]) self.canvas.itemconfig(rect, fill=self.fill_color) self.end.append(rect) # 加入撤销列表 elif self.temp[i] == 'oval': oval = self.canvas.create_oval(self.li[i][0], self.li[i][1], self.li[i][2], self.li[i][3]) self.canvas.itemconfig(oval, fill=self.fill_color) self.end.append(oval) # 加入撤销列表 break if self.function == 4: self.canvas.create_text(event.x, event.y, font=("等线", int(self.size)), text=self.text, fill=self.foreColor) self.function = 1 def onLeftButtonMove(self, event): # 按下鼠标左键并移动后运行此函数 if self.yesno == 0: return if self.function == 1: # 铅笔 self.lastDraw = self.canvas.create_line(self.X, self.Y, event.x, event.y, fill=self.foreColor) self.X = event.x self.Y = event.y elif self.function == 2: # 画直线 try: self.canvas.delete(self.lastDraw) except Exception: pass self.lastDraw = self.canvas.create_line(self.X, self.Y, event.x, event.y, fill=self.foreColor) elif self.function == 3: # 画矩形 try: self.canvas.delete(self.lastDraw) except Exception: pass self.lastDraw = self.canvas.create_rectangle(self.X, self.Y, event.x, event.y, outline=self.foreColor) elif self.function == 5: # 橡皮擦 self.lastDraw = self.canvas.create_rectangle(event.x - 10, event.y - 10, event.x + 10, event.y + 10, outline=self.backColor) elif self.function == 6: # 画圆 try: self.canvas.delete(self.lastDraw) except Exception: pass self.lastDraw = self.canvas.create_oval(self.X, self.Y, event.x, event.y, fill=self.backColor, outline=self.foreColor) def onLeftButtonUp(self, event): # 左键鼠标释放后运行此函数 if self.function == 2: self.lastDraw = self.canvas.create_line(self.X, self.Y, event.x, event.y, fill=self.foreColor) elif self.function == 3: # 正方形 self.lastDraw = self.canvas.create_rectangle(self.X, self.Y, event.x, event.y, outline=self.foreColor) self.li.append((self.X, self.Y, event.x, event.y)) # 保存图型的坐标 self.temp.append('rect') elif self.function == 6: # 圆形 self.lastDraw = self.canvas.create_oval(self.X, self.Y, event.x, event.y, outline=self.foreColor) self.li.append((self.X, self.Y, event.x, event.y)) # 保存图型的坐标 self.temp.append('oval') self.yesno = 0 if self.function != 7: self.end.append(self.lastDraw) def onRightButtonUp(self, event): # 在画布中鼠标右键按下并松开时,弹出菜单 self.menu.post(event.x_root, event.y_root) def drawCurve(self): # 铅笔 self.function = 1 def drawLine(self): # 直线 self.function = 2 def drawRectangle(self): # 矩形 self.function = 3 def drawCircle(self): # 画圆 self.function = 6 def drawText(self): # 文字 self.text = tk.simpledialog.askstring(title='输入文本', prompt='') if self.text is not None: self.size = tk.simpledialog.askinteger('输入字号', prompt='', initialvalue=20) if self.size is None: self.size = "20" self.function = 4 def onErase(self): # 橡皮擦 self.function = 5 def fill(self): c = askcolor(color=self.foreColor, title="选择画笔颜色") self.fill_color = c[1] self.function = 7 def chooseForeColor(self): # 设置前景色 self.foreColor = tk.colorchooser.askcolor()[1] def chooseBackColor(self): # 设置背景色 self.backColor = tk.colorchooser.askcolor()[1]"""------------- 截图 -----------------"""def get_real_resolution(): """获取真实的分辨率""" hDC = win32gui.GetDC(0) # 横向分辨率 w = win32print.GetDeviceCaps(hDC, win32con.DESKTOPHORZRES) # 纵向分辨率 h = win32print.GetDeviceCaps(hDC, win32con.DESKTOPVERTRES) return w, hdef get_screen_size(): """获取缩放后的分辨率""" w = GetSystemMetrics(0) h = GetSystemMetrics(1) return w, hreal_resolution = get_real_resolution()screen_size = get_screen_size()# Windows 设置的屏幕缩放率# ImageGrab 的参数是基于显示分辨率的坐标,而 tkinter 获取到的是基于缩放后的分辨率的坐标screen_scale_rate = round(real_resolution[0] / screen_size[0], 2)class Box: def __init__(self): self.start_x = None self.start_y = None self.end_x = None self.end_y = None def isNone(self): return self.start_x is None or self.end_x is None def setStart(self, x, y): self.start_x = x self.start_y = y def setEnd(self, x, y): self.end_x = x self.end_y = y def box(self): lt_x = min(self.start_x, self.end_x) lt_y = min(self.start_y, self.end_y) rb_x = max(self.start_x, self.end_x) rb_y = max(self.start_y, self.end_y) return lt_x, lt_y, rb_x, rb_y def center(self): center_x = (self.start_x + self.end_x) / 2 center_y = (self.start_y + self.end_y) / 2 return center_x, center_yclass SelectionArea: def __init__(self, canvas: tk.Canvas): self.canvas = canvas self.area_box = Box() def empty(self): return self.area_box.isNone() def setStartPoint(self, x, y): self.canvas.delete('area', 'lt_txt', 'rb_txt') self.area_box.setStart(x, y) # 开始坐标文字 self.canvas.create_text( x, y - 10, text=f'({x}, {y})', fill='red', tag='lt_txt') def updateEndPoint(self, x, y): self.area_box.setEnd(x, y) self.canvas.delete('area', 'rb_txt') box_area = self.area_box.box() # 选择区域 self.canvas.create_rectangle( *box_area, fill='black', outline='red', width=2, tags="area") self.canvas.create_text( x, y + 10, text=f'({x}, {y})', fill='red', tag='rb_txt')class ScreenShot(): def __init__(self, scaling_factor=2): self.win = tk.Tk() # self.win.tk.call('tk', 'scaling', scaling_factor) self.width = self.win.winfo_screenwidth() self.height = self.win.winfo_screenheight() # 无边框,没有最小化最大化关闭这几个按钮,也无法拖动这个窗体,程序的窗体在Windows系统任务栏上也消失 self.win.overrideredirect(True) self.win.attributes('-alpha', 0.25) self.is_selecting = False # 绑定按 Enter 确认, Esc 退出 self.win.bind('<KeyPress-Escape>', self.exit) self.win.bind('<KeyPress-Return>', self.confirmScreenShot) self.win.bind('<Button-1>', self.selectStart) self.win.bind('<ButtonRelease-1>', self.selectDone) self.win.bind('<Motion>', self.changeSelectionArea) self.canvas = tk.Canvas(self.win, width=self.width, height=self.height) self.canvas.pack() self.area = SelectionArea(self.canvas) self.win.mainloop() def exit(self, event): self.win.destroy() def clear(self): self.canvas.delete('area', 'lt_txt', 'rb_txt') self.win.attributes('-alpha', 0) def captureImage(self): if self.area.empty(): return None else: filename = tk.filedialog.asksaveasfilename(filetypes=[('.jpg', 'JPG')], initialdir='C:\Users\lin042\Desktop\') box_area = [x * screen_scale_rate for x in self.area.area_box.box()] self.clear() img = ImageGrab.grab(box_area).save(filename) return img def confirmScreenShot(self, event): img = self.captureImage() if img is not None: img.show() self.win.destroy() def selectStart(self, event): self.is_selecting = True self.area.setStartPoint(event.x, event.y) # print('Select', event) def changeSelectionArea(self, event): if self.is_selecting: self.area.updateEndPoint(event.x, event.y) # print(event) def selectDone(self, event): self.is_selecting = Falseif __name__ == '__main__': x = 1200 # 宽 y = 600 # 高 root = tk.Tk() root.title('街三仔画图') # 软件名 root.geometry('1200x600') # 设置软件大小 - 宽x高 Draw_designs(root) root.mainloop()