How to set view to location of item on canvas in Python Tkinter

Viewed 49

I'd like to create a search/finder tool for a map displayed in a canvas. I'd like the canvas to shift to the item when the user clicks find. How can I shift the view of the canvas to that item, preferably with zooming to fill the screen with that item?

In trying to find a solution I've explored the canvas.xview_moveto which seems like the right function but it looks like the input for that is the

...FRACTION of the total width of the canvas (to be made) off-screen to the left".

I can probably write some janky code to use that but it would be quite inelegant.

Likewise, I've looked at co-opting the scan_mark and scan_dragto functions but I'm also using those for zooming and it's producing odd results.

What I'd like is a function that allows me to input the bounds of a view (xmin, xmax, ymin, ymax) and snap to that. Is there any function that let's me do that?

Reproducible example code. I hard-coded it to always go to the third circle, to skip the item input part to reduce the size of the example code.

items = [
        (25, 25),
        (25, 50),
        (25, 75),
        (25, 100)
    ]

    circle_rad = 10

    def go_to_third():
        # desired code
        # canvas.set_x(items[2][0])
        # canvas.set_y(items[2][1])
        # canvas.set_width(circle_rad * 2, keep_xy_ratio=True)
        pass  # to make this run/compile

    def do_pan(event):
        canvas.scan_mark(event.x, event.y)

    def do_zoom(event):
        x = canvas.canvasx(event.x)
        y = canvas.canvasy(event.y)
        factor = 1.001 ** event.delta
        canvas.scale(tk.ALL, x, y, factor, factor)


    # set up canvas
    root = tk.Tk()
    window = tk.Frame(root)
    window.pack()
    search_button = tk.Button(window, text="go to 3rd item", command=go_to_third)
    search_button.grid(row=0, column=0)
    canvas = tk.Canvas(window, bg='lightgrey')
    canvas.grid(row=1, column=0)

    canvas.bind("<ButtonPress-1>", do_pan)
    canvas.bind("<B1-Motion>", lambda event: canvas.scan_dragto(event.x, event.y, gain=1))
    canvas.bind("<MouseWheel>", do_zoom)

    item_ids = []

    for x, y in items:
        item_ids.append(canvas.create_oval(
            x - circle_rad,
            y - circle_rad,
            x + circle_rad,
            y + circle_rad
        ))

    root.mainloop()

The code above produces the following:

enter image description here

Clicking the "go to 3rd item" button should produce the following:

enter image description here

0 Answers
Related