Home > Software engineering >  Python TkInter: How can I get the canvas coordinates of the visible area of a scrollable canvas?
Python TkInter: How can I get the canvas coordinates of the visible area of a scrollable canvas?

Time:12-04

I need to find out what the visible coordinates of a vertically scrollable canvas are using python and tkinter.

Let's assume I have a canvas that is 800 x 5000 pixels, and the visible, vertically scrollable window is 800x800. If I am scrolled all the way to the top of the canvas, I would like to have a function that, when run, would return something like:

x=0
y=0,
w=800
h=800

But if I were to scroll down and then run the function, I would get something like this:

x=0
y=350
w=800
h=800

And if I resized the window vertically to, say, 1000, I would get:

x=0
y=350
w=800
h=1000

I tried this code:


            self.canvas.update()
            print(f"X={self.canvas.canvasx(0)}")
            print(f"Y={self.canvas.canvasy(0)}")
            print(f"W={self.canvas.canvasx(self.canvas.winfo_width())}")
            print(f"H={self.canvas.canvasy(self.canvas.winfo_height())}")

But it gives me the size of the whole canvas, not the visible window inside the canvas.

I have tried searching for the answer, but am surprised not to have found anyone else with the same question. Perhaps I just don't have the right search terms.

Context for anyone who cares:

I am writing a thumbnail browser that is a grid of thumbnails. Since there may be thousands of thumbnails, I want to update the ones that are visible first, and then use a thread to update the remaining (hidden) thumbnails as needed.

CodePudding user response:

The methods canvasx and canvasy of the Canvas widget will convert screen pixels (ie: what's visible on the screen) into canvas pixels (the location in the larger virtual canvas).

You can feed it an x or y of zero to get the virtual pixel at the top-left of the visible window, and you can give it the width and height of the widget to get the pixel at the bottom-right of the visible window.

x0 = canvas.canvasx(0)
y0 = canvas.canvasy(0)
x1 = canvas.canvasx(canvas.winfo_width())
y1 = canvas.canvasy(canvas.winfo_height())

The canonical tcl/tk documentation says this about the canvasx method:

pathName canvasx screenx ?gridspacing?: Given a window x-coordinate in the canvas screenx, this command returns the canvas x-coordinate that is displayed at that location. If gridspacing is specified, then the canvas coordinate is rounded to the nearest multiple of gridspacing units.


Here is a contrived program that will print the coordinates of the top-left and bottom right visible pixels every five seconds.

import tkinter as tk

root = tk.Tk()
canvas_frame = tk.Frame(root, bd=1, relief="sunken")
statusbar = tk.Label(root, bd=1, relief="sunken")

statusbar.pack(side="bottom", fill="x")
canvas_frame.pack(fill="both", expand=True)

canvas = tk.Canvas(canvas_frame, width=800, height=800, bd=0, highlightthickness=0)
ysb = tk.Scrollbar(canvas_frame, orient="vertical", command=canvas.yview)
xsb = tk.Scrollbar(canvas_frame, orient="horizontal", command=canvas.xview)
canvas.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)

canvas_frame.grid_rowconfigure(0, weight=1)
canvas_frame.grid_columnconfigure(0, weight=1)
canvas.grid(row=0, column=0, sticky="nsew")
ysb.grid(row=0, column=1, sticky="ns")
xsb.grid(row=1, column=0, sticky="ew")

for row in range(70):
    for column in range(10):
        x = column * 80
        y = row * 80
        canvas.create_rectangle(x, y, x 64, y 64, fill="gray")
        canvas.create_text(x 32, y 32, anchor="c", text=f"{row},{column}")

canvas.configure(scrollregion=canvas.bbox("all"))

def show_coords():
    x0 = int(canvas.canvasx(0))
    y0 = int(canvas.canvasy(0))
    x1 = int(canvas.canvasx(canvas.winfo_width()))
    y1 = int(canvas.canvasy(canvas.winfo_height()))
    statusbar.configure(text=f"{x0},{y0} / {x1},{y1}")
    root.after(5000, show_coords)

show_coords()

root.mainloop()
  • Related