创建布局时使用 pack 的具体问题

Specific issue using pack while creating a layout

我必须使用 tkinter 库来创建 GUI。

我有这个代码:

# -*- coding: UTF-8 -*-
import tkinter as tk

class Application(tk.Frame):

    resx=1600
    resy=900

    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.pack(fill="both", expand=1)
        self.createWidgets()
        master.minsize(self.resx, self.resy)
        master.maxsize(self.resx, self.resy)

    def createWidgets(self):
        self.hi_there = tk.Button(self)
        self.hi_there["text"] = "Create new window"
        self.hi_there["command"] = self.PlayMode
        self.hi_there.pack(side="top")

    def ShowMenu(self, master):
        print("Here I need DELETE master, in my case PlayM")

    def PlayMode(self):
        PlayM = tk.Tk()
        PlayM.minsize(self.resx, self.resy)
        PlayM.maxsize(self.resx, self.resy)
        PlayM.title("Game")

        bf= tk.Frame(PlayM, bg="blue")
        bf.pack(side="bottom", fill=tk.X, expand = 1)
        lbTEST=tk.Label(bf)
        lbTEST["text"] = "TESTING"
        lbTEST.pack()

        mf = tk.Frame(PlayM,bg="red")
        mf.pack(side="right", fill=tk.Y, expand=1)
        self.LogOut = tk.Button(mf)
        self.LogOut["text"] = "LOGOUT"
        self.LogOut.pack()
        self.LogOut["command"] = self.ShowMenu(PlayM)

root = tk.Tk()
app = Application(master=root)
app.master.title("Useless think")
app.mainloop()

我需要这样的图片:

我不知道为什么我的代码不起作用。当我打包 bf(底部框架)并设置 side = "bottom" 时,它出现在屏幕中间。为什么? 当我打包 mf (菜单框架)

时与 side = "right" 相同

我还有一个问题。关于注销按钮。我设置命令的方法 "ShowMenu"

当我 运行 我的代码时,这个方法只自动启动一次,但是当我点击按钮时没有任何反应。为什么?

你使用 pack 方法而不是 grid 有什么具体原因吗?

您可以像这样配置您的应用程序框架:

self.grid_rowconfigure(0, weight=1)    #make the first "upper" row expand
self.grid_rowconfigure(1, weight=0)    #leave the second "lower" row and do not expand it

self.grid_columnconfigure(0, weight=0) # first column, do not expand
self.grid_columnconfigure(1, weight=1) # second columnd, DO expand
self.grid_columnconfigure(0, weight=0) # third column, do not expand

在您的 mainframe/application class 上,然后致电:

bf.grid(row=1, column=0, columnspan=3, sticky=tk.NW+tk.SE) # span over three columns
lf.grid(row=0, column=0, sticky=tk.NW+tk.SE) # default is to span over one column
rf.grid(row=0, column=2, sticky=tk.NW+tk.SE)

编辑

非常抱歉,我忘记提到注销按钮了。 您在绑定时调用事件处理程序,因此它将在那里执行。

如果您想让它传递值,请使用:

  • command=lambda parent=PlayM: self.ShowMenu(parent)
  • 使用 class 对象在创建时存储你的父引用 self._Parent = PlayM 并在 ShowMenu
  • 中使用它

我个人更喜欢为单个值存储对象。如果你有很多 windows 要销毁,我会使用 lambda。

首先,您的代码存在严重缺陷。您不应创建多个 Tk 实例。如果您需要创建额外的 windows,请创建 Toplevel.

的实例

When I pack my bf (bottom frame) and set side = "bottom", but it appears in the middle of the screen. Why?

您将 mfmfexpand 设置为 1,因此每个人最终都会占用 [=] 中可用 space 的一半51=]。如果您只是将 expand 设置为 0(零)或将 bf 设置为 False,它只会占用必要的 space。

bf.pack(side="bottom", fill=tk.X, expand = 0)

作为一般规则,您应该只在单个小部件("hero" 小部件)上为 expand 设置真值,除非您希望将额外的 space 平均分配给小部件。

When I run my code, this method is started automatically only once, but when I click to button nothing happens. Why?

因为您要告诉 运行。看看这段代码:

self.LogOut["command"] = self.ShowMenu(PlayM)

和这段代码完全一样:

result = self.ShowMenu(PlayM)
self.logOut["command"] = result

看到问题了吗?

command 属性需要对函数的 引用。粗略翻译一下,就是不能用()。如果您需要向命令传递参数,典型的解决方案是使用 functools.partiallambda 创建对匿名函数的引用,该函数将使用参数调用真实函数:

self.logOut["command"] = lambda arg=PlayM: self.ShowMenu(arg)

除了Bryan Oakley已经提到的问题和R4PH43L使用grid给出的解决方案,这里有一个可能仅使用 pack 的布局解决方案。

这个想法是,通过使用 side="left"(或 side="right")然后使用 side="top"(或 side="bottom")并不像您期望的那样工作。 在一个框架内,您应该只使用 side 的值(在同一框架中打包小部件时),这些值可以是垂直的("top""bottom")或水平的("right""left"),但不是 两者。因此,要仅使用 pack 完成像您这样的布局,您需要额外的框架!

# -*- coding: UTF-8 -*-
import tkinter as tk

class Application(tk.Frame):

    resx=400
    resy=200

    def __init__(self, master=None):
        tk.Frame.__init__(self, master, bg="red")
        self.pack(fill="both", expand=1)
        self.create_widgets()
        master.geometry("400x200+100+500")

    def create_widgets(self):
        self.hi_there = tk.Button(self)
        self.hi_there["text"] = "Create new window"
        self.hi_there["command"] = self.play_mode
        self.hi_there.pack(side="top")

    def show_menu(self, master):
        print("Here I need DELETE master, in my case play_m")

    def play_mode(self):
        # Using a Toplevel instead of a Tk
        # You should have just one Tk in your app
        play_m = tk.Toplevel()
        play_m.geometry("400x200+100+500")
        play_m.title("Game")

        top_frame = tk.Frame(play_m, bg="blue")       
        top_frame.pack(side="top", fill="both", expand=True)

        left_top_frame = tk.Frame(top_frame, bg="white")
        left_top_frame_label = tk.Label(left_top_frame, text="left top frame")
        left_top_frame_label.pack()
        left_top_frame.pack(side="left", fill="y")

        middle_top_frame = tk.Frame(top_frame, bg="black")

        middle_top_frame_button = tk.Button(middle_top_frame, text="Logout", command=play_m.destroy)
        middle_top_frame_button.pack()
        middle_top_frame.pack(side="left", fill="both", expand=True)

        right_top_frame = tk.Frame(top_frame, bg="white")
        right_top_frame_label = tk.Label(right_top_frame, text="right top frame")
        right_top_frame_label.pack()
        right_top_frame.pack(side="right", fill="y")

        bottom_frame = tk.Frame(play_m, bg="yellow")
        bottom_frame_label = tk.Label(bottom_frame, text="bottom frame")
        bottom_frame_label.pack()        
        bottom_frame.pack(side="top", fill="both")


root = tk.Tk()
app = Application(master=root)
app.master.title("Useless think")
app.mainloop()

这是 Tk window 在 OS X (Sierra) 上的主要结果:

第二个顶层

为了便于说明,我稍微更改了尺寸。我还重命名了使用 _ 和小写字母的方法。