Jan-09-2025, 05:33 PM
Here is my code, and I want when I scale my grid, it will scale where the cursor is and smoothly move to the middle of the screen. Similar to how AutoCAD does it. Basically I want the cursor to be my zoom focus. For now it scales by making the center of the canvas the zoom focus.
import tkinter as tk import json def create_grid(canvas, grid_size, color="black"): """Draws a grid on the canvas.""" canvas.delete("grid") max_width = max_columns * grid_size max_height = max_rows * grid_size for x in range(0, max_width + 1, grid_size): canvas.create_line(x, 0, x, max_height, fill=color, dash=(1, 3), tags="grid") for y in range(0, max_height + 1, grid_size): canvas.create_line(0, y, max_width, y, fill=color, dash=(1, 3), tags="grid") canvas.config(scrollregion=(0, 0, max_width, max_height)) # Limit scrolling def add_text_to_grid(canvas, row, col, text, font=("Arial", 12)): """Adds a text element aligned to the grid, within limits.""" if 0 <= row < max_rows and 0 <= col < max_columns: x = col * grid_size y = row * grid_size item = canvas.create_text(x, y, text=text, font=font, tags="grid_text", anchor="center") text_items.append({"item": item, "row": row, "col": col}) else: print("Cannot place text outside the grid limits.") def update_text_positions(): """Updates text positions when the grid size changes.""" for text in text_items: row, col = text["row"], text["col"] if 0 <= row < max_rows and 0 <= col < max_columns: x = col * grid_size y = row * grid_size canvas.coords(text["item"], x, y) def on_mouse_wheel(event): """Zooms in or out with the mouse wheel, centered at the cursor.""" global grid_size, offset_x, offset_y # Get cursor position relative to canvas cursor_x = canvas.canvasx(event.x) cursor_y = canvas.canvasy(event.y) # Calculate zoom factor if event.delta > 0: # Scroll up new_grid_size = max(grid_size - 5, 10) # Minimum grid size is 10 else: # Scroll down new_grid_size = min(grid_size + 5, 191) # Maximum grid size is 191 # Zoom adjustment ratio if new_grid_size != grid_size: zoom_ratio = new_grid_size / grid_size # Adjust offsets to keep the cursor centered offset_x = cursor_x - (cursor_x - offset_x) * zoom_ratio offset_y = cursor_y - (cursor_y - offset_y) * zoom_ratio grid_size = new_grid_size create_grid(canvas, grid_size) update_text_positions() # Update canvas view canvas.xview_moveto(offset_x / (max_columns * grid_size)) canvas.yview_moveto(offset_y / (max_rows * grid_size)) def on_mouse_drag_start(event): """Start dragging the canvas.""" global start_x, start_y start_x = event.x start_y = event.y def on_mouse_drag(event): """Handle dragging the canvas.""" global start_x, start_y, offset_x, offset_y dx = start_x - event.x dy = start_y - event.y # Update offsets with boundaries max_width = max_columns * grid_size - canvas.winfo_width() max_height = max_rows * grid_size - canvas.winfo_height() offset_x = max(0, min(offset_x + dx, max_width)) offset_y = max(0, min(offset_y + dy, max_height)) canvas.xview_moveto(offset_x / (max_columns * grid_size)) canvas.yview_moveto(offset_y / (max_rows * grid_size)) # Update starting position for smooth dragging start_x = event.x start_y = event.y def save_settings(): """Saves the grid size to a settings file.""" data = {"grid_size": grid_size} with open("settings.json", "w") as f: json.dump(data, f) def load_settings(): """Loads the grid size from a settings file.""" try: with open("settings.json", "r") as f: data = json.load(f) return data.get("grid_size", 100) # Default grid size is 100 except FileNotFoundError: return 100 # Initialize variables grid_size = load_settings() text_items = [] start_x = start_y = 0 offset_x = offset_y = 0 # Define maximum rows and columns for the grid max_rows = 101 # Maximum number of rows max_columns = 192 # Maximum number of columns # Create the main window and canvas window = tk.Tk() window.title("Zoomable Grid with Cursor-Centered Zoom") # Set the window to start maximized window.state("zoomed") canvas = tk.Canvas(window, bg="white") canvas.pack(fill="both", expand=True) # Create grid and add elements create_grid(canvas, grid_size) add_text_to_grid(canvas, 4, 5, "Center") # Aligned to row 4, column 5 add_text_to_grid(canvas, 2, 1, "Random") # Aligned to row 2, column 1 # Bind events window.protocol("WM_DELETE_WINDOW", lambda: (save_settings(), window.destroy())) canvas.bind("<Control-MouseWheel>", on_mouse_wheel) canvas.bind("<ButtonPress-1>", on_mouse_drag_start) canvas.bind("<B1-Motion>", on_mouse_drag) window.mainloop()