![](/img/trans.png)
[英]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.