【综合案例】python tkinter使用canvas开发综合画图软件,附上完整的思路和项目源码

正在尝试着做一个比较完善的画图软件,计划使用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的全面使用,目前这个版本还在迭代中,后续将会有更好玩的功能,敬请期待!如果需要源码,后续我整理好后会提供,也可同步关注我的公众号(俊哥随笔)

感谢支持!

发布了15 篇原创文章 · 获赞 16 · 访问量 442

猜你喜欢

转载自blog.csdn.net/dhjabc_1/article/details/105452557