如何用Python创建可在笛卡尔坐标系/网格上鼠标绘图并获取坐标的GUI
基于Tkinter的网格绘图与坐标获取工具
下面是一个完整的可运行实现,支持在网格画布上用鼠标绘制线段,同时自动记录每条线段的起止坐标:
import tkinter as tk from tkinter import ttk class GridDrawer: def __init__(self, root): self.root = root self.root.title("网格绘图工具") # 画布基础参数 self.canvas_width = 600 self.canvas_height = 600 self.grid_size = 50 # 网格间距 # 绘图状态变量 self.start_point = None self.current_preview = None self.coord_records = [] # 创建画布组件 self.canvas = tk.Canvas(root, width=self.canvas_width, height=self.canvas_height, bg="white") self.canvas.pack(padx=10, pady=10) # 创建坐标记录显示区 self.record_frame = ttk.Frame(root) self.record_frame.pack(padx=10, pady=5, fill=tk.X) self.record_label = ttk.Label(self.record_frame, text="已绘制线段坐标:") self.record_label.pack(anchor=tk.W) self.record_text = tk.Text(self.record_frame, height=8, wrap=tk.WORD) self.record_text.pack(fill=tk.X, pady=2) # 绑定鼠标事件 self.canvas.bind("<Button-1>", self.on_mouse_down) self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up) # 初始化绘制网格 self.draw_grid() def draw_grid(self): # 绘制垂直网格线+坐标标注 for x in range(0, self.canvas_width, self.grid_size): self.canvas.create_line(x, 0, x, self.canvas_height, fill="#e0e0e0") self.canvas.create_text(x+5, 5, text=str(x), font=("Arial", 8)) # 绘制水平网格线+坐标标注 for y in range(0, self.canvas_height, self.grid_size): self.canvas.create_line(0, y, self.canvas_width, y, fill="#e0e0e0") self.canvas.create_text(5, y+5, text=str(y), font=("Arial", 8)) def on_mouse_down(self, event): # 记录线段起点,创建临时预览线 self.start_point = (event.x, event.y) self.current_preview = self.canvas.create_line(event.x, event.y, event.x, event.y, fill="blue", width=2) def on_mouse_up(self, event): if not self.start_point: return # 完成线段绘制,删除临时预览线 end_point = (event.x, event.y) self.canvas.delete(self.current_preview) self.canvas.create_line(self.start_point[0], self.start_point[1], end_point[0], end_point[1], fill="red", width=2) # 记录坐标并更新显示 record_str = f"线段:起点({self.start_point[0]}, {self.start_point[1]}) → 终点({end_point[0]}, {end_point[1]})" self.coord_records.append(record_str) self.record_text.insert(tk.END, record_str + "\n") self.record_text.see(tk.END) self.start_point = None def switch_to_cartesian(self): # 切换为中心原点的笛卡尔坐标系(y轴向上为正) self.canvas.delete("all") center_x = self.canvas_width // 2 center_y = self.canvas_height // 2 # 绘制坐标轴 self.canvas.create_line(center_x, 0, center_x, self.canvas_height, fill="black", width=2) self.canvas.create_line(0, center_y, self.canvas_width, center_y, fill="black", width=2) # 绘制笛卡尔网格与坐标标注 for x in range(center_x % self.grid_size, self.canvas_width, self.grid_size): self.canvas.create_line(x, 0, x, self.canvas_height, fill="#e0e0e0") cart_x = x - center_x self.canvas.create_text(x+5, center_y+5, text=str(cart_x), font=("Arial", 8)) for y in range(center_y % self.grid_size, self.canvas_height, self.grid_size): self.canvas.create_line(0, y, self.canvas_width, y, fill="#e0e0e0") cart_y = center_y - y self.canvas.create_text(center_x+5, y+5, text=str(cart_y), font=("Arial", 8)) # 重新绑定笛卡尔坐标系下的鼠标事件 self.canvas.bind("<Button-1>", self.on_cartesian_down) self.canvas.bind("<ButtonRelease-1>", self.on_cartesian_up) def on_cartesian_down(self, event): center_x = self.canvas_width // 2 center_y = self.canvas_height // 2 cart_x = event.x - center_x cart_y = center_y - event.y self.start_point = (cart_x, cart_y) self.current_preview = self.canvas.create_line(event.x, event.y, event.x, event.y, fill="blue", width=2) def on_cartesian_up(self, event): if not self.start_point: return center_x = self.canvas_width // 2 center_y = self.canvas_height // 2 end_cart_x = event.x - center_x end_cart_y = center_y - event.y end_point = (end_cart_x, end_cart_y) self.canvas.delete(self.current_preview) self.canvas.create_line( center_x + self.start_point[0], center_y - self.start_point[1], center_x + end_point[0], center_y - end_point[1], fill="red", width=2 ) record_str = f"线段:起点({self.start_point[0]}, {self.start_point[1]}) → 终点({end_point[0]}, {end_point[1]})" self.coord_records.append(record_str) self.record_text.insert(tk.END, record_str + "\n") self.record_text.see(tk.END) self.start_point = None if __name__ == "__main__": root = tk.Tk() app = GridDrawer(root) # 添加坐标系切换按钮 switch_btn = ttk.Button(root, text="切换到笛卡尔坐标系", command=app.switch_to_cartesian) switch_btn.pack(pady=5) root.mainloop()
功能说明
- 默认使用Tkinter原生坐标系(左上角为原点(0,0)),点击拖动鼠标绘制线段,松开后自动记录坐标
- 点击「切换到笛卡尔坐标系」按钮可切换为中心原点的坐标系,y轴向上为正
- 下方文本框实时记录所有绘制线段的坐标信息
关键逻辑说明
- 网格绘制:通过循环生成等间距的水平线和垂直线,同时标注对应坐标值
- 鼠标事件处理:
<Button-1>:记录鼠标点击的起点,创建蓝色临时预览线<ButtonRelease-1>:替换为红色实线线段,记录并显示坐标
- 坐标系转换:笛卡尔坐标系通过将画布中心作为原点,对鼠标坐标进行加减转换实现
弧线扩展建议
如果需要绘制弧线,可以添加以下修改:
- 新增「线段/弧线」模式切换按钮
- 弧线绘制需要记录起点、控制点和终点,使用
canvas.create_arc()方法实现,同时记录三个点的坐标
内容的提问来源于stack exchange,提问作者P_Python
相关产品推荐
相关产品推荐

