0

I'm using the following code to create a Tkinter window and switch the frames it displays.

import tkinter

class GUI(tkinter.Tk):
    def __init__(self):
        tkinter.Tk.__init__(self)
        self._frame = None
        self.switch(Frame1)

    def switch(self, frame_class):
        new_frame = frame_class(self)
        if self._frame is not None:
            self._frame.destroy()
        self._frame = new_frame
        self._frame.pack()


class Frame1(tkinter.Frame):
    def __init__(self, parent):
        tkinter.Frame.__init__(self, parent)
        tkinter.Label(self, text="Frame1").pack()

        parent.switch(Frame2) # not working
        tkinter.Button(self, text="switch", command=lambda: parent.switch(Frame2)).pack() # working


class Frame2(tkinter.Frame):
    def __init__(self, parent):
        tkinter.Frame.__init__(self, parent)
        tkinter.Label(self, text="Frame2").pack()


if __name__ == '__main__':
    gui = GUI()
    gui.mainloop()

Note that the switch function is executed without a button, yet only works on click.

Martin007
  • 19
  • 9
  • *Where* exactly are you calling `.switch()`, if not from a button? We need to see a [mcve] to be able to help. – jasonharper Mar 30 '21 at 13:53
  • @jasonharper thanks, I edited my question. – Martin007 Mar 30 '21 at 14:16
  • Your call to `.switch()` in `Frame1.__init__()` is happening *during* the call to `.switch()` made in `GUI.__init__()`. The outer call undoes the effects of the inner call. But this code makes no sense anyway, what's the point of having a `Frame1` that simply replaces itself with `Frame2`? – jasonharper Mar 30 '21 at 14:25
  • @jasonharper there is a data reading process just above the ```.switch()``` call in ```Frame1.__init__()```. Frame1 should be displayed until this process is complete, but I can't switch frames inside the ```Frame1.__init__()``` method. What can I do? – Martin007 Mar 30 '21 at 14:41
  • In the scenario you describe, `Frame1` would never actually be visible; widget state changes are not immediately shown, you normally have to return to the mainloop for visual updates to actually happen. It's possible to force an update sooner, but that would still leave your GUI in an unresponsive state for the duration of your data reading process - which isn't a great user experience. – jasonharper Mar 30 '21 at 15:04
  • I think the simplest thing that would get your code running would be to do the data reading and the second `.switch()` in a separate method, that you schedule slightly in the future via `.after()`. Even a 1-millisecond delay would allow `Frame1` to become visible before the data reading begins. – jasonharper Mar 30 '21 at 15:04

1 Answers1

0

This is what is happening:

  1. GUI starts to switch to frame 1
  2. Frame1 is created and switches to frame 2
  3. GUI finishes switch to frame 1

The app, therefore, ends up on frame 1

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685