简体   繁体   中英

Tkinter pack() geometry manager confusion

Just when I thought I understood the pack() manager, I came upon the following problem:

I created two frames (each with a Button ) and want to pack them horizontally in another frame (which is the 'main' frame). For some reason, they appear vertically. If I replace the first frame by a simple button, the button + second frame are packed horizontally, using the same pack() instructions.

Here's a distilled (but working) version of the program:

from Tkinter import *

class QuitBtn1(Button):
    def __init__(self):
        Button.__init__(self)
        self["text"] = "Quit"
        self["fg"] = "red"
        self["command"] = self.quit

class QuitBtn(Frame):
    def __init__(self):
        Frame.__init__(self)

        b = Button(text = "Quit", fg = "red", command = self.quit)
        b.pack()

class HiBtn(Frame):
    def __init__(self):
        Frame.__init__(self)

        b = Button(text = "Hi there", fg = "blue", command = self.quit)
        b.pack()

class App(Frame):
    def __init__(self, master = None):
        Frame.__init__(self, master)
        self.pack()
        self.createWidgets()

    def say_hi(self):
        print "hi there, everyone!"

    def createWidgets(self):
        self.QUIT = QuitBtn().pack(side = LEFT)
        self.hi_there = HiBtn().pack(side = LEFT)


root = Tk()
app = App(master = root)
app.mainloop()
root.destroy()

As run, it produces two, vertically packed, frames. Just switching the names of QuitBtn1 and QuitBtn (which changes the frame into a simple button), changes the packing to horizontal.

I've looked at tens of examples, which seem to somehow avoid this exact structure. Am I doing something wrong here?

The problem is that you aren't giving any of your widgets a parent, so they all default to the root window. Even though you think the buttons are inside the frames, they are not.

Because the default for pack is to put things along the top, when you call pack() on the buttons, they are being stacked in the root window. Your two frames, because nothing is in them, have a size of 1x1 so you can't see them. The are being packed on the left, you just can't see them.

The solution is to properly nest your widgets. For example:

class QuitBtn(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)

        b = Button(self, text = "Quit", fg = "red", command = self.quit)
        b.pack()

def createWidgets(self):
    self.QUIT = QuitBtn(self)
    ...
    self.QUIT.pack(side = LEFT)
    ...

The above creates a button widget inside the QuitBtn frame, and packs it to the top of that frame. Later, that frame (and it's contents) will be packed to the left side of the main frame.

It's also important to separate the creation of your widgets from the layout of your widgets. When you do something like self.QUIT = QuitBtn(...).pack(...) , self.QUIT will be set to None because that is what pack(...) returns. Plus, in my experience, moving all of your layout for a given containing frame into a separate block makes it much easier to manage your layouts.

One way is to tell pack what you want to pack each widget in, using the in_= parameter. So:

from Tkinter import *

class QuitBtn(Frame):
    def __init__(self):
        Frame.__init__(self)

        b = Button(text = "Quit", fg = "red", command = self.quit)
        b.pack(in_=self)                                                   # Here

class HiBtn(Frame):
    def __init__(self):
        Frame.__init__(self)

        b = Button(text = "Hi there", fg = "blue", command = self.quit)
        b.pack(in_=self)                                                   # Here

class App(Frame):
    def __init__(self, master = None):
        Frame.__init__(self, master)
        self.pack()
        self.createWidgets()

    def say_hi(self):
        print "hi there, everyone!"

    def createWidgets(self):
        self.QUIT = QuitBtn().pack(side = LEFT)       
        self.hi_there = HiBtn().pack(side = LEFT)      


root = Tk()
app = App(master = root)
app.mainloop()
root.destroy()

Produces a horizontal arrangement of buttons within frames (each within the App frame).

在此处输入图片说明

I don't think I've ever needed this myself, but I don't remember ever declaring widgets using top-level classes either.

As a side note, thank you so much for supplying a distilled, version of your code that demonstrates the same issue! I wish this were more common.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM