AracKnight
AracKnight

Reputation: 397

tkinter: Frame in Toplevel displayed in parent

I have currently two problems with Toplevel instances in Tkinter.

First and most important: I want to display a popup window and place 2 frames in it for better arangement in grid, but it doesn't work as I expect it to work:

import tkinter


root = tkinter.Tk()
tkinter.Button(root, text="ABC").grid(column=0, row=0)
tkinter.Label(root, text="FOO").grid(column=1, row=1)

win = tkinter.Toplevel()
f1 = tkinter.Frame(win).grid(row=0, column=0)
f2 = tkinter.Frame(win).grid(row=1, column=1)
tkinter.Label(f1, text="FRAME 1").grid()
tkinter.Label(f2, text="FRAME 2").grid()

root.mainloop()

I would expect "FRAME 1" and "FRAME 2" to be placed in the Toplevel window, but they are actually placed in root. How do I fix this?

Second, less important: The popup window in the code above is spawning behind the root window, while I would like it to be placed in front of root, how do I achieve this?

Upvotes: 1

Views: 6138

Answers (2)

Mike - SMT
Mike - SMT

Reputation: 15226

When setting your geometry manager be it grid(), pack() or place() and you need to be able to interact with that widget later you will need to assign the widget to a variable and then apply the geometry manager on a new line using that variable name. This way your variable will not be a value of None but rather the proper widget. This happens because the geometry managers all return None.

Next the reason your labels are on the wrong windows is because when your labels try to connect with f1 and f2 they are not able to find a proper tkinter container due to the values being None so it defaults to the root tkinter window in an attempt to be place on something.

With fixing the None issues you will also fix your label issue.

To address the matter of your top level window not being in front of your root window there are a couple of things you can do. The main reason this is happening is how your code is generating the top level at __init__ rather than later with a button or a timed event.

If you really need your top level window to open at the same time as root you can use after() and a function to do this and it will be placed on top. If you do not need it right when the window opens you may want to assign a command to a button to run a function that builds the top window.

Here is an example with after():

import tkinter as tk


root = tk.Tk()

def create_top():
    win = tk.Toplevel(root)
    f1 = tk.Frame(win)
    f1.grid(row=0, column=0)
    f2 = tk.Frame(win)
    f2.grid(row=1, column=1)
    tk.Label(f1, text="FRAME 1").grid()
    tk.Label(f2, text="FRAME 2").grid()

tk.Button(root, text="ABC").grid(column=0, row=0)
tk.Label(root, text="FOO").grid(column=1, row=1)

root.after(10, create_top)
root.mainloop()

Here is an example with a button:

import tkinter as tk


root = tk.Tk()

def create_top():
    win = tk.Toplevel(root)
    f1 = tk.Frame(win)
    f1.grid(row=0, column=0)
    f2 = tk.Frame(win)
    f2.grid(row=1, column=1)
    tk.Label(f1, text="FRAME 1").grid()
    tk.Label(f2, text="FRAME 2").grid()

tk.Button(root, text="ABC", command=create_top).grid(column=0, row=0)
tk.Label(root, text="FOO").grid(column=1, row=1)

root.mainloop()

Upvotes: 2

user10455554
user10455554

Reputation: 413

You set your frames f1 and f2 to the return-value of the grid() command, which is None, therefore tkinter.Label(f1, text="FRAME 1").grid() does not work as you expect.

Try something like this:

win = tkinter.Toplevel()
f1 = tkinter.Frame(win)
f1.grid(row=0, column=0)
tkinter.Label(f1, text="FRAME 1").grid()

Upvotes: 4

Related Questions