当前位置: 首页 > news >正文

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()

相关文章:

  • CTF WEB题
  • Linux RT调度器之负载均衡
  • 火语言RPA--列表项内容获取
  • (一)微服务初见之 Spring Cloud 介绍
  • 自己动手打造AI Agent:基于DeepSeek-R1+websearch从零构建自己的Manus深度探索智能体AI-Research
  • 【C语言系列】C语言内存函数
  • Codeforces Round 1009 (Div. 3)-G
  • HTML 标题
  • Ubuntu 24.04-JAVA-JDBC-mysql
  • Influxdb cli删除数据步骤
  • 【c++】【智能指针】shared_ptr底层实现
  • python_巨潮年报pdf下载
  • 判断是不是二叉搜索树(C++)
  • java静态变量,静态方法存储在内存中哪个位置
  • TCP怎么保证可靠传输
  • redis常用命令
  • Sublime Text 2.0.2 安装与汉化指南:从下载到中文包配置的完整教程
  • 【强化学习】第二讲——探索与利用exploration vs. exploitation
  • [WEB开发] Web基础
  • zero-shot文字分类模型
  • 五一去哪儿| 追着花期去旅行,“赏花经济”绽放文旅新活力
  • 百年传承,再启新程,参天中国迎来2.0时代
  • 徐徕任上海浦东新区副区长
  • 78家公募年度业绩比拼:23家营收净利双升,十强座次微调
  • 万科:一季度营收近380亿元,销售回款率超100%
  • 中行一季度净赚超543亿降2.9%,利息净收入降逾4%