如何在tkinter中处理来自不同类的回调和事件绑定?

2024-05-13 06:03:22 发布

您现在位置:Python中文网/ 问答频道 /正文

根据Bryan Oakley对这篇文章的回答Best way to structure a tkinter application?,我想将tab1的内容移动到它自己的类中,但是让say_hello和get_item保持原样。我该怎么做

import tkinter as tk
from tkinter import ttk, N, S, END, EXTENDED

class MainApplication(tk.Frame):
    def __init__(self, root, *args, **kwargs):
        tk.Frame.__init__(self, root, *args, **kwargs)
        self.root = root
        self.nb = ttk.Notebook(root)

        self.tab1 = ttk.Frame(self.nb)
        self.tab2 = ttk.Frame(self.nb)

        self.nb.add(self.tab1, text='TAB1')
        self.nb.add(self.tab2, text='TAB2')

        self.nb.grid(row=0, column=0)

        #contents of tab1 - which I would like to place in their own class
        self.frame = tk.Frame(self.tab1)
        self.frame.grid(row=0, column=0)

        self.button = tk.Button(self.frame, text='Hello', command=self.say_hello)
        self.button.grid(row=0, column=0)

        self.items = ['A','B','C']

        self.listbox = tk.Listbox(self.frame, selectmode=EXTENDED, exportselection=0)
        self.listbox.grid(row=1, column=0)

        self.scrollbar = tk.Scrollbar(self.frame, orient='vertical')
        self.scrollbar.grid(row=1, column=1, sticky=N+S)
        self.listbox.config(yscrollcommand=self.scrollbar.set)
        self.scrollbar.config(command=self.listbox.yview)
        self.listbox.bind('<<ListboxSelect>>', self.get_item)

        for item in self.items:
            self.listbox.insert(END, item) 


        #contents of tab2 - which should be in their own class
        #...
        #...


    def say_hello(self):
        print('hello')

    def get_item(self, evt):
        w = evt.widget
        index = int(w.curselection()[0])
        print('item selected = {}'.format(self.items[index]))

if __name__ == "__main__":
    root = tk.Tk() 
    MainApplication(root)
    root.mainloop()

编辑:

感谢萨阿德的详细回复。我运行了你的代码,研究了它,学到了很多东西。然而,我修改了我的问题,使其更加集中


Tags: selfhellocolumnrootitemframetab1tk
1条回答
网友
1楼 · 发布于 2024-05-13 06:03:22

据我所说,我认为每个人都有自己的写作风格,组织他们的文件和课程。我组织代码的方式可能不是最好的。但我会尽我最大的努力使它更有条理,更简单。尽管没有关于如何组织Tkinter应用程序的硬性规定。但是,是的,OOP是组织大型项目的最佳方式,因为它们使代码更小,更容易理解

您可以在一个单独的文件(tab1.py,tab2.py,…)中为每个选项卡(Tab1, Tab2, ..)创建一个类,但我更希望将所有选项卡都放在一个名为tabs.py的文件中。这样我就可以像这样导入它们

import tabs

tabs.Tab1()
tabs.Tab2()
...

请仔细阅读代码中的每条注释和我的观点,以获得您的大部分答案。

我已将您的代码分为4部分

  1. 首先,我为所有选项卡创建了一个基本框架类在这个基本框架类中,我们可以配置所有选项卡共同需要的东西。例如,假设您希望每个选项卡中都有一个具有背景颜色浅黄色的框架。因此,您可以创建一个基类,并将其作为baseframe.py放在一个单独的文件中。就像这样

    # baseframe.py
    import tkinter as tk
    
    class BaseFrame(tk.Frame):
        "This is a base frame for Tabs."
        def __init__(self, master=None, **kw):
            super().__init__(master=master, **kw)
            # This will be your baseframe, if you need it.
            # Do something here that is common for all tabs.
            # for example if you want to have the same color for every tab.
    
            # These are just some example, change it as per your need.
            self['bg'] = 'lightyellow'
            self['relief'] = 'sunken'
            self['borderwidth'] = 5
            ...
    
        def show_info(self):
            "This function is common to every tab"
            # You don't necessarily need this, 
            # This is just to show you the possibilities.
    
            # Do something in this function, that you want to have in every tab.
            ...
            ...
            print('show_info', self)
            return
    
  2. Would it make sense to have a separate file with a function in it for creating a generic Listbox?

    我认为一个CustomListbox类会很好,你可以很有创造性地创建这个类,但我保持它的简单性。我在CustomListbox类中添加了一个滚动条,并对其进行了配置

    您可以为此创建文件customlistbox.py。或者将baseframe.py的名称更改为basewidgets.py,并将BaseFrameCustomListbox保存在一个文件中

    # customlistbox.py
    import tkinter as tk
    
    class CustomListbox(tk.Listbox):
        "Tkinter listbox which has vertical scrollbar configured."
        def __init__(self, master=None, cnf={}, **kw):
            super().__init__(master=master, cnf=cnf, **kw)
            # Config scrollbar
            self.scrollbar = tk.Scrollbar(self.master,orient='vertical',command=self.yview)
            self.config(yscrollcommand=self.scrollbar.set)
    

    请参见选项卡类中的如何使用它

  3. 这里是tabs.py文件

    # tabs.py
    import tkinter as tk
    from baseframe import BaseFrame
    from customlistbox import CustomListbox
    from tkinter import ttk, N, S, END, EXTENDED
    
    class Tab1(BaseFrame):
        "This is Tab 1."
        def __init__(self, master=None, **kw):
            super().__init__(master=master, **kw)
            self.button2 = tk.Button(self,text='Welcome to Tab1', command=self.show_info)
            self.button2.grid(row=0, column=0)
            self.button = tk.Button(self, text='Hello', command=self.say_hello)
            self.button.grid(row=1, column=0)
    
            self.items = ['A','B','C']
    
            self.listbox = CustomListbox(self, selectmode=EXTENDED, exportselection=0)
            self.listbox.grid(row=2, column=0)
            self.listbox.scrollbar.grid(row=2, column=1, sticky=N+S)
            self.listbox.bind("<<ListboxSelect>>", self.get_item)
    
            for item in self.items:
                self.listbox.insert(END, item)
    
        def say_hello(self):
            "Callback function for button."
            print('hello')
    
        def get_item(self, evt):
            "Internal function."
            w = evt.widget
            index = int(w.curselection()[0])
            print('item selected = {}'.format(self.items[index]))
    
    class Tab2(BaseFrame):
        "This is Tab2."
        def __init__(self, master=None, **kw):
            super().__init__(master=master, **kw)
            self.button = tk.Button(self,text='Welcome to Tab2', command=self.show_info)
            self.button.grid(row=0, column=0)
    
  4. 最后是main.py文件。您可以在这里导入选项卡(import tabs)。我根据自己的意愿组织了MainApplication课程。例如,我没有为选项卡创建每个实例(self.tab1,self.tab2,…),而是创建了一个列表self.tabs来包含所有选项卡,这更便于通过self.tabsindex访问

    # main.py
    import tkinter as tk
    import tabs as tb
    from tkinter import ttk, N, S, END, EXTENDED
    
    
    class MainApplication(ttk.Notebook):
        def __init__(self, master=None, **kw):
            super().__init__(master=master, **kw)
            self.master = master
            self.tabs = []  # All tabs list, easier to access.
    
            # Add tabs here.
            self.add_tab(tb.Tab1, text='TAB1')
            self.add_tab(tb.Tab2, text='TAB2')
            ...
            ...
    
            # Excess methods of Tabs:-
            self.tabs[0].say_hello()
            self.tabs[1].show_info()
    
            # for example: Bind say_hello() with "Button-2" to this class.
            self.bind('<Button-2>', lambda evt: self.tabs[0].say_hello())
    
    
        def add_tab(self, tab, **kw):
            "Adds tab to the notebook."
            self.tabs.append(tab(self))
            self.add(self.tabs[-1], **kw)
    
    
    if __name__ == "__main__":
        root = tk.Tk()
    
        # Creating an container.
        container = tk.Frame(root)
        container.grid()
    
        # Initializing the app.
        app = MainApplication(container)
        app.grid()
    
       root.mainloop()
    

希望这能让你明白很多事情

相关问题 更多 >