我最初的方法基于以下示例:
Best way to structure a tkinter application?
我正在尝试创建一个更复杂的应用程序,其中包含多个子应用程序,每个子应用程序都显示在一个单独的顶层窗口中。所有子应用程序都需要能够在彼此之间交换信息。因此,我打算在主应用程序中使用类变量来实现这一需求。
此刻我被困在一点上:
我不知道如何在创建子应用程序类示例后正确地实现对子应用程序类示例的销毁。
“app_one”窗口只能打开一次,然后可以正确关闭。当然,如果窗口第二次打开,则会出现错误,因为只有“Toplevel”窗口示例被破坏,而子应用程序类示例本身未被破坏。
如何在关闭“顶级”窗口时正确实现子应用程序类示例的销毁?
除此之外,我也非常感谢任何其他关于如何改进我的模板结构的建议如下“Python”以及“tkinter”最佳实践范例:
import tkinter
import tkinter.filedialog
class main_app(tkinter.Tk):
root = None
var_one = None
sub_app_one_instance = None
sub_app_two_instance = None
sub_app_thr_instance = None
def __init__(self):
super().__init__()
main_app.root = self # <<< in order to be able to refer to root window in other classes
main_app.var_one = 99 # <<< class variables in order to exchange data between main app and sub apps
main_app.sub_app_one_instance = None
main_app.sub_app_two_instance = None
main_app.sub_app_thr_instance = None
self.create_tkinter_interface_main_app()
def create_tkinter_button(self, button_destination, button_text, button_width):
tkinter_button = tkinter.Button(button_destination, text=button_text, width=button_width)
return tkinter_button
def create_tkinter_label(self, label_destination, label_text):
tkinter_label = tkinter.Label(label_destination, text=label_text)
return tkinter_label
def create_tkinter_interface_main_app(self):
frame_main = tkinter.Frame(self)
frame_sub_one = tkinter.Frame(frame_main, bg="red")
frame_sub_two = tkinter.Frame(frame_main, bg="green")
frame_sub_thr = tkinter.Frame(frame_main, bg="blue")
label_app = self.create_tkinter_label(frame_main, "application")
label_one = self.create_tkinter_label(frame_sub_one, "menu one")
label_two = self.create_tkinter_label(frame_sub_two, "menu two")
label_thr = self.create_tkinter_label(frame_sub_thr, "menu thr")
button_one = self.create_tkinter_button(frame_sub_one, "app_one", 20)
button_one.configure(command=self.sub_app_one_open)
button_two = self.create_tkinter_button(frame_sub_one, "app_two", 20)
label_app.pack(side=tkinter.TOP, fill=tkinter.X, expand=tkinter.NO)
frame_sub_one.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.YES)
frame_sub_two.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.YES)
frame_sub_thr.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.YES)
label_one.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10)
button_one.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10)
button_two.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10)
label_two.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10)
label_thr.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10)
frame_main.pack(fill=tkinter.BOTH, expand=tkinter.TRUE)
def sub_app_one_open(self):
if not main_app.sub_app_one_instance == None:
main_app.sub_app_one_instance.sub_app_one_move_to_front()
return None
else:
main_app.sub_app_one_instance = sub_app_one()
class sub_app_one(main_app): # <<< inherit from main application in order to be able to access class variables
def __init__(self):
self.sub_app_one_toplevel_instance = tkinter.Toplevel(main_app.root)
self.sub_app_one_toplevel_instance.protocol("WM_DELETE_WINDOW", self.sub_app_one_close_toplevel)
print(main_app.var_one) # <<< access information from main app
def sub_app_one_move_to_front(self):
self.sub_app_one_toplevel_instance.attributes("-topmost", 0x01) # <<< set window state
self.sub_app_one_toplevel_instance.attributes("-topmost", 0x00) # <<< reset window state
def sub_app_one_close_toplevel(self):
self.sub_app_one_toplevel_instance.destroy()
self.sub_app_one_toplevel_instance = None
if __name__ == "__main__":
main_app_instance = main_app()
main_app_instance.mainloop()
2条答案
按热度按时间nszi6y051#
〈〈〈从主应用程序继承,以便能够访问类变量
这不是继承的工作方式。如果
sub_app_one
需要访问主应用程序中的变量,它应该通过main_app
的示例获得它们。你绝对不应该从main_app
继承。当你使用继承的时候,你是说子类 * 是一个 * 主类。意思是,你最终得到了两个主类。一个是原来的,一个和原来的完全一样,只是做了一些修改。这意味着你最终得到了
tk.Tk
的两个示例,这不是tkinter的设计使用方式。你可以做到,但是行为并不直观。也很少是正确的选择。你应该做的是在创建
main_app
的示例时将其传递到你的sub_app_one
中,它看起来像这样:至于删除窗口的处理,最简单的解决方案是将子类设为
tk.Toplevel
的子类,这样,就可以使用tkinter内置的事件处理来了解小部件何时被销毁。下面是一个工作示例:
bvjxkvbb2#
在过去的几周里,我一直在处理这样的应用程序,下面是我可以给出的一些建议。我将把我的答案分为两部分:
1.我自己的代码中的一些示例
1.如果这是一个长期项目,建议将很有帮助
1.管理GUI
我建议你有一个主类来管理你的应用程序。这个类把你的应用程序的所有框架/页面放在彼此的顶部,并把你需要的提升到顶部。我为每个框架都有一个单独的文件,因为每个框架的代码都可能很长。这是'Main.py'文件:
您有一个管理GUI的"main"文件。现在您必须创建以后将成为应用程序页面的框架。最佳做法: 将它们分别保存在单独的文件中,然后导入www.example.com!以下是www.example.com文件示例:Main.py! This is an example Frame.py file:
根据需要对任意多的帧重复此操作。然后,您的目录中应包含以下文件:
Main.py, Frame1.py, Frame2.py, Frame3.py, etc.
尝试运行www.example.com文件! GUI现在看起来仍然很无聊,但您将用自己的代码填充每个框架。Main.py file! The GUI still looks boring right now, but you will fill each frame with your own code.
1.破坏框架
假设你有一个只想使用一次的起始页,它在使用后应该被销毁。让我们的'Frame1.py'文件作为起始页。它的代码应该如下所示:
如果单击起始页上的"自毁"按钮,此框架将被正确销毁。但是,您仍然可以看到在"Main.py"中定义的"框架1"按钮,即使单击它没有产生任何结果。返回到"Main.py"文件并删除定义此按钮的部分。无论如何,您不需要它。因为当您启动应用程序时,起始页将被行'self.show_frame1()'提升到顶部。
我希望这对你的问题有所帮助。下面是我想给你的一些其他建议:
1.文件结构
一个好的文件结构可以帮助你对你的代码有一个总体的了解,并且会使修复错误变得更加容易。你可能需要一个类似于下面的目录:
为什么要为应用程序的每个框架使用文件夹?当你为每个框架编写代码时,你可能会开始用子框架填充每个框架。每个子框架都有自己的功能,可能需要大量的代码。因此,我会为你使用的每个major子框架定义一个类,用大量的代码来描述它。如果你有多个这样的子框架类,你甚至可能想要在frame文件夹中有一个"subframe"文件夹。2你也可以把那些只被这个frame使用的文件放在frame文件夹中。3假设你必须执行一些复杂的计算并定义一个类来完成它。4你可以把这个类保存在frame文件夹中的一个单独的文件中。
1.快速GUI加载
试着不要让你的电脑在启动程序时加载一堆不必要的东西。最好的情况是它只需要打包你所有的页面/框架和它们的按钮。不要让它加载巨大的图片文件或任何类似的东西。让它通过点击按钮来完成这些事情。
1.漂亮不会得奖的
......至少这里的情况可能不是这样。我不会花太多时间在继续之前完美地格式化每个按钮,因为你可能会一直移动东西。一旦你把所有需要的元素打包到框架中,你就可以考虑格式化了。
这就是我现在要分享的。如果你有任何后续问题,请随时提问!
贝斯特安塞尔