Racers 发表于 2022-4-21 08:19

一段熟悉tkinter.Canvas控件的代码实现一个画图功能小软件

from tkinter import *
from tkinter.colorchooser import *

# 窗口的全局变量 宽度与高度
win_width = 660
win_height = 380


class Application(Frame):
    """实现一个画图的小软件"""

    def __init__(self, master=None, bg_color='gold', fg_color='DarkOrange'):# Gold 金 色   DarkOrange 深橙色
      super().__init__(master)
      self.master = master
      self.bg_color = bg_color      # 定义一个背景颜色的对象属性
      self.fg_color = fg_color      # 定义一个前景色
      self.x = self.y = 0         # 起始位置的 x 与 y 初始值
      self.last_draw = 0            # 表示最后绘制的图形的ID
      self.start_draw_flag = False# 标记最开始的画的位置变量
      self.pack()

      # 下面定义按钮的一些参数      
      self.bin_text = {'start': '开始', 'pen': '画笔', 'rect': '画矩形', 'clear': '清屏', 'eraser': '橡皮擦',
                         'line': '直线', 'line_arrow': '直线(箭头)', 'color': '画笔颜色', 'bg_color': '背景颜色', 'quit': '退出'}

      # 下面开始实现控件 设置一个宽度随窗口,高度为root窗口0.9倍的大小的数值,背景色设置为默认传参的 粉色
      self.draw_pad = Canvas(self, width=win_width, height=win_height*0.9, bg=self.bg_color)
      self.draw_pad.pack()

      # 采用循环的方式创建按钮
      for k, v in self.bin_text.items():
            # index = self.bin_text.index(i)
            self.btn = Button(self, text=v, name=k)
            self.btn.pack(side='left', padx=10)

      # 按钮类事件处理
      self.btn.bind_class('Button', '<Button-1>', self.event_massage)
      # 鼠标释放,为了解决 self.start_draw_flag为True后起点一直是一个点的问题
      self.draw_pad.bind('<ButtonRelease-1>', self.stop_draw)
      # 颜色快捷键的设置
      root.bind('<KeyPress-r>', self.kjj)   # 设置红色的快捷键
      root.bind('<KeyPress-g>', self.kjj)   # 设置绿的快捷键
      root.bind('<KeyPress-y>', self.kjj)   # 设置黄色的快捷键

    # 事件管理
    def event_massage(self, event):
      name = event.widget.winfo_name()
      # print(name)
      if name == 'line':
            self.draw_pad.bind('<B1-Motion>', self.my_line)# B1-Motion 是按住鼠标拖动的事件

      elif name == 'line_arrow':
            self.draw_pad.bind('<B1-Motion>', self.my_line_arrow)# B1-Motion 是按住鼠标拖动的事件

      elif name == 'rect':
            self.draw_pad.bind('<B1-Motion>', self.my_rect)# B1-Motion 是按住鼠标拖动的事件

      elif name == 'pen':
            self.draw_pad.bind('<B1-Motion>', self.my_pen)# B1-Motion 是按住鼠标拖动的事件

      elif name == 'eraser':
            self.draw_pad.bind('<B1-Motion>', self.my_eraser)# B1-Motion 是按住鼠标拖动的事件

      elif name == 'color':
            cor = askcolor(title='选择画笔颜色', color=self.fg_color)
            self.fg_color = cor

      elif name == 'bg_color':
            cor = askcolor(title='选择背景颜色', color=self.bg_color)
            self.bg_color = cor
            self.draw_pad['bg'] = self.bg_color
            event.widget.state = 'Disabled'

      elif name == 'clear':
            self.draw_pad.delete('all')# 直接清除全部就 ok 了

      elif name == 'quit':# 设置程序的退出机制
            root.destroy()

    # 让self.start_draw_flag为初始
    def stop_draw(self, event):
      self.start_draw_flag = False
      self.last_draw = event.x * 0      # 这样解决画新线删除旧线的问题,改变一下 Self.last_draw的值找不到原来的值my_line函数里也就删除不了了

    # 重复代码精简封装一下 一个新的函数方法
    def start_draw(self, event):
      # 每次调用这个函数方法之前都删除掉前面的线,
      # print('要删除的是什么东西?:', self.last_draw)
      self.draw_pad.delete(self.last_draw)

      # 判断起始位置是不是False
      if not self.start_draw_flag:# 如果是False说明第一次画
            self.start_draw_flag = True
            self.x = event.x# 一开始的 x
            self.y = event.y# 一载始的 y

    # 画直线的函数方法
    def my_line(self, event):      
      # 直接调用 start_draw方法
      self.start_draw(event)
      # 开始画一条直线
      self.last_draw = self.draw_pad.create_line(self.x, self.y, event.x, event.y, fill=self.fg_color)

    # 画带箭头的直线的函数方法
    def my_line_arrow(self, event):
      # 直接调用 start_draw方法
      self.start_draw(event)
      # 开始画一条带箭头的直线区别是只加一个参数就可以了就是加一个 arrow=LAST
      self.last_draw = self.draw_pad.create_line(self.x, self.y, event.x, event.y, arrow=LAST,fill=self.fg_color)

    # 画矩形的函数方法
    def my_rect(self, event):
      # 直接调用 start_draw方法
      self.start_draw(event)

      # # 开始画一个矩形,下面代码是全部填充的样式
      # self.last_draw = self.draw_pad.create_rectangle(self.x, self.y, event.x, event.y, fill=self.fg_color)

      # 开始画一个矩形,下面代码是不进行填充的样式outline 表示的是边框颜色
      self.last_draw = self.draw_pad.create_rectangle(self.x, self.y, event.x, event.y, outline=self.fg_color)

    # 利用画直线 的方法进行画笔功能的实现,就是一小段一小段的直线连起来,但是要去掉self.last_draw,这样就留住了,不删除了
    def my_pen(self, event):
      # 直接调用 start_draw方法
      self.start_draw(event)
      # 开始画一条带箭头的直线区别是只加一个参数就可以了就是加一个 arrow=LAST
      self.draw_pad.create_line(self.x, self.y, event.x, event.y, fill=self.fg_color)
      # 重新给self.x 与 self.y进行赋值,这样起点就会随鼠标移动一直变化 了
      self.x = event.x
      self.y = event.y

    # 橡皮擦
    def my_eraser(self, event):
      # 就是画一个背景色的矩形,下面代码是全部填充的样式
      self.last_draw = self.draw_pad.create_rectangle(event.x-4, event.y-4, event.x+4, event.y+4,
                                                      outline=self.bg_color, fill=self.bg_color)

    # 快捷键函数方法
    def kjj(self, event):
      if event.char == 'r':
            self.fg_color = '#ff0000'
      if event.char == 'g':
            self.fg_color = '#00ff00'
      if event.char == 'y':
            self.fg_color = 'yellow'# #ffff00


if __name__ == '__main__':
    root = Tk()
    root.geometry(f'{win_width}x{win_height}+500+180')
    root.title('画图小工具---> by: luxingyu329')
    # root['bg'] = 'yellow'
    app = Application(master=root)# 实例化对象
    root.mainloop()

wsgdbd 发表于 2022-4-21 08:19


感谢楼主的分享

coco9452 发表于 2022-4-21 08:32

6666

qsjzhdsq 发表于 2022-4-21 08:33

谢谢@Thanks!

linghao 发表于 2022-4-21 08:34

先收藏了,万一用到呢

engyuyAn 发表于 2022-4-21 08:34

谢谢大牛

林相 发表于 2022-4-21 08:50

感谢楼主分享!大牛有你更精彩!
页: [1]
查看完整版本: 一段熟悉tkinter.Canvas控件的代码实现一个画图功能小软件