[英]Are there any ways to bind the enter key to a Tkinter.Toplevel() window in Python?
[英]Tkinter.TopLevel; passing a function from another class?
我当前正在编写一个小程序,我想先打开一个主菜单,此主菜单上的按钮会打开一个单独的窗口。 为了将这个主菜单和单独的窗口放在一起(它们都是类),我制作了第三个容器类,如下所示:
class1(tk.TopLevel):
def __init__(self, arg1):
#make button with arg1 function as the attached command
class2:
....
def arg1(self):
#initialize main app
class3:
def __init__(self):
class2()
class1(arg1)
我面临的问题是我不希望在按下按钮之前打开2类窗口。 有没有办法做到这一点? 对于顶层,总是有self.withdraw,我打算在需要时按下按钮后用来删除主菜单。
我在想(请告诉我这听起来是否合理)是让主菜单类具有抽象功能,而“容器”类充当中间人并在其中具有创建和销毁class1对象的方法(主应用程序)调用时。 这样,我便会将此方法附加到主菜单中。
我可以获取有关如何解决此问题的帮助/反馈吗?
如果我对您的程序逻辑的理解是正确的-您具有“游戏”设计,其中class1
充当“ 主菜单” ,而class2
模仿“游戏” ,则在我们完成“主菜单”之后开始。 您使用第三类的想法是合理的,但是我发现这种方法有点麻烦。 无论如何,让我们先坚持您想要的布局,然后再尝试类似的方法。 另请注意,由于您将class2
视为“主应用程序”,因此我会将您的class2
视为tk.Tk()
。
这是您所需的布局。 让我们尝试编写一些简单的代码,然后再讨论它:
# imports
try:
import tkinter as tk
import tkinter.simpledialog as sd
import tkinter.messagebox as msg
except ImportError:
import Tkinter as tk
import tkSimpleDialog as sd
import tkMessageBox as msg
import random as rnd
# classes
class Class1(tk.Toplevel):
def __init__(self, master, arg_function):
tk.Toplevel.__init__(self, master)
self.minsize(350, 200)
# make button with arg_function as the attached command
# lets hold our function first
self.function_to_execute = arg_function
# define widgets
self.main_frame = tk.Frame(self)
self.function_label = tk.Label(self, text='Function is %s\n Let\'s try to call it?' % arg_function.__name__)
self.execute_button = tk.Button(self, text='Execute %s and Quit' % arg_function.__name__,
command=self.execute_and_quit)
# pack stuff
self.main_frame.pack(fill='both', expand=True)
self.function_label.pack(fill='both', expand=True)
self.execute_button.pack(fill='x')
# handle closing
self.protocol('WM_DELETE_WINDOW', lambda: self.execute_and_quit(False))
def execute_and_quit(self, execute=True):
info = None
if callable(self.function_to_execute) and execute:
info = self.function_to_execute()
self.destroy()
self.master.renew(info)
class Class2(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.minsize(350, 200)
# define widgets
self.main_frame = tk.Frame(self)
self.user_info_label = tk.Label(self)
self.quit_button = tk.Button(self, text='Quit', command=self.destroy)
# pack stuff
self.main_frame.pack(fill='both', expand=True)
self.user_info_label.pack(fill='both', expand=True)
self.quit_button.pack(fill='x')
# let's hide our app on initialisation
self.withdraw()
def renew(self, info_to_renew=None):
self.deiconify()
if info_to_renew is None or info_to_renew == '':
self.user_info_label['text'] = 'Your person is unknown'
else:
if type(info_to_renew) is str:
self.user_info_label['text'] = 'Hello, %s! Although there\'s nothing more about you...' % info_to_renew
elif info_to_renew:
self.user_info_label['text'] = 'Gosh! You\'re filthy drunkard!'
else:
self.user_info_label['text'] = 'Are you building your drink refusal skills?'
class Class3:
def __init__(self):
# hold functions
self.arg_functions_to_choose_from = [self.ask_for_name, self.ask_for_drunkenness]
# hold our windows
self.main_app = Class2()
self.main_menu = Class1(self.main_app, self.pick_random_function())
self.main_app.mainloop()
def pick_random_function(self):
return rnd.choice(self.arg_functions_to_choose_from)
def ask_for_name(self):
return sd.askstring(title='Please, introduce yourself', prompt='What is your name?', parent=self.main_menu)
def ask_for_drunkenness(self):
return msg.askyesno(title='Driving under the influence of alcohol is prohibited in this state',
message='Are you drunk?', icon='question', parent=self.main_menu)
# entry point
entry_point = Class3()
如您所见-这有点奏效。 但是总的来说,这种布局是薄弱的,以我的拙见,应该避免使用代表“变量桶”的类。 假设现在我们的“雇主”希望主应用程序中的另一个按钮可以再次显示主菜单窗口。 我们可以在Class2
内创建Class1
实例,也可以向Class3
添加反向引用。 在第一种情况下,我们的时间布局变得不合逻辑,而在第二种情况下,我们的中间人不再是容器,而是控制器。
当然,所有这些只是我的主观意见,如果您对此方法足够了解,没有人可以责怪您。
嘿,这种布局是您真正想要的,因为我们的“中间人”内部有一个可以创建和销毁的方法 (如您所述)。
完整的代码实现与上面的代码非常相似(因此我决定不显示完整的代码,而是“抽象的片段”)。 此处的区别在于对Class3
的反向引用(现在是控制器),负责Windows交互的所有代码的重定位到Class3
中,实际上,现在没有理由将抽象函数的引用传递给Class1
,因为它对我们的中间人Class3
具有后向引用(我们可以直接从类中提取此函数)。
# ...
# classes
class Class1(tk.Toplevel):
def __init__(self, master, controller):
tk.Toplevel.__init__(self, master)
# let's keep reference to our middleman
self.controller = controller
# define and pack widgets
# ...
# all other methods interacts with children/widgets of this window and with controller
# ...
class Class2(tk.Tk):
def __init__(self, controller):
tk.Tk.__init__(self)
# let's keep reference to our middleman
self.controller = controller
# define and pack widgets
# ...
# all other methods interacts with children/widgets of this window and with controller
# ...
class Class3:
def __init__(self):
# hold functions
self.arg_functions_to_choose_from = [self.ask_for_name, self.ask_for_drunkenness]
# hold our windows
self.main_app = Class2(self)
self.main_menu = Class1(self.main_app, self)
# all other methods interacts with windows, starts mainloop, handles events, etc...
# ...
# ...
这种布局的缺点在于不必要的复杂性和功能的重复。 Tk()
类已经是与Tk
相关的任何儿童的控制器,并且也可以有效地管理自己。 我认为,当我们尝试控制与Tk
相关的内容以及(例如)某个类中与Python / Platform / Network相关的内容时,应该保留此布局以用于更复杂的事情。 所以这是最后一个(针对此答案)选项...
逻辑相同,布局非常相似,但只有两个类。 没什么好说的了,尝试尝试一下代码片段,找出此示例与第一个示例之间的区别:
# imports
#...
# classes
class Class1(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.minsize(350, 200)
# make button with arg_function as the attached command
# lets hold our function first
self.function_to_execute = self.master.pick_random_function()
# define widgets
self.main_frame = tk.Frame(self)
self.function_label = tk.Label(self, text='Function is %s\n Let\'s try to call it?' % self.function_to_execute.__name__)
self.execute_button = tk.Button(self, text='Execute %s and Quit' % self.function_to_execute.__name__,
command=self.execute_and_quit)
# pack stuff
self.main_frame.pack(fill='both', expand=True)
self.function_label.pack(fill='both', expand=True)
self.execute_button.pack(fill='x')
# handle closing
self.protocol('WM_DELETE_WINDOW', lambda: self.execute_and_quit(False))
def execute_and_quit(self, execute=True):
info = None
if callable(self.function_to_execute) and execute:
info = self.function_to_execute()
self.destroy()
self.master.renew(info)
class Class2(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.minsize(350, 200)
self.arg_functions_to_choose_from = [ask_for_name, ask_for_drunkenness]
# define widgets
self.main_frame = tk.Frame(self)
self.user_info_label = tk.Label(self)
self.show_menu_button = tk.Button(self, text='Repeat', command=self.show_menu)
self.quit_button = tk.Button(self, text='Quit', command=self.destroy)
# pack stuff
self.main_frame.pack(fill='both', expand=True)
self.user_info_label.pack(fill='both', expand=True)
self.show_menu_button.pack(fill='x')
self.quit_button.pack(fill='x')
self.show_menu()
def renew(self, info_to_renew=None):
self.deiconify()
if info_to_renew is None or info_to_renew == '':
self.user_info_label['text'] = 'Your person is unknown'
else:
if type(info_to_renew) is str:
self.user_info_label['text'] = 'Hello, %s! Although there\'s nothing more about you...' % info_to_renew
elif info_to_renew:
self.user_info_label['text'] = 'Gosh! You\'re filthy drunkard!'
else:
self.user_info_label['text'] = 'Are you building your drink refusal skills?'
def show_menu(self):
self.withdraw()
menu = Class1(self)
def pick_random_function(self):
return rnd.choice(self.arg_functions_to_choose_from)
# functions
def ask_for_name():
return sd.askstring(title='Please, introduce yourself', prompt='What is your name?')
def ask_for_drunkenness():
return msg.askyesno(title='Driving under the influence of alcohol is prohibited in this state',
message='Are you drunk?', icon='question')
# entry point
main_app = Class2()
main_app.mainloop()
同样,它也不是理想的解决方案,因为又有不必要的复杂性(当主菜单和主应用程序都可以继承自tk.Frame
类时,我们试图处理两个单独的窗口,但是让我们自己研究一下)如果您问我一个比第一个更合法的选择。
您的问题既是客观的(当您询问如何将某项内容传递给班级时)又是主观的(当您询问如何通过当前的结构将某项内容传递给班级时;当您要求反馈时),因此我的回答主要是:基于 (这通常不是答案,因为最终的决定权取决于您)。 您可以在此处找到基于意见的答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.