如何从启动页面在不同的 tkinter 画布之间切换并返回子画布中的启动页面
Posted
技术标签:
【中文标题】如何从启动页面在不同的 tkinter 画布之间切换并返回子画布中的启动页面【英文标题】:How to switch between different tkinter canvases from a start-up page and return back to start-up page in sub-canvas 【发布时间】:2020-11-10 23:52:02 【问题描述】:我创建了一个start-up canvas,其中包含可调整到其他两个子画布的按钮。此外,在这两个子画布中,创建了返回启动画布的按钮。但是,在我输入sub-canvas 后,我无法返回到启动画布。也就是说,当我点击按钮返回启动画布时,它会在子画布旁边创建一个启动画布,而不是关闭子画布并切换到启动画布。有没有办法在画布之间切换并返回主画布?谢谢!
import tkinter as tk
from tkinter import ttk
import re
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._Canvas= None
self.switch_Canvas(StartUpPage)
def switch_Canvas(self, Canvas_class):
new_Canvas = Canvas_class(self)
if self._Canvas is not None:
self._Canvas.destroy()
self._Canvas = new_Canvas
self._Canvas.pack()
class StartUpPage(tk.Canvas):
def __init__(self, master, *args, **kwargs):
tk.Canvas.__init__(self, master, *args, **kwargs)
tk.Frame(self)
tk.Label(self, text="Example").grid(column = 0, row = 0)
tk.Button(self, text="Canvas1",
command=lambda: master.switch_Canvas(PageOne)).grid(column = 0, row = 1)
tk.Button(self, text="Canvas2",
command=lambda: master.switch_Canvas(PageTwo)).grid(column = 0, row = 2)
class PageOne(tk.Canvas, tk.Tk):
def __init__(self, master, *args, **kwargs):
root = tk.Canvas.__init__(self, *args, **kwargs)
self.frame1 = tk.Frame(root, width=430)
self.frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
tk.Label(self.frame1, text="First Canvas").pack(side="top", fill="x", pady=5)
tk.Button(self.frame1, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage)).pack()
class PageTwo(tk.Canvas, tk.Tk):
def __init__(self, master, *args, **kwargs):
root = tk.Canvas.__init__(self, *args, **kwargs)
self.frame2 = tk.Frame(root, width=430)
self.frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
tk.Label(self.frame2, text="Second Canvas").pack(side="top", fill="x", pady=5)
tk.Button(self.frame2, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage)).pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
【问题讨论】:
“我无法返回启动画布”是什么意思?它是如何失败的?程序会崩溃吗?它会切换到错误的画布吗?它会抛出错误吗?另外,您发布的代码中存在缩进错误,请您修复它吗? Indent onself._Canvas.destroy()
运行代码后,我猜你想在画布之间切换(并首先创建它们),但代码实际上是在每次按钮调用时创建新画布,我猜对了吗?
谢谢!我已经修复了缩进错误。我的问题就像 Space 提到的那样。我无法返回到启动画布。相反,我将继续在我的子画布旁边创建启动画布。
解决方案似乎很明显:不要在函数中创建画布类的新实例。相反,在函数之外创建一次。你试过吗?
谢谢@BryanOakley!我真的不知道如何将画布类放在函数之外。你能给我解释一下吗?谢谢!我在每个子画布中都内置了几个框架。我想在启动画布的不同子画布之间切换。
【参考方案1】:
所以,有几件事:
第一:在PageOne
和PageTwo
中有错误的父/主关系
def __init__(self, master, *args, **kwargs):
由于您是从 tk.Canvas
继承的,因此该类的新实例具有父 master
,它本身就是从中传递的
SampleApp
's switchCanvas
as Canvas_class(self)
意思是 master
是 .
或(主/根)窗口。
但是(我们仍然在 PageOne
内部)你正在使 root = tk.Canvas.__init__(self, *args, **kwargs)
变成 None
,因为初始化程序返回 None
。
然后您将创建一个新的Frame
,其父级为root
,即None
。
总之
PageOne
和 PageTwo
是(也是)Canvas
的新实例,其父窗口是主窗口,或者是由 SampleApp
创建的实例。
root
又名Frame
的父级是None
(当你将它定位为self.frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
时,我什至不知道它是如何影响它的)
因此,我假设您正在尝试创建 Frame
的 PageOne
和 PageTwo
实例,并在其中放入 Canvas
。
这是我对这两个类所做的一些更改:它们的实例现在都属于(类型)tk.Frame
,其中包含其他小部件。
class PageTwo(tk.Frame): # Sub-lcassing tk.Frame
def __init__(self, master, *args, **kwargs):
# self is now an istance of tk.Frame
tk.Frame.__init__(self,master, *args, **kwargs)
# make a new Canvas whose parent is self.
self.canvas = tk.Canvas(self,bg='yellow', width=430)
self.label = tk.Label(self, text="Second Canvas").pack(side="top", fill="x", pady=5)
self.button = tk.Button(self, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage))
self.button.pack()
# pack the canvas inside the self (frame).
self.canvas.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
#print('is instance',isinstance(self,tk.Frame))
第二:跟踪创建了哪些实例/对象,而不是不断产生新的实例/对象。我决定保持简单并创建一个内部dict
ionary,其键是StratUpPage
、PageOne
或PageTwo
的类,这意味着每个类都有一个可以切换的实例到。
def switch_Canvas(self, Canvas_class):
# Unless the dictionary is empty, hide the current Frame (_mainCanvas is a frame)
if self._mainCanvas:
self._mainCanvas.pack_forget()
# Modification 2: is the Class type passed is a one we have seen before?
canvas = self._allCanvases.get(Canvas_class, False)
# if Canvas_class is a new class type, canvas is False
if not canvas:
# Instantiate the new class
canvas = Canvas_class(self)
# Store it's type in the dictionary
self._allCanvases[Canvas_class] = canvas
# Pack the canvas or self._mainCanvas (these are all frames)
canvas.pack(pady = 60)
# and make it the 'default' or current one.
self._mainCanvas = canvas
完整代码
import tkinter as tk
from tkinter import ttk
import re
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._mainCanvas= None
# The dictionary to hold the class type to switch to
# Each new class passed here, will only have instance or object associated with it (i.e the result of the Key)
self._allCanvases = dict()
# Switch (and create) the single instance of StartUpPage
self.switch_Canvas(StartUpPage)
def switch_Canvas(self, Canvas_class):
# Unless the dictionary is empty, hide the current Frame (_mainCanvas is a frame)
if self._mainCanvas:
self._mainCanvas.pack_forget()
# is the Class type passed one we have seen before?
canvas = self._allCanvases.get(Canvas_class, False)
# if Canvas_class is a new class type, canvas is False
if not canvas:
# Instantiate the new class
canvas = Canvas_class(self)
# Store it's type in the dictionary
self._allCanvases[Canvas_class] = canvas
# Pack the canvas or self._mainCanvas (these are all frames)
canvas.pack(pady = 60)
# and make it the 'default' or current one.
self._mainCanvas = canvas
class StartUpPage(tk.Canvas):
def __init__(self, master, *args, **kwargs):
tk.Canvas.__init__(self, master, *args, **kwargs)
tk.Frame(self) # Here the parent of the frame is the self instance of type tk.Canvas
tk.Label(self, text="Example").grid(column = 0, row = 0)
tk.Button(self, text="Canvas1",
command=lambda: master.switch_Canvas(PageOne)).grid(column = 0, row = 1)
tk.Button(self, text="Canvas2",
command=lambda: master.switch_Canvas(PageTwo)).grid(column = 0, row = 2)
class PageOne(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self,master, *args, **kwargs)
self.canvas = tk.Canvas(self,bg='blue', width=430)
print('got',self,master,args,kwargs)
tk.Label(self, text="First Canvas").pack(side="top", fill="x", pady=5)
tk.Button(self, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage)).pack()
self.canvas.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
class PageTwo(tk.Frame): # Sub-lcassing tk.Frame
def __init__(self, master, *args, **kwargs):
# self is now an istance of tk.Frame
tk.Frame.__init__(self,master, *args, **kwargs)
# make a new Canvas whose parent is self.
self.canvas = tk.Canvas(self,bg='yellow', width=430)
self.label = tk.Label(self, text="Second Canvas").pack(side="top", fill="x", pady=5)
self.button = tk.Button(self, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage))
self.button.pack()
# pack the canvas inside the self (frame).
self.canvas.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
#print('is instance',isinstance(self,tk.Frame))
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
编辑:视觉演示
【讨论】:
以上是关于如何从启动页面在不同的 tkinter 画布之间切换并返回子画布中的启动页面的主要内容,如果未能解决你的问题,请参考以下文章
如何设置 Python tkinter 画布元素的 z 索引?