You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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轴向上为正
  • 下方文本框实时记录所有绘制线段的坐标信息

关键逻辑说明

  1. 网格绘制:通过循环生成等间距的水平线和垂直线,同时标注对应坐标值
  2. 鼠标事件处理:
    • <Button-1>:记录鼠标点击的起点,创建蓝色临时预览线
    • <ButtonRelease-1>:替换为红色实线线段,记录并显示坐标
  3. 坐标系转换:笛卡尔坐标系通过将画布中心作为原点,对鼠标坐标进行加减转换实现

弧线扩展建议

如果需要绘制弧线,可以添加以下修改:

  • 新增「线段/弧线」模式切换按钮
  • 弧线绘制需要记录起点、控制点和终点,使用canvas.create_arc()方法实现,同时记录三个点的坐标

内容的提问来源于stack exchange,提问作者P_Python

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:01:22