簡體   English   中英

Tkinter.TopLevel; 從另一個類傳遞函數?

[英]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對象的方法(主應用程序)調用時。 這樣,我便會將此方法附加到主菜單中。

我可以獲取有關如何解決此問題的幫助/反饋嗎?

簡短答案:

構造tkinter應用程序的最佳方法

長答案:

序幕:

如果我對您的程序邏輯的理解是正確的-您具有“游戲”設計,其中class1充當“ 主菜單” ,而class2模仿“游戲” ,則在我們完成“主菜單”之后開始。 您使用第三類的想法是合理的,但是我發現這種方法有點麻煩。 無論如何,讓我們先堅持您想要的布局,然后再嘗試類似的方法。 另請注意,由於您將class2視為“主應用程序”,因此我會將您的class2視為tk.Tk()

選項:

  • 3類:頂級-主菜單,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添加反向引用。 在第一種情況下,我們的時間布局變得不合邏輯,而在第二種情況下,我們的中間人不再是容器,而是控制器。

當然,所有這些只是我的主觀意見,如果您對此方法足夠了解,沒有人可以責怪您。

  • 3類:頂級-主菜單,Tk-主應用程序,中間人-控制器:

嘿,這種布局是您真正想要的,因為我們的“中間人”內部有一個可以創建和銷毀的方法 (如您所述)。

完整的代碼實現與上面的代碼非常相似(因此我決定不顯示完整的代碼,而是“抽象的片段”)。 此處的區別在於對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相關的內容時,應該保留此布局以用於更復雜的事情。 所以這是最后一個(針對此答案)選項...

  • 2類:頂級-主菜單,Tk-主應用程序,容器,控制器:

邏輯相同,布局非常相似,但只有兩個類。 沒什么好說的了,嘗試嘗試一下代碼片段,找出此示例與第一個示例之間的區別:

#   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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM