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()