2017-07-26 69 views
3

我有一个问题,在tkinter中彼此堆叠'Pages'。在tkinter中彼此不堆叠的帧

我有一个主要的Frame包含两个子帧,其中包含不同的信息。第一个子帧包含一个Listbox和一对按钮,并打包在主框架的左侧。第二帧应该包含不同的'页面'(现在两个),并让它们填满整个框架。 我的问题是,两个'页'并排显示,而不是彼此顶部。

import tkinter as tk 


class Settings(tk.Tk): 

    def __init__(self, master=None): 
     tk.Tk.__init__(self, master) 
     self.focus_force() 
     self.grab_set() 
     # set focus to settings window 
     # Main window title 
     self.title("Settings") 


     # set up grid containers 
     container_main = tk.Frame(self, width=500, height=700) 
     container_main.pack(side='top', fill='both', expand=True) 
     container_main.grid_rowconfigure(0, weight=1) 
     container_main.grid_columnconfigure(0, weight=1) 

     container_listbox = tk.Frame(container_main, bg='blue', width=200, height=700) 
     container_listbox.pack(side='left', fill='both', expand=True) 
     container_listbox.grid_rowconfigure(0, weight=1) 
     container_listbox.grid_columnconfigure(0, weight=1) 

     container_settings = tk.Frame(container_main, bg='red', width=300, height=700) 
     container_settings.pack(side='right', fill='both', expand=True) 
     container_settings.grid_rowconfigure(0, weight=1) 
     container_settings.grid_columnconfigure(0, weight=1) 

     # build settings pages 
     self.frames = {} 

     self.frames["Options"] = Options(parent=container_listbox, controller=self) 
     self.frames["General"] = General(parent=container_settings, controller=self) 
     self.frames["Future"] = Future(parent=container_settings, controller=self) 

如果我发现这两条线。我得到一个错误,说我不能在里面使用几何管理器网格。

 # self.frames["General"].grid(row=0, column=0, sticky='nsew') 
     # self.frames["Future"].grid(row=0, column=0, sticky='nsew') 

def show_frame(self, page_name): 
     frame = self.frames[page_name] 
     frame.tkraise() 


class Options(tk.Frame): 

    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(parent, text='List Box') 
     label.grid(row=0, column=0, sticky='nsew', padx=1, pady=1) 
     button1 = tk.Button(parent, text='General', command=lambda: controller.show_frame('General')) 
     button2 = tk.Button(parent, text='Future', command=lambda: controller.show_frame('Future')) 
     button1.grid(row=1, column=0, sticky='ew') 
     button2.grid(row=2, column=0, sticky='ew') 


class General(tk.Frame): 
    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(parent, text='General') 
     label.pack(side='left', fill='both', expand=True,) 
     print("Hi I'm General") 

class Future(tk.Frame): 
    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(parent, text='Future') 
     label.pack(side='left', fill='both', expand=True) 
     print("Hi I'm Future") 

app = Settings() 
app.mainloop() 

这两个'页'被初始化并显示在同一时间,这是有道理的。因为frame.tkraise()应该是这样做,但不是这样,我只是不知道如何使其中一个上升。我还希望能够在页面上或页面上做grid_forget(),以避免将来可能意外地将值输入到隐藏的输入框中。

编辑:如果我注释掉'未来'页面,那么'一般'页面将占用整个框架空间,因此使用grid_forget()我会得到相同的结果。我只是不知道我会在哪里,但grid_forget(),然后还会在哪里重新配置或拨打grid()

回答

2

我的问题是两个'页面'是并排显示,而不是彼此顶部显示。

如果使用pack()把你的根窗口上的框架,然后用grid()该框架内,然后它会工作,但如果您尝试使用pack()框架内,然后尝试用grid()的里面同一帧它会失败。

对于根窗口和框架也是如此。如果在根窗口中有pack()框架,则不能使用grid()将任何内容放入同一个根窗口。

grid() VS pack()问题的问题,是因为其中类General和类Future被配置标签控件的位置在何处正在使用pack()父框架。这阻止了在同一父框架中使用grid()来放置常规框架和未来框架。

为了解决这个问题,我们改变:

label = tk.Label(parent, text='General') 

and 

label = tk.Label(parent, text='Future') 

到:

label = tk.Label(self, text='General') 

and 

label = tk.Label(self, text='Future') 

以上是正常工作所需要的这个唯一的解决方法。

import tkinter as tk 


class Settings(tk.Tk): 

    def __init__(self, master=None): 
     tk.Tk.__init__(self, master) 
     self.focus_force() 
     self.grab_set() 
     # set focus to settings window 
     # Main window title 
     self.title("Settings") 

     container_main = tk.Frame(self, width=500, height=700) 
     container_main.pack(side='top', fill='both', expand=True) 
     container_main.grid_rowconfigure(0, weight=1) 
     container_main.grid_columnconfigure(0, weight=1) 

     container_listbox = tk.Frame(container_main, bg='blue', width=200, height=700) 
     container_listbox.pack(side='left', fill='both', expand=True) 
     container_listbox.grid_rowconfigure(0, weight=1) 
     container_listbox.grid_columnconfigure(0, weight=1) 

     container_settings = tk.Frame(container_main, bg='red', width=300, height=700) 
     container_settings.pack(side='right', fill='both', expand=True) 
     container_settings.grid_rowconfigure(0, weight=1) 
     container_settings.grid_columnconfigure(0, weight=1) 

     self.frames = {} 

     self.frames["Options"] = Options(parent=container_listbox, controller=self) 
     self.frames["General"] = General(parent=container_settings, controller=self) 
     self.frames["Future"] = Future(parent=container_settings, controller=self) 


     self.frames["General"].grid(row=0, column=0, sticky='nsew') 
     self.frames["Future"].grid(row=0, column=0, sticky='nsew') 

    def show_frame(self, page_name): 
     frame = self.frames[page_name] 
     frame.tkraise() 

class Options(tk.Frame): 

    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(parent, text='List Box') 
     label.grid(row=0, column=0, sticky='nsew', padx=1, pady=1) 
     button1 = tk.Button(parent, text='General', command=lambda: controller.show_frame('General')) 
     button2 = tk.Button(parent, text='Future', command=lambda: controller.show_frame('Future')) 
     button1.grid(row=1, column=0, sticky='ew') 
     button2.grid(row=2, column=0, sticky='ew') 


class General(tk.Frame): 
    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(self, text='General') 
     label.pack(side='left', fill='both', expand=True) 
     print("Hi I'm General") 

class Future(tk.Frame): 
    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(self, text='Future') 
     label.pack(side='left', fill='both', expand=True) 
     print("Hi I'm Future") 

app = Settings() 
app.mainloop() 
+0

解决'不能使用几何管理器网格内'问题。我仍然需要一次只显示一个'Page',例如[Bryan Oakley's](https://stackoverflow.com/a/7557028/7703610)示例减去for循环。 –

+0

您的第一个陈述略有误导。您可以在窗口中同时使用包和网格,而不仅仅是共享共同父项的小部件。您的声明几乎使听起来像是您在窗口中的任何位置使用包装,然后您无法在任何地方使用网格,这是错误的。 –

+0

这可能是你如何在框架上使用包。 –

2

我已经采取了结构调整你的应用程序逻辑来传达如何可能更容易解决你的问题的自由 - 我已经包括了一些额外的信息也是如此。

您的当前实施中存在hideshow您网页的问题,这是您的按钮的command关键字分配问题。在我提供的实现中,Options类是所有魔法发生的地方 - 具体地说,是show_generalshow_future方法。简单地说,当点击通用按钮时,我调用小部件上的pack_forget()小部件,general_page小部件上调用pack(...),反之亦然,当我想显示未来页面时。 (这个实现可以很容易地缩放,但我会留给你的。)

此外,我以一种模块化代码的方式构造应用程序逻辑,因此在每一步我都可以测试它并查看在哪里我在,例如我开始在main()创建每个容器,并确保一切正确布置(我喜欢给每个容器一个不同的颜色,使之更容易可视化间距,等等),然后移动到我想要的框架和SettingsOptions框架放置小部件,并编写GUI所需的代码以按预期运行。

我不会解释每一行,但我确信在这一点上,你可以看到我所得到的。我建议阅读代码(从main()开始), ,然后弄清楚如何以及为什么我以我所做的方式编写它。 (我写这功能我相信你意,以及在这里和那里提供了一些三分球 - 这并不意味着是完美的。)

import tkinter as tk 

class Options(tk.Frame): 

    def __init__(self, master): 

     tk.Frame.__init__(self, master, bg='#FFFFFF', relief='ridge', bd=1) 

     # Grab the 'Settings' tk.Frame object 
     settings_frame = self.master.master.winfo_children()[1].winfo_children()[0] 
     self.settings = settings_frame 

     self.options() 


    def options(self): 
     self.label = tk.Label(self, text='List Box') 
     self.button1 = tk.Button(self, text='General', command=self.show_general) 
     self.button2 = tk.Button(self, text='Future', command=self.show_future) 

     self.label.grid(row=0, sticky='nsew', padx=1, pady=1) # column is set to 0 by default 
     self.button1.grid(row=1, sticky='nsew', padx=1, pady=1) 
     self.button2.grid(row=2, sticky='nsew', padx=1, pady=1) 


    def show_general(self): 
     self.settings.future_page.pack_forget() 
     self.settings.general_page.pack(fill='both', expand=True) 


    def show_future(self): 
     self.settings.general_page.pack_forget() 
     self.settings.future_page.pack(fill='both', expand=True) 


class Settings(tk.Frame): 

    def __init__(self, master): 

     tk.Frame.__init__(self, master, bg='#FFFFFF', relief='ridge', bd=1) 

     self.pages() 


    def pages(self): 
     self.general_page = tk.Label(self, fg='#FFFFFF', bg='#FF0000', 
      relief='ridge', bd=1, text="Hi, I'm General.") 
     self.future_page = tk.Label(self, fg='#FFFFFF', bg='#0000FF', 
      relief='ridge', bd=1, text="Hi, I'm Future.") 

     self.general_page.pack(fill='both', expand=True) 


def main(): 
    root = tk.Tk() 
    root.title('Settings') 
    root.configure(bg='#DDDDDD', width=500, height=500) 
    root.resizable(width=False, height=False) 

    main_container = tk.Frame(root, bg='#FFFFFF', width=500, height=500) 
    main_container.pack(fill='both', padx=20, pady=20) # Add some padding to see container difference 
    main_container.pack_propagate(False) # Avoid sizing based on widget contents 

    listbox_left = tk.Frame(main_container, bg='#4285F4', width=235) # Take 15 from both sides for padding 
    settings_right = tk.Frame(main_container, bg='#272727', width=235) 

    listbox_left.pack(side='left', fill='y', padx=(10, 0), pady=10) 
    listbox_left.pack_propagate(False) 
    settings_right.pack(side='right', fill='y', padx=(0, 10), pady=10) 
    settings_right.pack_propagate(False) 

    settings = Settings(settings_right) # Must be instantiated before Options 
    options = Options(listbox_left) 

    settings.pack(fill='both', expand=True, padx=2, pady=2) 
    options.pack(fill='both', expand=True, padx=2, pady=2) 

    root.mainloop() 


if __name__ == '__main__': 
    main() 

我希望这有助于!

+0

_“你在正确的轨道上,但是需要有一些方法来调用这两个小部件,以便显示一个小部件,并且隐藏一个小部件。”_ - 这是不正确的。如果两个小部件堆叠在一起,您只需将其中一个放在另一个之上即可。 –

+0

@BryanOakley编辑 - 谢谢。 – flevinkelming

+0

@flevinkelming,非常感谢。我用与颜色和模块化相同的方式处理问题。我只是迷路了。并入'pack_forget()'也是我一直在寻找的,所以感谢一个简单而彻底的例子。 –