Search code examples
pythoncanvastkinterinitializationdimensions

Tkinter: Get actual canvas size during init (including auto-stretching by "sticky")


I am trying to center some text on a canvas, during program initialization. However, winfo_width/height don't return the correct values for me in this case, so I cannot properly place text using Canvas method create_text(), since I cannot calculate the correct center position. I can only get the correct dimensions post-init, say if I query the size in a button callback.

How to solve this? Here's the code:

try:
    from Tkinter import *
except ImportError:
    from tkinter import *

class GUI:
    def __init__(self):
        # root window of the whole program
        self.root = Tk()
        self.root.minsize(800, 600)

        # canvas/viewport for displaying the image and drawing vectors on it
        self.viewport = Canvas(self.root, bd=2, relief='ridge', highlightthickness=0)

        # define master buttons for audio preview, render to file and clear all vectors
        btn_preview = Button(self.root, text='Preview', command=self.Preview)

        # layout managing
        self.viewport.grid(columnspan=3, padx=5, pady=5, sticky=N+S+W+E)
        btn_preview.grid(row=1, padx=85, pady=10, ipadx=5, ipady=5, sticky=W)

        # position text on canvas to notify user he can load the image by clicking it
        self.viewport.update_idletasks()
        textpos = (self.viewport.winfo_width(),self.viewport.winfo_height())
        print(textpos)
        self.viewport.create_text(textpos[0] / 2, textpos[1] / 2, text="Click here to load an image!", justify='center', font='arial 20 bold')

        # weights of rows and columns
        self.root.rowconfigure(0, weight=1)
        self.root.columnconfigure(0, weight=1)

    def Preview(self, event=None):
        textpos = (self.viewport.winfo_width(),self.viewport.winfo_height())
        print(textpos)

if __name__ == '__main__':
    mainwindow = GUI()
    mainloop()

Compare the dimensions returned on init to dimensions after you click the Preview button. They're different!


Solution

  • OK haha, I managed to solve it after checking this answer. I needed to bind <Configure> event to the canvas, and define a function that does stuff when window is resized. It's working now!

    try:
        from Tkinter import *
    except ImportError:
        from tkinter import *
    
    class GUI:
        textid = 0
    
        def __init__(self):
            # root window of the whole program
            self.root = Tk()
            self.root.minsize(800, 600)
    
            # canvas/viewport for displaying the image and drawing vectors on it
            self.viewport = Canvas(self.root, bd=2, relief='ridge', highlightthickness=0)
    
            # define master buttons for audio preview, render to file and clear all vectors
            btn_preview = Button(self.root, text='Preview', command=self.Preview)
    
            # layout managing
            self.viewport.grid(columnspan=3, padx=5, pady=5, sticky=N+S+W+E)
            btn_preview.grid(row=1, padx=85, pady=10, ipadx=5, ipady=5, sticky=W)
    
            # weights of rows and columns
            self.root.rowconfigure(0, weight=1)
            self.root.columnconfigure(0, weight=1)
    
            # bind mouse actions for the canvas
            self.viewport.bind('<Configure>', self.ResizeCanvas)
    
        def Preview(self, event=None):
            textpos = (self.viewport.winfo_width(),self.viewport.winfo_height())
            print(textpos)
    
        def ResizeCanvas(self, event):
            if self.textid != 0:
                event.widget.delete('openfiletext')
            # position text on canvas to notify user he can load the image by clicking it
            textpos = (self.viewport.winfo_width(), self.viewport.winfo_height())
            self.textid = self.viewport.create_text(textpos[0] / 2, textpos[1] / 2, text="Click here to load an image!", justify='center', font='arial 20 bold', tag='openfiletext')
    
    
    if __name__ == '__main__':
        mainwindow = GUI()
        mainloop()