Switch between two frames in tkinter
我已经构建了我的前几个脚本,上面有一个漂亮的小图形用户界面,正如教程所示,但是没有一个脚本涉及到为更复杂的程序做什么。
如果你有"开始菜单"的功能,在打开屏幕时,当你选择用户时,你会移动到程序的另一个部分并适当地重新绘制屏幕,那么这样做的优雅方式是什么?
是否只需在"开始菜单"框中输入cx1(0),然后为另一部分创建一个新的窗口小部件?当他们按下后退按钮时,这个过程会发生逆转?
一种方法是将帧堆叠在一起,然后您可以简单地按照堆叠顺序将一个帧提升到另一个帧之上。上面的那个将是可见的那个。如果所有的框架都是相同的大小,这是最有效的,但是只要稍微做一点工作,你就可以让它与任何大小的框架一起工作。
注意:为了实现这一点,一个页面的所有小部件必须具有该页面(即:
下面是一个人为的例子,向您展示一般概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | import tkinter as tk # python 3 from tkinter import font as tkfont # python 3 #import Tkinter as tk # python 2 #import tkFont as tkfont # python 2 class SampleApp(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic") # the container is where we'll stack a bunch of frames # on top of each other, then the one we want visible # will be raised above the others container = tk.Frame(self) container.pack(side="top", fill="both", expand=True) container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) self.frames = {} for F in (StartPage, PageOne, PageTwo): page_name = F.__name__ frame = F(parent=container, controller=self) self.frames[page_name] = frame # put all of the pages in the same location; # the one on the top of the stacking order # will be the one that is visible. frame.grid(row=0, column=0, sticky="nsew") self.show_frame("StartPage") def show_frame(self, page_name): '''Show a frame for the given page name''' frame = self.frames[page_name] frame.tkraise() class StartPage(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(self, text="This is the start page", font=controller.title_font) label.pack(side="top", fill="x", pady=10) button1 = tk.Button(self, text="Go to Page One", command=lambda: controller.show_frame("PageOne")) button2 = tk.Button(self, text="Go to Page Two", command=lambda: controller.show_frame("PageTwo")) button1.pack() button2.pack() class PageOne(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(self, text="This is page 1", font=controller.title_font) label.pack(side="top", fill="x", pady=10) button = tk.Button(self, text="Go to the start page", command=lambda: controller.show_frame("StartPage")) button.pack() class PageTwo(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(self, text="This is page 2", font=controller.title_font) label.pack(side="top", fill="x", pady=10) button = tk.Button(self, text="Go to the start page", command=lambda: controller.show_frame("StartPage")) button.pack() if __name__ =="__main__": app = SampleApp() app.mainloop() |
号
如果您发现在类中创建实例的概念令人困惑,或者在构造过程中不同的页需要不同的参数,则可以分别显式调用每个类。循环主要用来说明每个类都是相同的。
例如,要单独创建类,可以删除循环(
1 2 3 4 5 6 7 | self.frames["StartPage"] = StartPage(parent=container, controller=self) self.frames["PageOne"] = PageOne(parent=container, controller=self) self.frames["PageTwo"] = PageTwo(parent=container, controller=self) self.frames["StartPage"].grid(row=0, column=0, sticky="nsew") self.frames["PageOne"].grid(row=0, column=0, sticky="nsew") self.frames["PageTwo"].grid(row=0, column=0, sticky="nsew") |
号
随着时间的推移,人们以这段代码(或者一个复制了这段代码的在线教程)为起点提出了其他问题。您可能需要阅读这些问题的答案:
- 了解tkinter_uuu init中的父级和控制器__
- 特金特!了解如何切换帧
- 如何从类中获取变量数据
- 从tkinter帧向另一帧调用函数
- 如何访问tkinter中不同类的变量?
- 我如何制作一个方法,每次在Tkinter中显示帧时运行
- t旋转框架调整大小
- tkinter有单独文件中的页面代码
- 按按钮时刷新一帧
这是另一个简单的答案,但不使用类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from tkinter import * def raise_frame(frame): frame.tkraise() root = Tk() f1 = Frame(root) f2 = Frame(root) f3 = Frame(root) f4 = Frame(root) for frame in (f1, f2, f3, f4): frame.grid(row=0, column=0, sticky='news') Button(f1, text='Go to frame 2', command=lambda:raise_frame(f2)).pack() Label(f1, text='FRAME 1').pack() Label(f2, text='FRAME 2').pack() Button(f2, text='Go to frame 3', command=lambda:raise_frame(f3)).pack() Label(f3, text='FRAME 3').pack(side='left') Button(f3, text='Go to frame 4', command=lambda:raise_frame(f4)).pack(side='left') Label(f4, text='FRAME 4').pack() Button(f4, text='Goto to frame 1', command=lambda:raise_frame(f1)).pack() raise_frame(f1) root.mainloop() |
在
我已经修改了布莱恩·奥克利的回答,在替换旧的框架之前先把它销毁。作为一个额外的好处,这消除了对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | # Multi-frame tkinter application v2.3 import tkinter as tk class SampleApp(tk.Tk): def __init__(self): tk.Tk.__init__(self) self._frame = None self.switch_frame(StartPage) def switch_frame(self, frame_class): """Destroys current frame and replaces it with a new one.""" new_frame = frame_class(self) if self._frame is not None: self._frame.destroy() self._frame = new_frame self._frame.pack() class StartPage(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10) tk.Button(self, text="Open page one", command=lambda: master.switch_frame(PageOne)).pack() tk.Button(self, text="Open page two", command=lambda: master.switch_frame(PageTwo)).pack() class PageOne(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10) tk.Button(self, text="Return to start page", command=lambda: master.switch_frame(StartPage)).pack() class PageTwo(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10) tk.Button(self, text="Return to start page", command=lambda: master.switch_frame(StartPage)).pack() if __name__ =="__main__": app = SampleApp() app.mainloop() |
。
号
解释- 删除旧的
_frame ,如果它存在,则用新的帧替换它。 - 添加了
.pack() 的其他帧(如menubars)将不受影响。 - 可用于实现
tkinter.Frame 的任何类。 - 窗口自动调整大小以适应新内容
版本历史记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | v2.3 - Pack buttons and labels as they are initialized v2.2 - Initialize `_frame` as `None`. - Check if `_frame` is `None` before calling `.destroy()`. v2.1.1 - Remove type-hinting for backwards compatibility with Python 3.4. v2.1 - Add type-hinting for `frame_class`. v2.0 - Remove extraneous `container` frame. - Application now works with any generic `tkinter.frame` instance. - Remove `controller` argument from frame classes. - Frame switching is now done with `master.switch_frame()`. v1.6 - Check if frame attribute exists before destroying it. - Use `switch_frame()` to set first frame. v1.5 - Revert 'Initialize new `_frame` after old `_frame` is destroyed'. - Initializing the frame before calling `.destroy()` results in a smoother visual transition. v1.4 - Pack frames in `switch_frame()`. - Initialize new `_frame` after old `_frame` is destroyed. - Remove `new_frame` variable. v1.3 - Rename `parent` to `master` for consistency with base `Frame` class. v1.2 - Remove `main()` function. v1.1 - Rename `frame` to `_frame`. - Naming implies variable should be private. - Create new frame before destroying old frame. v1.0 - Initial version. |