0

I have been trying to find a way to size a frame inside of a canvas window for quite a while to no avail. I finally came across some posts that helped me begin to understand the problem, and eventually dug up a post that gave the solution below:

import tkinter as tk

def onCanvasConfigure(e):
    canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())

root=tk.Tk()

canvas = tk.Canvas(root, background="blue")
frame = tk.Frame(canvas, background="red")

canvas.pack(expand=True, fill="both")
canvas.create_window((0,0), window=frame, anchor="nw", tags="frame")

canvas.bind("<Configure>", onCanvasConfigure)

root.mainloop()

This completely solves my problem....if I don't have the GUI in a function, which I need to. I have multiple different GUI's that would need to implement this solution. I have come across other solutions that use OOP, but I haven't yet wrapped my head around OOP. I've also found a way to make the above code work inside of a program myself:

import tkinter as tk

def onCanvasConfigure(e):
    canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())

def test():
    window=tk.Tk()

    global canvas
    
    canvas = tk.Canvas(master=window)
    frame=tk.Frame(master=canvas, background='red')

    canvas.pack(expand=True, fill=tk.BOTH)
    canvas.create_window((0,0), window=frame, anchor=tk.NW, tags = 'frame')

    canvas.bind("<Configure>", onCanvasConfigure)

    root.mainloop()

test()

However, this requires the use of a global variable, which I would rather avoid. Is there anything I'm missing that would help me resize the frame inside of the canvas window? If you have any pointers to where I might even find this information that would also be helpful.

  • "This completely solves my problem....if I don't have the GUI in a function, which I need to" 1) what happens to the canvas if you instantiate the GUI inside a function? 2) why do you *need* to instantiate the GUI this way? 3) if a global fixes the problem, why avoid it? (Yes, I know global variables come with their own caveats, but they have their uses...and this sure looks like one of them) – JRiggles Feb 07 '23 at 20:40
  • Would I not run into issues with naming? I have multiple GUI's that would need to use variables that I would typically name the same inside of their own functions, can I get away with reusing this global variable, or would it cause problems? – Tyler Willke Feb 07 '23 at 20:42
  • If you had multiple functions accessing a variable named `canvas` you might run into issues *if* each of those functions also called `global canvas` - that would mean they're all referring to the *same* `canvas` object (if that's not what you want, then *there's* the problem). But if a function doesn't specify `global canvas` and it happens to contain a variable named `canvas` that particular variable will be **local to the function** and have no bearing on your "main" `canvas` object – JRiggles Feb 07 '23 at 20:52

2 Answers2

1

The event object that is passed in has a reference to the widget that received the event. So, just replace your global canvas with e.widget, or initialize a local variable named canvas:

def onCanvasConfigure(e):
    canvas = e.widget
    canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

If it helps, here's an object-oriented version of your application code. Other than the implementation differences, it should behave the same way as the functional version.

import tkinter as tk


class App(tk.Tk):  # create a base app class that inherits from Tk
    def __init__(self):
        super().__init__()  # initialize Tk
    
        self.canvas = tk.Canvas(master=self)
        self.frame = tk.Frame(master=self.canvas, background='red')

        self.canvas.pack(expand=True, fill=tk.BOTH)
        self.canvas.create_window(
            (0,0),
            window=self.frame,
            anchor=tk.NW,
            tags='frame',
        )

        self.canvas.bind('<Configure>', self.on_canvas_configure)
 
    def on_canvas_configure(self, event):
        self.canvas.itemconfig(
            'frame',
            height=self.canvas.winfo_height(),
            width=self.canvas.winfo_width(),
        )


if __name__ == '__main__':
    root = App()  # instantiate the App class
    root.mainloop()  # start the app

Since everything here is contained within the App class, you can avoid globals (thanks to self!)

JRiggles
  • 4,847
  • 1
  • 12
  • 27