Tkinter:为什么一个Frame会覆盖另一个?

2 投票
1 回答
2391 浏览
提问于 2025-04-16 02:52

我正在用Python的Tkinter库制作一个图形用户界面(GUI)。我的想法是在主框架里面放几个框架(Frame)。我能让两个框架显示出来,但第三个框架却覆盖在第二个框架上。我试过在主框架中使用pack()和grid()这两种布局管理方式。

我想要一个“顶部内容”框架和一个“底部内容”框架,中间放一个Cat1框架和1到8个Cat2框架。这个图形界面需要能够动态重建,因为应用程序需要根据控制的Cat2小部件的数量来调整。

(还有一个小问题。因为我引入了Cat2Frame类,并把tkFont变量放到了全局范围内,我的12号字体还是只有9号。)

这是我整理过的代码片段。(还没做到底部框架。)

def anchor(widget, rows=0, cols=0):
    """Takes care of anchoring/stretching widgets via grid 'sticky'"""
    for r in range(rows):
        widget.rowconfigure(r, weight=1)
    for c in range(cols):
        widget.columnconfigure(c, weight=1)


font_ = None
bold_ = None
bold_12 = None

class TkinterGui():
    """Tkinter implementation of the GUI"""
    def __init__(self):
        """Create the Tkinter GUI"""
        self._top_level = None
        self._top_row   = None
        self._buildGui(_TITLE)


    def _buildGui(self, title):
        """Build the Tkinter GUI"""
        self._top_level  = Tkinter.Tk()
        font_ = tkFont.Font(family='FreeSans', size=9)
        bold_ = tkFont.Font(family='FreeSans', size=9, weight='bold')
        bold_12 = tkFont.Font(family='FreeSans', size=12, weight='bold')
        anchor(self._top_level, 4, 1)
        self._top_row = 0
        self._buildTop()
        self._top_row = 1
        self._buildCat1()
        t1 = Cat2Frame(self._top_level, "Cat2 1")
        self._top_row = 2
        t1.place_frame(self._top_row)
        t5 = Cat2Frame(self._top_level, "Cat2 5")
        self._top_row = 3
        t5.place_frame(self._top_row)
        self._top_level.title(title)


    def _buildTop(self):
        """Private method to build the Top frame of the GUI."""
        top_frame = Tkinter.Frame(self._top_level, name='top_frame')
        anchor(top_frame, 1, 3)
        top_frame.columnconfigure(0, weight=2)
        top_frame.columnconfigure(1, weight=5)
        col1_label = Tkinter.Label(top_frame
                                   , name='col1_label'
                                   , text="Col1"
                                   , font=bold_12
                                   , width=20
                                    ).grid(row=0
                                         , column=0
                                         , sticky=N+E+W+S
                                          )
        col2_label = Tkinter.Label(top_frame
                                   , name='col2_label'
                                   , text="Col2"
                                   , font=bold_12
                                   , width=40
                                    ).grid(row=0
                                         , column=1
                                         , sticky=N+E+W+S
                                          )
        top_button = Tkinter.Button(top_frame
                                    , name='top_button'
                                    , text='Top Button'
                                    , font=bold_
                                     ).grid(row=0
                                          , column=2
                                          , sticky=E
                                           )
        top_frame.grid(row=self._top_row, column=0, sticky=N+W)


    def _buildCat1(self):
        """Private method to build the Cat1 frame of the GUI"""
        cat1_frame = Tkinter.Frame(self._top_level, name='cat1_frame')
        anchor(cat1_frame, 3, 3)
        cur_row = 0
        cat1_frame.columnconfigure(2, weight=6)
        Tkinter.Label(cat1_frame
                    , name='cat1_label'
                    , text='Cat1'
                    , font=bold_
                     ).grid(row=cur_row, column=0, sticky=N+E+W+S)
        cat1_size = Tkinter.Text(cat1_frame
                                 , name='cat1_size'
                                 , state=DISABLED
                                 , font=font_
                                 , height=1
                                 , width=10
                                  ).grid(row=cur_row
                                       , column=1
                                       , sticky=E
                                        )
        cat1_status = Tkinter.Text(cat1_frame
                                   , name='cat1_status'
                                   , state=DISABLED
                                   , font=font_
                                   , height=3
                                   , width=72
                                    ).grid(row=cur_row
                                         , column=2
                                         , rowspan=3
                                         , sticky=N+E+W+S
                                          )
        cur_row += 1
        cat1_model = Tkinter.Text(cat1_frame
                                  , name='cat1_model'
                                  , state=DISABLED
                                  , font=font_
                                  , height=1
                                  , width=30
                                   ).grid(row=cur_row
                                        , column=0
                                        , columnspan=2
                                        , sticky=N+W
                                         )
        cur_row += 1
        cat1_serial = Tkinter.Text(cat1_frame
                                   , name='cat1_serial'
                                   , state=DISABLED
                                   , font=font_
                                   , height=1
                                   , width=30
                                    ).grid(row=cur_row
                                         , column=0
                                         , columnspan=2
                                         , sticky=N+W
                                          )
        cat1_frame.grid(row=self._top_row, column=0, sticky=N+W)


class Cat2Frame():
    """Class encapsulation for a Cat2 Frame in the GUI"""
    def __init__(self, parent, t_label):
        """Initialize a Cat2 Frame in the GUI"""
        self._frame = Tkinter.Frame(parent, name='cat2_frame')
        anchor(self._frame, 3, 4)
        self.cur_row = 0
        self._frame.columnconfigure(2, weight=5)

        self._label = Tkinter.Label(self._frame
                                  , name='cat2_label'
                                  , text=t_label
                                  , font=bold_
                                   )
        self._size = Tkinter.Text(self._frame
                                , name='cat2_size'
                                , state=DISABLED
                                , font=font_
                                , height=1
                                , width=10
                                 )
        self._status = Tkinter.Text(self._frame
                                  , name='cat2_status'
                                  , state=DISABLED
                                  , font=font_
                                  , height=3
                                  , width=60
                                   )
        self._control = Tkinter.IntVar()
        self._enabled = Tkinter.Radiobutton(self._frame
                                          , name='cat2_enabled'
                                          , variable=self._control
                                          , text='Enabled'
                                          , font=bold_
                                          , value=1
                                           )
        self._model = Tkinter.Text(self._frame
                                 , name='cat2_model'
                                 , state=DISABLED
                                 , font=font_
                                 , height=1
                                 , width=30
                                  )
        self._disabled = Tkinter.Radiobutton(self._frame
                                           , name='cat2_disabled'
                                           , variable=self._control
                                           , text='Disabled'
                                           , font=bold_
                                           , value=0
                                            )
        self._serial = Tkinter.Text(self._frame
                                  , name='cat2_serial'
                                  , state=DISABLED
                                  , font=font_
                                  , height=1
                                  , width=30
                                   )


    def place_frame(self, top_row):
        self._label.grid(row=self.cur_row, column=0, sticky=N+E+S+W)
        self._size.grid(row=self.cur_row, column=1, sticky=E)
        self._status.grid(row=self.cur_row, column=2, rowspan=3, sticky=N+E+W+S)
        self._enabled.grid(row=self.cur_row, column=3, sticky=W)
        self.cur_row += 1
        self._model.grid(row=self.cur_row, column=0, columnspan=2, sticky=N+W)
        self._disabled.grid(row=self.cur_row, column=3, sticky=W)
        self.cur_row += 1
        self._serial.grid(row=self.cur_row, column=0, columnspan=2, sticky=N+W)
        self.cur_row += 1
        self._frame.grid(row=top_row, column=0, sticky=N+W)

1 个回答

3

你的代码问题的简单回答是,你给了两个 Cat2Frame 对象相同的名字。要么给它们起不同的名字,要么就别用名字属性来命名这个框架。

另外,我建议你考虑一下你的编程风格。你写的代码很难读懂。我发现把布局和小部件的创建分开写,会大大提高可读性,这样也能更容易找到错误(因为可以一眼看出布局是否正确)。

这里有个简单的建议,告诉你怎么可以让代码更易读。我不是说这是重构代码的最佳方法,只是给你一些思路。

我假设你想把所有的行都放在一个网格里,所以你的每个“框架”(Cat1Frame, Cat2Frame)其实并不是真正的框架,因为在不同的小部件中对齐单元格会很困难。如果不是这样(也就是说,每个“框架”确实是独立的小部件),那么代码可以更简单。

我没有花时间去创建顶行,也没有精确调整行和列的大小。我还给每个文本小部件加了背景,这样你可以看到它们是怎么布局的。我发现,当你能看到小部件的边界时,解决布局问题会简单得多。

import Tkinter
from Tkinter import DISABLED

_TITLE="This is the title"

class TkinterGui():
    def __init__(self):
        self._buildGui(_TITLE)
        self.last_row = 0

    def _buildGui(self, title):
        self._top_level  = Tkinter.Tk()
        self._top_level.title(title)

        Cat1(self._top_level, label="Cat 1", row=1)
        Cat2(self._top_level, label="Cat 2.1", row=4)
        Cat2(self._top_level, label="Cat 2.2", row=7)

class Cat1:
    def __init__(self, parent, label=None, row=0):
        self._label  = Tkinter.Label(parent, text=label)
        self._size   = Tkinter.Text(parent, state=DISABLED, height=1, width=10, background="bisque")
        self._status = Tkinter.Text(parent, state=DISABLED, height=3, width=72, background="bisque")
        self._model  = Tkinter.Text(parent, state=DISABLED, height=1, width=30, background="bisque")
        self._serial = Tkinter.Text(parent, state=DISABLED, height=1, width=30, background="bisque")

        self._label.grid( row=row,   column=0, sticky="nsew")
        self._size.grid(  row=row,   column=1, sticky="nsew")
        self._status.grid(row=row,   column=2, rowspan=3, sticky="nsew")
        self._model.grid( row=row+1, column=0, columnspan=2, sticky="nsew")
        self._serial.grid(row=row+2, column=0, columnspan=2, sticky="nsew")

class Cat2:        
    def __init__(self, parent, label=None, row=0):
        self._control = Tkinter.IntVar()

        self._label    = Tkinter.Label(parent, text=label)
        self._size     = Tkinter.Text(parent, state=DISABLED, height=1, width=10, background="bisque")
        self._status   = Tkinter.Text(parent, state=DISABLED, height=3, width=72, background="bisque")
        self._model    = Tkinter.Text(parent, state=DISABLED, height=1, width=30, background="bisque")
        self._serial   = Tkinter.Text(parent, state=DISABLED, height=1, width=30, background="bisque")
        self._enabled  = Tkinter.Radiobutton(parent, variable=self._control, text="Enabled", value=1)
        self._disabled = Tkinter.Radiobutton(parent, variable=self._control, text="Disabled", value=0)

        self._label.grid(   row=row,   column=0, sticky="nsew")
        self._size.grid(    row=row,   column=1, sticky="nsew")
        self._status.grid(  row=row,   column=2, rowspan=3, sticky="nsew")
        self._model.grid(   row=row+1, column=0, columnspan=2, sticky="nsew")
        self._serial.grid(  row=row+2, column=0, columnspan=2, sticky="nsew")
        self._enabled.grid( row=row,   column=3, sticky="nsew")
        self._disabled.grid(row=row+1, column=3, sticky="nsew")


if __name__ == "__main__":
    gui=TkinterGui()
    gui._top_level.mainloop()

撰写回答