tkinter快键画布
这个画板仅仅是为了服务于个人设计的截图功能贴图功能中的画板,方便我直接进行继承拓展。
截图自制工具详见我的主页。个人认为设计可谓相当简洁优雅。
下面的快捷画布的使用方法:
①按住Ctrl键,调出画笔;松开即结束绘图;
②双击Ctrl键,调出调色板;
③按照Ctrl键并滚动鼠标中键,调整画笔粗细;
import time
import tkinter as tk
from tkinter import colorchooser
class CustomCanvas(tk.Canvas):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self._bind_events()
self.focus_set()
self.__preview_items: list[str] = list()
self.__stroke_history: list[str] = list()
self.__current_stroke: list[str] = list()
self.pen_color: str = 'black'
self.drawing_mode: bool = False
self.last_ctrl_press: float = 0.0
self.pen_size: int = 3
self.last_x: int = None
self.last_y: int = None
def _bind_events(self):
# 控制键监听
self.bind('<Control_L>', self.__start_drawing)
self.bind('<Control-KeyRelease>', self.__stop_drawing)
# 鼠标监听
self.bind('<B1-Motion>', self.__draw_continuous)
self.bind('<Motion>', self.__update_preview)
self.bind('<Button-1>', self.__draw_single_point)
self.bind("<ButtonRelease-1>", self.__finalize_stroke)
# 功能快捷键
self.bind('<Control-MouseWheel>', self.__adjust_pen_size)
self.bind("<Control-z>", self.undo_last_stroke)
def __start_drawing(self, event):
if self._is_double_ctrl_click():
self.__change_pen_color(event)
self.drawing_mode = True
self.config(cursor='pencil')
def __stop_drawing(self, event):
self.drawing_mode = False
self.config(cursor='')
def __finalize_stroke(self, event):
self.last_x = self.last_y = None
self.__stroke_history.append(self.__current_stroke)
self.__current_stroke = []
def __change_pen_color(self, event):
color = colorchooser.askcolor()[1]
if color:
self.pen_color = color
def __adjust_pen_size(self, event):
if event.delta > 0:
self.pen_size = min(124, self.pen_size + 1)
else:
self.pen_size = max(1, self.pen_size - 1)
self.__show_brush_preview(event)
def __show_brush_preview(self, event):
self.__update_preview(event, force=True)
preview = self.__draw_single_point(event, record=False)
self.__preview_items.append(preview)
def __update_preview(self, event, force: bool = False):
if force:
self.__clear_preview()
current_x, current_y = event.x, event.y
if self.last_x is not None and self.last_y is not None:
dx = abs(current_x - self.last_x)
dy = abs(current_y - self.last_y)
if dx < 10 and dy < 10:
return
self.last_x, self.last_y = current_x, current_y
if len(self.__preview_items) != 0:
self.__clear_preview()
def __clear_preview(self):
for item in self.__preview_items:
self.delete(item)
self.__preview_items.clear()
def _is_double_ctrl_click(self):
current_time = time.time()
is_double_click = (current_time - self.last_ctrl_press) < 0.3
self.last_ctrl_press = current_time
return is_double_click
def __draw_continuous(self, event, record: bool = True) -> str:
line = ""
if not self.drawing_mode:
return line
if self.last_x and self.last_y:
line = self.create_line(
self.last_x, self.last_y, event.x, event.y,
fill=self.pen_color, width=self.pen_size,
capstyle=tk.ROUND, joinstyle=tk.ROUND
)
if record:
self.__current_stroke.append(line)
self.last_x = event.x
self.last_y = event.y
return line
def __draw_single_point(self, event, record: bool = True) -> str:
point = ""
if not self.drawing_mode:
return point
point = self.create_oval(
event.x - self.pen_size // 2, event.y - self.pen_size // 2,
event.x + self.pen_size // 2, event.y + self.pen_size // 2,
fill=self.pen_color, outline=self.pen_color,
)
self.last_x = event.x
self.last_y = event.y
if record:
self.__current_stroke.append(point)
return point
def undo_last_stroke(self, event):
if len(self.__stroke_history) == 0:
return
last_draw = self.__stroke_history.pop()
for item in last_draw:
self.delete(item)
if __name__ == '__main__':
root = tk.Tk()
canvas = CustomCanvas(root, bg='white', width=800, height=600)
canvas.pack()
root.mainloop()