1

I am trying to write an application which requires some logic to be performed every time a particular frame is shown. Taking advice from the questions here, here and here, I have come up with some code structured like this:

import tkinter as tk
from tkinter import ttk


class MainApp(tk.Tk):
    def __init__(self, *args, **kwargs) -> None:
        tk.Tk.__init__(self, *args, **kwargs)

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.madlibs_source = ""

        self.frames = {
            "PageOne":  PageOne(
                parent=container,
                controller=self,
                ),
            "PageTwo": PageTwo(
                parent=container,
                controller=self,
                ),
            "PageThree": PageThree(
                parent=container,
                controller=self,
                ),
            }

        self.frames["PageOne"].grid()

    def show_frame(self, page_name: str) -> None:
        '''Show a frame for the given page name'''
        for frame in self.frames.values():
            frame.grid_remove()
        frame = self.frames[page_name]
        frame.grid()
        frame.event_generate("<<ShowFrame>>")


class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        page_two_button = ttk.Button(
            self,
            text="Page 2",
            command=lambda: self.controller.show_frame("PageTwo"),
            )
        page_two_button.pack(padx="10", pady="10")

        page_three_button = ttk.Button(
            self,
            text="Page 3",
            command=lambda: self.controller.show_frame("PageThree"),
            )
        page_three_button.pack(padx="10", pady="10")


class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        page_three_button = ttk.Button(
            self,
            text="Page 3",
            command=lambda: self.controller.show_frame("PageThree"),
            )
        page_three_button.pack(padx="10", pady="10")


class PageThree(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        some_label = tk.Label(self, text="This is page three")
        some_label.pack()
        self.bind("<<ShowFrame>>", self.frame_shown)

    def frame_shown(self, event):
        print("Doing something")


if __name__ == "__main__":
    root = MainApp()
    root.mainloop()

I would expect from this that whenever page three is shown, the frame_shown method would be run and "Doing Something" would be output to the console, but this doesn't seem to to be the case. Can anybody point out where I'm going wrong?

1 Answers1

0

My guess is that the event isn't being processed since the frame isn't visible at the time that you generate the event. I didn't see anything in the documentation about this, but that's the only explanation I can think of.

If you wait for the window to be visible before generating the event, the event will be processed as expected. You can do that by calling frame.wait_visibility() before generating the event.

    def show_frame(self, page_name: str) -> None:
        '''Show a frame for the given page name'''
        for frame in self.frames.values():
            frame.grid_remove()
        frame = self.frames[page_name]
        frame.grid()
        frame.wait_visibility()
        frame.event_generate("<<ShowFrame>>")
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685