Tkinter Toplevel in OOP script: how?

3.1k views Asked by At

Goal of the script:

  • (3) different windows, each in its own class, with its own widgets and layout, are created via Toplevel and callbacks.
  • When a new (Toplevel) window is created, the previous one is destroyed. Thus, only one window is visible and active at a time.

Problem? Basically, I've tried many things and failed, so I must understand too little of ["parent", "master", "root", "app", "..."] :(

Note on raising windows: I have implemented a successful example of loading all frames on top of each other, and controlling their visibility via the .raise method.

For this problem, however, I don't want to load all the frames at once. This is an abstracted version of a quiz program that will require quite a lot of frames with images, which makes me reluctant to load everything at once.

Script (not working; bugged):

#!/usr/bin/env python

from Tkinter import *
import tkMessageBox, tkFont, random, ttk

class First_Window(Frame):
    """The option menu which is shown at startup"""
    def __init__(self, master):
        Frame.__init__(self, master)

        self.gotosecond = Button(text = "Start", command = self.goto_Second)
        self.gotosecond.grid(row = 2, column = 3, sticky = W+E)

    def goto_Second(self):
        self.master.withdraw()
        self.master.update_idletasks()
        Second_Window = Toplevel(self)

class Second_Window(Toplevel):
    """The gamewindow with questions, timer and entrywidget"""
    def __init__(self, *args):
        Toplevel.__init__(self)
        self.focus_set()

        self.gotothird = Button(text = "gameover", command = self.goto_Third)
        self.gotothird.grid(row = 2, column = 3, sticky = W+E)

    def goto_Third(self):
        Third_Window = Toplevel(self)
        self.destroy()

class Third_Window(Toplevel):
    """Highscores are shown with buttons to Startmenu"""
    def __init__(self, *args):
        Toplevel.__init__(self)
        self.focus_set()
        self.master = First_Window

        self.gotofirst = Button(text = "startover", command = self.goto_First)
        self.gotofirst.grid(row = 2, column = 3, sticky = W+E)

    def goto_First(self):
        self.master.update()
        self.master.deiconify()
        self.destroy()

def main():
    root = Tk()
    root.title("Algebra game by PJK")
    app = First_Window(root)
    root.resizable(FALSE,FALSE)
    app.mainloop()

main()
1

There are 1 answers

2
abarnert On

The problem is not really a Tkinter problem, but a basic problem with classes vs. instances. Actually, two similar but separate problems. You probably need to read through a tutorial on classes, like the one in the official Python tutorial.


First:

self.master = First_Window

First_Window is a class. You have an instance of that class (in the global variable named app), which represents the first window on the screen. You can call update and deiconify and so forth on that instance, because it represents that window. But First_Window itself isn't representing any particular window, it's just a class, a factory for creating instances that represent particular windows. So you can't call update or deiconify on the class.

What you probably want to do is pass the first window down through the chain of windows. (You could, alternatively, access the global, or do various other things, but this seems cleanest.) You're already trying to pass it to Second_Window, you just need to stash it and pass it again in the Second_Window (instead of passing self instance, which is useless—it's just a destroyed window object), and then stash it and use it in the Third_Window.


Second:

Second_Window = Toplevel(self)

Instead of creating an instance of the Second_Window class, you're just creating an instance of the generic Toplevel class, and giving it the local name Second_Window (which temporarily hides the class name… but since you never use that class, that doesn't really matter).

And you have the same problem when you try to create the third window.


So:

class First_Window(Frame):
    # ...
    def goto_Second(self):
        # ...
        second = Second_Window(self)

class Second_Window(Toplevel):
    def __init__(self, first, *args):
        Toplevel.__init__(self)
        self.first = first
        # ...

    def goto_Third(self):
        third = Third_Window(self.first)
        self.destroy()

class Third_Window(Toplevel):
    """Highscores are shown with buttons to Startmenu"""
    def __init__(self, first, *args):
        Toplevel.__init__(self)
        self.first = first
        # ...

    def goto_First(self):
        self.first.update()
        self.first.deiconify()
        self.destroy()