Tkinter 画布基本梳理

文章目录

目录

一、基础框架

python 复制代码
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, colorchooser

root = tk.Tk()
root.title("标题")
root.geometry("900x600")       # 宽x高
root.resizable(True, True)

root.mainloop()

变量类(配合控件双向绑定)

python 复制代码
s = tk.StringVar(value="abc")
i = tk.IntVar(value=10)
d = tk.DoubleVar(value=1.5)
b = tk.BooleanVar(value=True)

s.get()      # 读取
s.set("x")   # 设置

二、常用控件

1. 标签 Label

python 复制代码
tk.Label(root, text="文字", font=("微软雅黑", 12),
         fg="blue", bg="white").pack()

2. 按钮 Button

python 复制代码
def on_click():
    print("点击了")

tk.Button(root, text="按钮", command=on_click,
          width=10, height=2, bg="#ddd", fg="black",
          state=tk.NORMAL).pack()

3. 单行输入框 Entry

python 复制代码
var = tk.StringVar()
entry = tk.Entry(root, textvariable=var, width=20, show=None)
entry.pack()

# var.get() 读取
# var.set("x") 设置
# entry.delete(0, tk.END) 清空

show="*" 可做密码框。

4. 多行文本框 Text

python 复制代码
text = tk.Text(root, width=40, height=8, wrap=tk.WORD)
text.pack()

text.insert(tk.END, "第一行\n")
text.insert("1.0", "插入开头\n")
content = text.get("1.0", tk.END)
text.delete("1.0", tk.END)

5. 复选框 Checkbutton

python 复制代码
v = tk.BooleanVar()
tk.Checkbutton(root, text="启用", variable=v).pack()
print(v.get())   # True / False

6. 单选框 Radiobutton

python 复制代码
v = tk.StringVar(value="A")
tk.Radiobutton(root, text="选项A", variable=v, value="A").pack()
tk.Radiobutton(root, text="选项B", variable=v, value="B").pack()

7. 列表框 Listbox

python 复制代码
lb = tk.Listbox(root, height=6, selectmode=tk.EXTENDED)
lb.pack()
lb.insert(tk.END, "item1", "item2")

sel = lb.curselection()             # 选中索引元组
val = lb.get(sel[0]) if sel else None
lb.delete(0, tk.END)                # 清空
lb.bind("<<ListboxSelect>>", lambda e: print("选中变化"))

8. 下拉框 Combobox(ttk)

python 复制代码
var = tk.StringVar(value="矩形")
cb = ttk.Combobox(root, textvariable=var, state="readonly",
                  values=["矩形", "椭圆", "直线"])
cb.pack()
cb.current(0)   # 选第 0 项
cb.bind("<<ComboboxSelected>>", lambda e: print(var.get()))

9. 滑块 Scale

python 复制代码
v = tk.IntVar(value=5)
tk.Scale(root, from_=1, to=20, orient=tk.HORIZONTAL,
         variable=v, length=200).pack()

10. 微调框 Spinbox

python 复制代码
v = tk.IntVar(value=3)
tk.Spinbox(root, from_=1, to=100, textvariable=v, width=5).pack()

11. 容器 Frame / LabelFrame

python 复制代码
f1 = tk.Frame(root, bd=1, relief=tk.GROOVE)
f1.pack(fill=tk.BOTH, expand=True)

f2 = tk.LabelFrame(root, text="设置")
f2.pack(fill=tk.X)

12. 进度条 Progressbar(ttk)

python 复制代码
pb = ttk.Progressbar(root, maximum=100, mode="determinate")
pb.pack(fill=tk.X)

pb["value"] = 40   # 设置进度
pb.start(50)       # 动画
pb.stop()

三、布局管理

pack:简单堆叠

python 复制代码
w.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5, pady=5)

grid:表格

python 复制代码
w.grid(row=0, column=1, sticky="nsew", padx=5, pady=5)

root.columnconfigure(1, weight=1)
root.rowconfigure(0, weight=1)

place:绝对定位

python 复制代码
w.place(x=20, y=30, width=100, height=30)

⚠️ 同一个父容器里不要混用 pack 和 grid。


四、事件绑定

python 复制代码
widget.bind("<Button-1>", handler)         # 左键
widget.bind("<B1-Motion>", handler)        # 左键拖动
widget.bind("<ButtonRelease-1>", handler)  # 左键释放
widget.bind("<Motion>", handler)           # 鼠标移动
widget.bind("<KeyPress>", handler)         # 键盘按下
widget.bind("<Return>", handler)           # 回车
widget.bind("<Configure>", handler)        # 尺寸变化

after(ms, func) 做定时任务,不要用 time.sleep。


五、对话框

python 复制代码
messagebox.showinfo("提示", "操作成功")
messagebox.showwarning("警告", "注意")
messagebox.showerror("错误", "出错了")
ok = messagebox.askyesno("确认", "确定删除吗?")

path = filedialog.askopenfilename(filetypes=[("文本", "*.txt")])
path = filedialog.asksaveasfilename(defaultextension=".txt")

color = colorchooser.askcolor()[1]   # 返回 '#rrggbb'

六、菜单

顶部菜单栏

python 复制代码
menubar = tk.Menu(root)

file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="新建", command=lambda: print("新建"))
file_menu.add_separator()
file_menu.add_command(label="退出", command=root.quit)
menubar.add_cascade(label="文件", menu=file_menu)

root.config(menu=menubar)

右键菜单

python 复制代码
menu = tk.Menu(root, tearoff=0)
menu.add_command(label="复制")
menu.add_command(label="粘贴")

def popup(e):
    menu.tk_popup(e.x_root, e.y_root)

widget.bind("<Button-3>", popup)

七、Canvas 画布

7.1 创建画布

python 复制代码
canvas = tk.Canvas(
    root,
    width=700, height=500,
    bg="white",
    bd=2, relief=tk.SUNKEN,
    highlightthickness=0,
    scrollregion=(0, 0, 2000, 1500)
)
canvas.pack(fill=tk.BOTH, expand=True)

7.2 坐标系统

  • 左上角为 (0, 0)
  • x 向右增大,y 向下增大
  • 所有绘图方法返回一个 item id,用于后续修改、移动、删除
python 复制代码
rect = canvas.create_rectangle(50, 50, 200, 150)
print(rect)   # 例如 1

7.3 绘制图形

直线
python 复制代码
line = canvas.create_line(
    20, 20, 300, 200,
    fill="red", width=3, dash=(5, 3),
    arrow=tk.LAST, capstyle=tk.ROUND,
    joinstyle=tk.ROUND, smooth=True,
    tags="line"
)
选项 说明
fill 颜色
width 线宽
dash 虚线,如 (5, 3)
arrow tk.FIRST / tk.LAST / tk.BOTH
capstyle 线端样式
joinstyle 拐角样式
smooth 是否平滑曲线
tags 标签
矩形
python 复制代码
rect = canvas.create_rectangle(
    50, 50, 250, 180,
    fill="#aee", outline="blue",
    width=2, dash=(4, 2), tags="rect"
)
椭圆 / 圆
python 复制代码
oval = canvas.create_oval(
    300, 50, 500, 200,
    fill="#fea", outline="orange",
    width=3, tags="oval"
)

# 画圆时宽高相等
canvas.create_oval(100, 100, 200, 200, fill="pink")
圆弧 / 扇形 / 弓形
python 复制代码
arc = canvas.create_arc(
    50, 250, 250, 450,
    start=0, extent=120,
    style=tk.PIESLICE,
    fill="yellow", outline="black", width=2
)
style 值 效果
tk.ARC 只画弧线
tk.PIESLICE 扇形
tk.CHORD 弓形
多边形
python 复制代码
poly = canvas.create_polygon(
    300, 250, 450, 250, 500, 400, 250, 400,
    fill="#cfc", outline="green", width=2,
    smooth=True, tags="poly"
)
文本
python 复制代码
text = canvas.create_text(
    400, 500,
    text="你好,Canvas",
    font=("微软雅黑", 18, "bold"),
    fill="black", anchor="center",
    width=200, justify="left", tags="text"
)

anchor 可选:tk.N / tk.NE / tk.E / tk.SE / tk.S / tk.SW / tk.W / tk.NW / tk.CENTER

图片
python 复制代码
photo = tk.PhotoImage(file="logo.png")
img = canvas.create_image(100, 100, image=photo, anchor="nw")

使用 Pillow:

python 复制代码
from PIL import Image, ImageTk

pil_image = Image.open("photo.jpg").resize((200, 150))
photo = ImageTk.PhotoImage(pil_image)
canvas.create_image(100, 100, image=photo, anchor="nw")

⚠️ photo 必须保存为全局变量或实例属性,否则会被垃圾回收导致图片不显示。

嵌入其他控件
python 复制代码
entry = tk.Entry(root)
canvas.create_window(300, 300, window=entry,
                     anchor="nw", width=200, height=30)

7.4 图形项管理

python 复制代码
rect = canvas.create_rectangle(50, 50, 150, 100,
                               fill="red", tags="myrect")
操作 代码
修改配置 canvas.itemconfig(rect, fill="blue", width=3)
获取配置 canvas.itemcget(rect, "fill")
修改坐标 canvas.coords(rect, 100, 100, 250, 200)
获取坐标 canvas.coords(rect)
移动 canvas.move(rect, 10, 20)
缩放 canvas.scale(rect, 0, 0, 2, 2)
删除单个 canvas.delete(rect)
按 tag 删除 canvas.delete("myrect")
删除全部 canvas.delete("all")
查找全部 canvas.find_all()
按 tag 查找 canvas.find_withtag("myrect")
就近查找 canvas.find_closest(100, 100)
重叠查找 canvas.find_overlapping(0, 0, 200, 200)
包围查找 canvas.find_enclosed(0, 0, 200, 200)
添加 tag canvas.addtag_withtag("newtag", rect)
获取 tag canvas.gettags(rect)
删除 tag canvas.dtag(rect, "myrect")
提升层级 canvas.lift(rect)
降低层级 canvas.lower(rect)
边界框 canvas.bbox(rect)
类型 canvas.type(rect)

7.5 事件绑定

整个 Canvas 绑定:

python 复制代码
def on_click(event):
    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
    print("点击画布坐标:", x, y)

canvas.bind("<Button-1>", on_click)

给某个图形绑定:

python 复制代码
def click_rect(event):
    print("点击了矩形")

canvas.tag_bind("myrect", "<Button-1>", click_rect)

拖动矩形示例:

python 复制代码
rect = canvas.create_rectangle(100, 100, 200, 160,
                               fill="skyblue", tags="drag")

start_x = start_y = 0

def start_drag(event):
    global start_x, start_y
    start_x = canvas.canvasx(event.x)
    start_y = canvas.canvasy(event.y)
    canvas.lift("drag")

def drag(event):
    global start_x, start_y
    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
    canvas.move("drag", x - start_x, y - start_y)
    start_x, start_y = x, y

canvas.tag_bind("drag", "<Button-1>", start_drag)
canvas.tag_bind("drag", "<B1-Motion>", drag)

7.6 滚动条

python 复制代码
frame = tk.Frame(root)
frame.pack(fill=tk.BOTH, expand=True)

canvas = tk.Canvas(frame, bg="white",
                   scrollregion=(0, 0, 2000, 1500))

hbar = tk.Scrollbar(frame, orient=tk.HORIZONTAL, command=canvas.xview)
vbar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)

canvas.configure(xscrollcommand=hbar.set, yscrollcommand=vbar.set)

hbar.pack(side=tk.BOTTOM, fill=tk.X)
vbar.pack(side=tk.RIGHT, fill=tk.Y)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

内容动态增加时自动更新滚动区域:

python 复制代码
canvas.configure(scrollregion=canvas.bbox("all"))

鼠标滚轮:

python 复制代码
def on_mousewheel(event):
    canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

canvas.bind_all("<MouseWheel>", on_mousewheel)

Linux 下:

python 复制代码
canvas.bind_all("<Button-4>", lambda e: canvas.yview_scroll(-1, "units"))
canvas.bind_all("<Button-5>", lambda e: canvas.yview_scroll(1, "units"))

7.7 动画

python 复制代码
ball = canvas.create_oval(20, 100, 70, 150, fill="red")

def animate():
    canvas.move(ball, 3, 0)
    x1, y1, x2, y2 = canvas.coords(ball)
    if x2 > 700:
        canvas.coords(ball, 20, 100, 70, 150)
    root.after(16, animate)   # 约 60 FPS

animate()

八、完整综合示例

一个绘图板程序,包含:菜单栏、工具栏(下拉框、输入框、按钮、滑块、复选框、颜色选择)、左侧列表、右侧 Canvas、底部状态栏。

python 复制代码
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, colorchooser


class DrawingApp:
    def __init__(self, root):
        self.root = root
        root.title("Tkinter 综合示例 - 绘图板")
        root.geometry("1100x700")
        root.minsize(800, 500)

        # ---------- 状态数据 ----------
        self.fill_color = "#88ccff"
        self.outline_color = "#0055aa"
        self.items = {}          # canvas item id -> 描述
        self.list_order = []     # listbox 索引 -> item id
        self.start = None        # 拖动起点
        self.temp = None         # 正在绘制的临时图形

        # ---------- 界面 ----------
        self._build_menu()
        self._build_toolbar()
        self._build_body()
        self._build_status()

        self.canvas.bind_all("<MouseWheel>", self.on_wheel)

    # ================= 菜单 =================
    def _build_menu(self):
        menubar = tk.Menu(self.root)

        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="导出为 PostScript",
                              command=self.export_ps)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)
        menubar.add_cascade(label="文件", menu=file_menu)

        edit_menu = tk.Menu(menubar, tearoff=0)
        edit_menu.add_command(label="清空画布", command=self.clear_all)
        edit_menu.add_command(label="删除选中",
                              command=self.delete_selected)
        menubar.add_cascade(label="编辑", menu=edit_menu)

        help_menu = tk.Menu(menubar, tearoff=0)
        help_menu.add_command(
            label="关于",
            command=lambda: messagebox.showinfo("关于", "Tkinter 综合示例")
        )
        menubar.add_cascade(label="帮助", menu=help_menu)

        self.root.config(menu=menubar)

    # ================= 顶部工具栏 =================
    def _build_toolbar(self):
        bar = tk.Frame(self.root, bd=1, relief=tk.RAISED)
        bar.pack(side=tk.TOP, fill=tk.X)

        # 图形类型
        tk.Label(bar, text="图形:").pack(side=tk.LEFT, padx=(8, 2), pady=6)
        self.shape_var = tk.StringVar(value="矩形")
        ttk.Combobox(bar, textvariable=self.shape_var, state="readonly",
                     values=["矩形", "椭圆", "直线", "文本"], width=8
                     ).pack(side=tk.LEFT)

        # 填充色
        tk.Button(bar, text="填充色", command=self.choose_fill
                  ).pack(side=tk.LEFT, padx=(10, 4))
        self.fill_preview = tk.Label(bar, bg=self.fill_color, width=3,
                                     relief=tk.SUNKEN)
        self.fill_preview.pack(side=tk.LEFT)

        # 边框色
        tk.Button(bar, text="边框色", command=self.choose_outline
                  ).pack(side=tk.LEFT, padx=(10, 4))
        self.outline_preview = tk.Label(bar, bg=self.outline_color, width=3,
                                        relief=tk.SUNKEN)
        self.outline_preview.pack(side=tk.LEFT)

        # 线宽
        tk.Label(bar, text="线宽:").pack(side=tk.LEFT, padx=(12, 2))
        self.width_var = tk.IntVar(value=2)
        tk.Scale(bar, from_=1, to=15, orient=tk.HORIZONTAL,
                 variable=self.width_var, length=110).pack(side=tk.LEFT)

        # 是否填充
        self.fill_on = tk.BooleanVar(value=True)
        tk.Checkbutton(bar, text="填充", variable=self.fill_on
                       ).pack(side=tk.LEFT, padx=8)

        # 文本内容
        tk.Label(bar, text="文本:").pack(side=tk.LEFT, padx=(8, 2))
        self.text_var = tk.StringVar(value="Hello")
        tk.Entry(bar, textvariable=self.text_var, width=12).pack(side=tk.LEFT)

        # 清空按钮
        tk.Button(bar, text="清空", command=self.clear_all
                  ).pack(side=tk.LEFT, padx=(12, 4))

    # ================= 主体区域 =================
    def _build_body(self):
        body = tk.Frame(self.root)
        body.pack(fill=tk.BOTH, expand=True)

        # ---------- 左侧面板 ----------
        left = tk.Frame(body, width=210, bd=1, relief=tk.GROOVE)
        left.pack(side=tk.LEFT, fill=tk.Y)
        left.pack_propagate(False)

        tk.Label(left, text="图形列表", font=("微软雅黑", 11, "bold")
                 ).pack(pady=(10, 4))

        lb_frame = tk.Frame(left)
        lb_frame.pack(fill=tk.BOTH, expand=True, padx=8)

        sb = tk.Scrollbar(lb_frame, orient=tk.VERTICAL)
        self.listbox = tk.Listbox(lb_frame, yscrollcommand=sb.set)
        sb.config(command=self.listbox.yview)
        sb.pack(side=tk.RIGHT, fill=tk.Y)
        self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.listbox.bind("<<ListboxSelect>>", self.on_select)

        tk.Button(left, text="删除选中", command=self.delete_selected
                  ).pack(fill=tk.X, padx=8, pady=(8, 4))
        tk.Button(left, text="清空画布", command=self.clear_all
                  ).pack(fill=tk.X, padx=8, pady=(0, 10))

        # ---------- 右侧画布 ----------
        right = tk.Frame(body)
        right.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        self.canvas = tk.Canvas(right, bg="white", highlightthickness=0,
                                scrollregion=(0, 0, 2400, 1800))
        hbar = tk.Scrollbar(right, orient=tk.HORIZONTAL,
                            command=self.canvas.xview)
        vbar = tk.Scrollbar(right, orient=tk.VERTICAL,
                            command=self.canvas.yview)
        self.canvas.configure(xscrollcommand=hbar.set,
                              yscrollcommand=vbar.set)

        hbar.pack(side=tk.BOTTOM, fill=tk.X)
        vbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # 画布事件
        self.canvas.bind("<Button-1>", self.on_press)
        self.canvas.bind("<B1-Motion>", self.on_drag)
        self.canvas.bind("<ButtonRelease-1>", self.on_release)
        self.canvas.bind("<Motion>", self.on_motion)

    # ================= 状态栏 =================
    def _build_status(self):
        self.status = tk.StringVar(value="就绪:选择图形后在画布上拖动绘制")
        tk.Label(self.root, textvariable=self.status, anchor=tk.W,
                 bd=1, relief=tk.SUNKEN, padx=6
                 ).pack(side=tk.BOTTOM, fill=tk.X)

    # ================= 颜色选择 =================
    def choose_fill(self):
        color = colorchooser.askcolor(color=self.fill_color)[1]
        if color:
            self.fill_color = color
            self.fill_preview.config(bg=color)

    def choose_outline(self):
        color = colorchooser.askcolor(color=self.outline_color)[1]
        if color:
            self.outline_color = color
            self.outline_preview.config(bg=color)

    # ================= 画布事件 =================
    def on_press(self, event):
        x = self.canvas.canvasx(event.x)
        y = self.canvas.canvasy(event.y)
        self.start = (x, y)

        shape = self.shape_var.get()
        w = self.width_var.get()
        fill = self.fill_color if self.fill_on.get() else ""

        # 文本:单击直接创建
        if shape == "文本":
            item = self.canvas.create_text(
                x, y, text=self.text_var.get(),
                fill=self.outline_color, font=("微软雅黑", 16),
                anchor="nw"
            )
            self.register(item, f"文本: {self.text_var.get()}")
            self.start = None
            return

        # 其他:拖动创建
        if shape == "直线":
            self.temp = self.canvas.create_line(
                x, y, x, y, fill=self.outline_color, width=w,
                capstyle=tk.ROUND
            )
        elif shape == "矩形":
            self.temp = self.canvas.create_rectangle(
                x, y, x, y, fill=fill, outline=self.outline_color, width=w
            )
        elif shape == "椭圆":
            self.temp = self.canvas.create_oval(
                x, y, x, y, fill=fill, outline=self.outline_color, width=w
            )

    def on_drag(self, event):
        if self.temp is None or self.start is None:
            return
        x = self.canvas.canvasx(event.x)
        y = self.canvas.canvasy(event.y)
        x0, y0 = self.start
        self.canvas.coords(self.temp, x0, y0, x, y)

    def on_release(self, event):
        if self.temp is not None:
            self.register(self.temp, self.shape_var.get())
            self.temp = None
        self.start = None

    def on_motion(self, event):
        x = self.canvas.canvasx(event.x)
        y = self.canvas.canvasy(event.y)
        self.status.set(f"坐标: x={int(x)}, y={int(y)}")

    def on_wheel(self, event):
        self.canvas.yview_scroll(int(-event.delta / 120), "units")

    # ================= 列表管理 =================
    def register(self, item, kind):
        """把新图形登记到列表"""
        self.items[item] = kind
        self.refresh_list()

    def refresh_list(self):
        self.listbox.delete(0, tk.END)
        self.list_order = []
        for item in self.canvas.find_all():
            if item in self.items:
                self.list_order.append(item)
                self.listbox.insert(tk.END, f"[{item}] {self.items[item]}")

    def on_select(self, event):
        sel = self.listbox.curselection()
        if not sel:
            return
        item = self.list_order[sel[0]]
        self.status.set(f"选中: {self.items.get(item, '')} (id={item})")

    def delete_selected(self):
        sel = self.listbox.curselection()
        if not sel:
            messagebox.showinfo("提示", "请先在左侧列表中选择要删除的图形")
            return
        for i in reversed(sel):
            item = self.list_order[i]
            self.canvas.delete(item)
            self.items.pop(item, None)
        self.refresh_list()
        self.status.set("已删除选中图形")

    def clear_all(self):
        self.canvas.delete("all")
        self.items.clear()
        self.refresh_list()
        self.status.set("画布已清空")

    # ================= 导出 =================
    def export_ps(self):
        path = filedialog.asksaveasfilename(
            defaultextension=".ps",
            filetypes=[("PostScript 文件", "*.ps")]
        )
        if not path:
            return
        try:
            self.canvas.postscript(file=path, colormode="color")
            messagebox.showinfo("完成", f"已导出到:\n{path}")
        except Exception as e:
            messagebox.showerror("导出失败", str(e))


if __name__ == "__main__":
    root = tk.Tk()
    app = DrawingApp(root)
    root.mainloop()

使用说明

操作 效果
顶部下拉框选图形 矩形 / 椭圆 / 直线 / 文本
点"填充色 / 边框色" 打开颜色选择器
拖动"线宽"滑块 调节线条宽度
勾选"填充" 矩形/椭圆是否填充颜色
"文本"框输入内容 选择"文本"图形后单击画布即可放置
在画布上按住拖动 绘制当前图形
鼠标滚轮 垂直滚动画布
左侧列表 显示所有图形,点击查看信息
"删除选中" / "清空画布" 删除图形
菜单"文件 → 导出" 导出为 PostScript
底部状态栏 实时显示鼠标坐标与操作提示

九、常见坑

  1. 图片不显示

    PhotoImage 或 ImageTk.PhotoImage 必须保持引用,否则会被垃圾回收。

  2. 滚动后坐标不对

    事件中的 event.x、event.y 是窗口坐标,滚动后应使用:

    python 复制代码
    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
  3. 删除图形后 id 失效

    删除后不要再使用旧 id。

  4. 动画不要用 time.sleep

    使用 root.after()。

  5. Canvas 不是位图画布

    它是矢量图形项集合,适合画线、图形、文本、嵌入控件;如果要像素级绘图,用 Pillow + create_image。

  6. scrollregion 要设置正确

    否则滚动条范围不对。可动态设置:

    python 复制代码
    canvas.configure(scrollregion=canvas.bbox("all"))
  7. pack 和 grid 不要混用

    同一个父容器里不要同时使用两种布局管理器。

  8. ttk.Combobox 用 state="readonly"

    防止用户随意输入。


十、总结

Tkinter 的核心使用思路:

text 复制代码
创建根窗口
→ 创建控件(Label / Button / Entry / Canvas ...)
→ 用 pack / grid / place 布局
→ 用 command / bind 绑定事件
→ 用 after 做动画
→ 用变量类(StringVar / IntVar ...)双向绑定
→ 用 messagebox / filedialog / colorchooser 做交互

Canvas 核心流程:

text 复制代码
创建 Canvas
→ create_xxx 创建图形项,得到 item id
→ itemconfig / coords / move / delete 管理
→ bind / tag_bind 处理事件
→ after 做动画
→ Scrollbar + scrollregion 做滚动
→ create_image / create_window 显示图片或嵌入控件
相关推荐
Shulex2 小时前
面向跨境电商多渠道消息系统的技术架构:亚马逊站内信合规对接与自动化执行链路设计
运维·架构·自动化
Wx-bishekaifayuan2 小时前
django个性化旅游路线推荐平台49005-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
bloglin999992 小时前
vmware部署centos7启动docker容器后无法访问服务
运维·docker·容器
小白快快跑哦3 小时前
python-字符串全解(六):正则表达式-量词
python·正则表达式·字符串
禹凕3 小时前
机器学习之Selenium(Machina Learning about Selenium)
爬虫·python·selenium·测试工具·机器学习
杨云龙UP3 小时前
TDengine 3.4.2.8 Community 三节点三副本生产集群部署实战(DNode/MNode/taosAdapter/Explorer)
大数据·linux·运维·数据库·tdengine·时序库
梦帮科技3 小时前
领域认知知识库图谱注入:从双式记账图网络到高质量问答对自动化合成流水线
运维·网络·数据库·人工智能·矩阵·架构·自动化
逐流人4 小时前
Ceph分布式存储集群配置与池管理:从配置优先级到PG、复本池与纠删码池
运维·分布式·ceph·云原生·云计算·rados
海宇服务4 小时前
零信任架构实战:基于海宇车型识别精准构建自动化定损网关
运维·人工智能·架构·自动化