Search code examples
pythontkintertkinter-canvascustomtkinter

how to get size in pixels of canvas object, packed iinto ctk.frame


So, I've got this code

import customtkinter

from tkinter import Canvas
import tkinter as tk
from PIL import Image, ImageTk

app = customtkinter.CTk()

app.title = 'spectrum (v1.0.0)'
app.minsize(800, 600)

frame1 = customtkinter.CTkFrame(master=app)
frame2 = customtkinter.CTkFrame(master=app)

frame1.pack(padx=10, pady=10, expand=True, fill="both", side="left")
frame2.pack(padx=10, pady=10, expand=False, fill="y", side="right")

canvas1 = customtkinter.CTkCanvas(master=frame1, bg="black")
canvas1.create_line(0, 0, canvas1.size()[0], canvas1.size()[1], fill="#FFFFFF")
canvas1.pack(expand=True, fill="both")

canvas2 = Canvas(master=frame1, bg="black")
canvas2.pack(expand=True, fill="both")

canvas3 = Canvas(master=frame2, width=400, height=800, bg="black")
canvas3.pack(expand=True, fill="y")

print(canvas1.size())
print(canvas2.size())
print(canvas1.winfo_width(), canvas1.winfo_height())
print(canvas2.winfo_width(), canvas2.winfo_height())


app.mainloop()

nor Ctk.CtkCanvas (canvas1), nor Trinter.Canvas (canvas2) shows real size of the canvas widget. I expect this code to... "draw" a line from top-left to bottom-right corner of canvas1

P.S. canvas1 is the top one on the left side. and app.title doesn't work... Why? How do I fix it?

screenshot of app

I've tryed to get size by .size() and .winfo_width(), .winfo_height()screenshot of output


Solution

  • The size of the canvas can't be determined until the window actually renders, because the size is dependent on the size of the window, the geometry manager that you use (pack, grid, etc), the other widgets, and other variables.

    You can either call app.update() before trying to get the size, or configure it so that a function is called as soon as the widget is displayed (ie: bind to <Map> or <Visibility>.

    Also, the size() method doesn't return the size of the widget. It is an alias for grid_size, and returns the number of rows and columns being managed by grid inside the canvas. Since you haven't added any widgets to any rows or columns in the canvas, it's going to return (0,0).

    Using update

    ...
    app.update()
    
    print(canvas1.size())
    print(canvas2.size())
    print(canvas1.winfo_width(), canvas1.winfo_height())
    print(canvas2.winfo_width(), canvas2.winfo_height())
    

    Using a binding and function

    ...
    def do_something(event):
        print(canvas1.size())
        print(canvas2.size())
        print(canvas1.winfo_width(), canvas1.winfo_height())
        print(canvas2.winfo_width(), canvas2.winfo_height())
    canvas1.bind("<Visibility>", do_something)
    

    You have to be a bit more careful with the binding. For one, you're trying to manipulate both canvas1 and canvas2 which might not be updated at precisely the same time. Also, with the binding, the function will be called every time the event triggers, not just the first time the app starts.