How to pack a tkinter widget underneath an existing widget that has been packed to the left side?
Asked Answered
T

4

17

I'm attempting to write a basic Tkinter GUI that has a Text widget at the top, then a Button widget left aligned under it, then another Text widget underneath the button. The problem I'm having is, after packing the Button widget to the left, when I then go to pack the second Text widget, it puts it next to the button on the right, rather than underneath the button. This happens regardless of what I set the side argument to for the second Text widget Here's a simple piece of code that demonstrates this behaviour:

from Tkinter import *

root = Tk()

w = Text(root)
w.pack()

x = Button(root, text="Hi there!")
x.pack(side=LEFT)

y = Text(root)
y.pack(side=BOTTOM)

root.mainloop()

So how would I go about setting up the second Text widget so that it appears below the button, rather than to the right of it?

Trifurcate answered 28/9, 2009 at 4:25 Comment(1)
The before keyword argument of the pack() method might help.Kobold
Z
19

There are generally two solutions to layout problems:

  1. switch to using grid. It becomes real easy to do layouts like what you are trying to accomplish. Grid can solve probably 95% of all layout issues (it's amazing when you think about it -- Tk does with one manager what most toolkits need half a dozen to accomplish!)

  2. use multiple frames. If some widgets need to be stacked top-to-bottom and some left-to-right you can't always get what you want packing everything in a single frame. Use one frame for the top-to-bottom parts of the layout and additional frames for the left-to-right content.

Also realize that widgets don't have to be children of the widget in which they are packed/gridded. You can use the "in" parameter to put widgets in some other container than their parent.

For example, in your specific example you can create three frames, top, middle, bottom. Pack these top-to-bottom in your toplevel window. Then you can pack the first text widget in the top, the button or buttons horizontally in the middle, and the other text widget in the bottom.

The advantage to such an approach is that it makes it much easier to change the layout in the future (which in my experience always happens at some point). You don't have to re-parent any of your widgets, just pack/place/grid them in some other container.

In your short example it doesn't make much difference, but for complex apps this strategy can be a life saver.

My best advice is this: layout isn't an afterthought. Do a little planning, maybe even spend five minutes drawing on some graph paper. First decide on the major regions of your app and use a frame or some other container for each (paned window, notebook, etc). Once you have those, do the same divide-and-conquer approach for each section. This lets you use different types of layout for different sections of your app. Toolbars get horizontal layout, forms might get vertical layout, etc.

Zootechnics answered 29/9, 2009 at 11:40 Comment(0)
T
10

I was initially misunderstanding how packing worked and didn't realise that the entire left side was being "claimed" when i did x.pack(side=LEFT). What I found after reading this and the answer by Alex here is that I was not really after having x packed to the left side at all, but rather having it anchored to the left, using anchor=W (W for West) instead of side=LEFT. My revised code snippet which does what I was after looks like this:

from tkinter import *

root = Tk()

w = Text(root)
w.pack()

x = Button(root, text="Hi there!")
x.pack(anchor=W)

y = Text(root)
y.pack(side=BOTTOM)

root.mainloop()

This way x is not "claiming" the left side anymore, it's just aligned to the left (or West) within its block of space.

Trifurcate answered 28/9, 2009 at 6:22 Comment(0)
S
4

Packing happens in the order the .pack methods are called, so once x has "claimed" the left side, that's it -- it will take up the left portion of its parent and everything else within its parent will be to its right. You need a Frame to "mediate", e.g....:

from Tkinter import *

root = Tk()

w = Button(root, text="Mysterious W")
w.pack()

f = Frame(root)
x = Button(f, text="Hi there!")
x.pack()

y = Button(f, text="I be Y")
y.pack(side=BOTTOM)

f.pack(side=LEFT)

root.mainloop()

(changed Texts to Buttons for more immediate visibility of layout only -- the Tkinter on this Mac doesn't show Texts clearly until they have focus, but Buttons are quite clear;-).

Selfforgetful answered 28/9, 2009 at 5:15 Comment(0)
C
3

Do it the same way that WebView does using the Mosaic Canvas Widget Sets internals(which are very similar to Tk). The trick is that the second identical named Frame Object works as a Block Level Float(inline:block;) for everything placed after it and everything that calls "fr" already will automatically begin over inside of it.

You can have many doing this of TOP aligned widgets and simply add another identical named Frame where you want to break between side=LEFT's. Works after Bottom also.

fr=Frame(root)
fr.pack(fill=X, side=TOP)

block1=Label(fr)
block1.pack(side=LEFT)

block2=Label(fr)
block2.pack(side=LEFT)

block3=Button(fr)
block3.pack(side=LEFT)

# NAME IT THE SAME ID NAME AS THE FIRST MAIN FRAME...
fr=Frame(root)
fr.pack(fill=X, side=TOP)

# These NOW jump into the second Frame breaking the side=LEFT in new Frame
block4=Label(fr) 
block4.pack(side=LEFT)

block5=Label(fr)
block5.pack(side=LEFT)

# AND THEY CONTINUE GOING side=LEFT AFTERWARDS.
Cesena answered 29/3, 2019 at 6:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.