正在尝试着做一个比较完善的画图软件,计划使用tkinter库中的canvas控件来做,目前终于可以拿出来和大家见面了。
不忘初心,方得始终,让我们一起共勉!
好的,言归正传,下面我将详细介绍整个项目的建设过程。
先说一下提供的功能:
文件操作:
1)新建画布,原来的画布清理,然后重新新建一个全新的canvas画布。 2)保存画布,把当前的画布保存成图片。
图形操作:
1)画直线:画直线,并动态显示 2)画矩形:画矩形,并动态显示 3)画椭圆:画椭圆,并动态显示
选择工具:
1)移动:支持移动。 2)删除,选中后删除。
配置工具:
1)边框颜色:设置边框颜色 2)填充颜色:设置填充颜色 3)边框大小:设置边框大小
辅助功能:
提供工具栏,显示当前选中的图形,并随时记录当前鼠标所在的坐标位置。
一、使用canvas搭建画图软件的主窗口
from tkinter import *
root =Tk()
root.title('绘图软件')
root.geometry('700x480')
root.mainloop()
二、为了后续更好的扩展,引入类的概念。 对步骤一的软件主窗口先进行类对象化处理。
from tkinter import *
class MyCanvas:
def __init__(self, master):
self.master = master
self.init_canvas()
self.init_menu()
def init_canvas(self):
self.cv = Canvas(root, background='pink')
self.cv.pack(fill=BOTH, expand=True)
def init_menu(self):
pass
root =Tk()
root.title('绘图软件')
root.geometry('700x480')
mycanvas = MyCanvas(root)
root.mainloop()
三、实现菜单显示
from tkinter import *
class MyCanvas:
def __init__(self, master):
self.master = master
self.init_canvas()
self.init_menu()
def init_canvas(self):
self.cv = Canvas(root, background='pink')
self.cv.pack(fill=BOTH, expand=True)
def init_menu(self):
self.menubar = Menu(self.master)
self.master['menu'] = self.menubar
file_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='文件', menu=file_menu)
lang_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='格式', menu=lang_menu)
file_menu.add_command(label="新建", command=self.on_new)
file_menu.add_command(label="保存", command=self.on_save)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.on_exit)
lang_menu.add_command(label="字体", command=None)
lang_menu.add_command(label="颜色", command=None)
pass
def on_exit(self):
self.master.destroy()
def on_new(self):
pass
def on_save(self):
pass
root =Tk()
root.title('绘图软件')
root.geometry('700x480')
mycanvas = MyCanvas(root)
root.mainloop()
运行效果如下:
四、增加两个label控件显示为状态栏,并实现坐标显示和当前图形选择情况
def init_label(self):
self.label1 = Label(self.master,text='当前选择的图形为:',relief='sunken',justify='left',anchor=W)
self.label1.pack(side=LEFT,fill=X,expand=Y)
self.label2 = Label(self.master,text='当前鼠标的位置为:',padx=1,relief='sunken')
self.label2.pack(side=RIGHT)
五、实现鼠标移动操作,并实时更新坐标位置。
def init_bind(self):
self.cv.bind('<Motion>', self.mouse_move)
def mouse_move(self,event):
self.label2.config(text='当前鼠标的位置为:(%s,%s)' % (event.x,event.y))
运行的效果如下:
五、实现画图形的功能(画直线、矩形、椭圆)
1、先初始化相关的属性变量
def reset(self):
#当前选择的图形项
self.temp_item = None
self.first_x = None
self.first_y = None
self.last_x = None
self.last_y = None
self.status = True
self.item_type = 1 #默认画矩形
2、实现左键鼠标按下、移动和松开的具体实现函数
def StartMove(self,event):
if not self.status:
return
self.first_x = event.x
self.first_y = event.y
def StopMove(self,event):
if not self.status:
return
if self.temp_item is not None:
self.cv.delete(self.temp_item)
self.last_x = event.x
self.last_y = event.y
self.drag_handler()
self.reset()
def OnMotion(self,event):
if not self.status:
return
if self.temp_item is not None:
self.cv.delete(self.temp_item)
self.last_x = event.x
self.last_y = event.y
self.drag_handler()
3、绑定鼠标事件
def init_bind(self):
self.cv.bind('<Motion>', self.mouse_move)
self.cv.bind("<ButtonPress-1>", self.StartMove) # 监听左键按下操作响应函数
self.cv.bind("<ButtonRelease-1>", self.StopMove) # 监听左键松开操作响应函数
self.cv.bind("<B1-Motion>", self.OnMotion) # 监听鼠标移动操作响应函数
最终实现效果如下(默认画矩形):
六、重新更新状态栏的显示,以实现准确的显示鼠标和当前选择的图形。抽取出来形成一个函数,以后每次可以直接调用,不用多次重复写。
def txt(self,event):
self.label2.config(text='当前鼠标的位置为:(%s,%s)' % (event.x,event.y))
txt = ''
if self.item_type==1:
txt = '矩形'
if self.item_type==2:
txt = '直线'
if self.item_type==1:
txt = '椭圆'
if self.flag == 1:
txt2 = '新建'
if self.flag == 2:
txt2 = '修改'
local = (self.first_x, self.first_y, self.last_x, self.last_y)
result = '当前的状态为:%s,选择的图形为:%s%s' %(txt2,txt,local)
self.label1.config(text=result)
七、按照实际的菜单项,修改菜单栏。
def init_menu(self):
self.menubar = Menu(self.master)
self.master['menu'] = self.menubar
file_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='文件', menu=file_menu)
file_menu.add_command(label="新建", command=self.on_new)
file_menu.add_command(label="保存", command=self.on_save)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.on_exit)
lang_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='图形', menu=lang_menu)
lang_menu.add_command(label="直线", command=None)
lang_menu.add_command(label="矩形", command=None)
lang_menu.add_command(label="椭圆", command=None)
lang_menu1 = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='选择', menu=lang_menu1)
lang_menu1.add_command(label="移动", command=None)
lang_menu1.add_command(label="删除", command=None)
lang_menu2 = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='图形', menu=lang_menu2)
lang_menu2.add_command(label="边框颜色", command=None)
lang_menu2.add_command(label="填充颜色", command=None)
lang_menu2.add_command(label="边框大小", command=None)
八、实现边框颜色、填充颜色、边框大小设置。
# 处理选择边框颜色的方法
def choose_outline(self):
# 弹出颜色选择对话框
select_color = colorchooser.askcolor(parent=self.master,
title="请选择边框颜色", color=self.outline)
if select_color is not None:
self.outline = select_color[1]
# 处理选择填充颜色的方法
def choose_fill(self):
# 弹出颜色选择对话框
select_color = colorchooser.askcolor(parent=self.master,
title="请选择填充颜色", color=self.fill)
if select_color is not None:
self.fill = select_color[1]
else:
self.fill = None
九、绑定菜单项与具体实现函数之间的对应关系,形成事件响应。
(1)绑定配置颜色的部分函数:
# 处理选择边框颜色的方法
def choose_outline(self):
# 弹出颜色选择对话框
select_color = colorchooser.askcolor(parent=self.master,
title="请选择边框颜色", color=self.outline)
if select_color is not None:
self.outline = select_color[1]
# 处理选择填充颜色的方法
def choose_fill(self):
# 弹出颜色选择对话框
select_color = colorchooser.askcolor(parent=self.master,
title="请选择填充颜色", color=self.fill)
if select_color is not None:
self.fill = select_color[1]
def choose_bg(self):
# 弹出颜色选择对话框
select_color = colorchooser.askcolor(parent=self.master,
title="请选择填充颜色", color=self.bg)
if select_color is not None:
self.bg = select_color[1]
self.cv.configure(background=self.bg)
(2)绑定画图的部分函数:
def choose(self,i):
if i == 1:
self.item_type = 1 # 默认画矩形
self.flag = 1 # 默认为新建图形
if i == 2:
self.item_type = 2 # 画直线
self.flag = 1 # 默认为新建图形
if i == 3:
self.item_type = 3 # 默认画矩形
self.flag = 1 # 默认为新建图形
(3)菜单项与函数关联:
def init_menu(self):
self.menubar = Menu(self.master)
self.master['menu'] = self.menubar
file_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='文件', menu=file_menu)
file_menu.add_command(label="新建", command=self.on_new)
file_menu.add_command(label="保存", command=self.on_save)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.on_exit)
lang_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='图形', menu=lang_menu)
lang_menu.add_command(label="直线", command=lambda i=2: self.choose(i))
lang_menu.add_command(label="矩形", command=lambda i=1: self.choose(i))
lang_menu.add_command(label="椭圆", command=lambda i=3: self.choose(i))
lang_menu1 = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='选择', menu=lang_menu1)
lang_menu1.add_command(label="移动", command=None)
lang_menu1.add_command(label="删除", command=None)
lang_menu2 = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='配置', menu=lang_menu2)
lang_menu2.add_command(label="边框颜色", command=self.choose_outline)
lang_menu2.add_command(label="填充颜色", command=self.choose_fill)
lang_menu2.add_command(label="背景颜色", command=self.choose_bg)
十、实现图形的可移动设置
(1)实现响应函数:
def choose_move(self,i):
if i ==1:
self.move_flag = True
if i==2:
self.delete = True
(2)绑定事件
self.menubar.add_cascade(label='选择', menu=lang_menu1)
lang_menu1.add_command(label="移动", command=lambda i=1: self.choose_move(i))
lang_menu1.add_command(label="删除", command=lambda i=2: self.choose_move(i))
最终实现的效果如下:
好的,这篇博文写完了,其实主要是对canvas的全面使用,目前这个版本还在迭代中,后续将会有更好玩的功能,敬请期待!如果需要源码,后续我整理好后会提供,也可同步关注我的公众号(俊哥随笔)
感谢支持!