You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用类重构Tkinter代码?实现标签页与控件复用

优化Tkinter库存管理程序的代码结构

问题背景

我刚完成第一个Tkinter库存管理程序,为了扩展功能,希望优化代码、消除重复并提升扩展性。原代码中存在大量重复创建ttk.Notebook标签页、Frame、LabelFrame、Treeview的代码。

原重复代码

from tkinter import *
from tkinter import ttk

#Creating window, calling it root
root = Tk()
root.title("Ges_Stock")
root.geometry("1000x600")

#Creating Notebook so I can create tabs on it
my_notebook = ttk.Notebook(root)
my_notebook.pack(pady=10, fill="both")


# Tabs creation, packing, and naming it
my_frame1 = Frame(my_notebook, width=1200, height=700)
my_frame2 = Frame(my_notebook, width=1200, height=700)
my_frame3 = Frame(my_notebook, width=1200, height=700)
#my_frame4 = Frame(my_notebook, width=1200, height=700)

my_frame1.pack(fill="both", expand=1, pady=20)
my_frame2.pack(fill="both", expand=1, pady=20)
my_frame3.pack(fill="both", expand=1, pady=20)
#my_frame4.pack(fill="both", expand=1)

my_notebook.add(my_frame1, text="Barcode")
my_notebook.add(my_frame2, text="Manual Code")
my_notebook.add(my_frame3, text="Inventory Jumbo")
#my_notebook.add(my_frame4, text="Code Generator")


# Creating LabelFrames

wrapper1 = LabelFrame(my_frame1, text="Items List")
wrapper2 = LabelFrame(my_frame1, text="Add and Remove")

wrapper4 = LabelFrame(my_frame2, text="Items List")
wrapper5 = LabelFrame(my_frame2, text="Add and Remove")

wrapper7 = LabelFrame(my_frame3, text="Items List")
wrapper8 = LabelFrame(my_frame3, text="Add and Remove")

#wrapper10 = LabelFrame(my_frame4, text="Items List")
#wrapper11 = LabelFrame(my_frame4, text="Command Bar")

#Packing the frames
wrapper2.pack(fill="both", expand="yes", padx=20, ipady=20)
wrapper1.pack(fill="both", expand="yes", padx=20, ipady=20)

wrapper5.pack(fill="both", expand="yes", padx=20, ipady=20)
wrapper4.pack(fill="both", expand="yes", padx=20, ipady=20)

wrapper8.pack(fill="both", expand="yes", padx=20, ipady=20)
wrapper7.pack(fill="both", expand="yes", padx=20, ipady=20)

#wrapper11.pack(fill="both", expand="yes", padx=20, ipady=10)
#wrapper10.pack(fill="both", expand="yes", padx=20, ipady=10)
#endregion

# TREEVIEW CREATION
#Treeview on frame 1
trv = ttk.Treeview(wrapper1, columns=(1,2,3,4,5,6,7), show="headings", height="15") #
trv.pack(fill="both",padx=20, ipady=10)

#Treeview on frame 2
trv_est = ttk.Treeview(wrapper4, columns=(1, 2, 3, 4, 5, 6, 7), show="headings", height="15") #
trv_est.pack(fill="both", padx=20, ipady=10)

#Treeview on frame 3
trv_jmb = ttk.Treeview(wrapper7, columns=(1, 2, 3, 4, 5, 6, 7), show="headings", height="15") #
trv_jmb.pack(fill="both", padx=20, ipady=10)


root.mainloop()

尝试的简化代码

我尝试用类和函数简化代码,但没理清层级关系,目前的尝试代码如下:

from tkinter import *
from tkinter import ttk

root = Tk()
root.title('Codemy.com - Classes')
root.geometry("1000x600")

my_notebook = ttk.Notebook(root)
my_notebook.pack(pady=10, fill="both")

def btn_add():
    pass

class nt_book:

    def __init__(self, name_tab, num_frame):

        self.nome_aba = name_tab
        self.num_frame = num_frame

        self.num_frame = Frame(my_notebook, width=1200, height=700)
        self.num_frame.pack()

        my_notebook.add(self.num_frame, text=self.nome_aba)

    def wrap(self, name_wrap):
        self.name_wrap = name_wrap

        wrapper = LabelFrame(self.num_frame, text=self.name_wrap)
        wrapper.pack(fill="both", expand="yes", padx=20, ipady=20)


# Trying to call a tab and frames inside
iest = nt_book("Inventory", "frame1")

est_fr1 = est.wrap("Command Bar")
est_fr2 = est.wrap("Item List")

add_btn = Button(est_fr1, text="Add", command=btn_add)
add_btn.grid(row=1, column=1, padx=10, pady=5)

root.mainloop()

需求目标

  • 创建用于添加标签页的类;
  • 创建用于给标签页添加LabelFrame的类或函数;
  • 创建用于给LabelFrame添加Button和Treeview的类或函数;

不想每次新增标签页和功能都复制粘贴重复代码,不清楚是否需要使用内部类、子类或嵌套类。


解决方案

可以通过分层类结构实现代码复用,把标签页、LabelFrame、控件的创建封装成职责单一的类,保持层级清晰:

1. 核心类设计

  • InventoryApp: 主应用类,负责初始化窗口和Notebook容器
  • TabPage: 标签页类,每个实例对应一个Notebook标签页
  • SectionFrame: 标签页内的分组容器(LabelFrame)
  • WidgetFactory: 工具类,封装Treeview和Button的创建逻辑

2. 完整优化代码

from tkinter import *
from tkinter import ttk

class WidgetFactory:
    """工具类:封装常用控件的创建逻辑"""
    @staticmethod
    def create_treeview(parent, columns, height=15):
        tree = ttk.Treeview(parent, columns=columns, show="headings", height=height)
        tree.pack(fill="both", padx=20, ipady=10)
        # 统一设置表头和列宽
        for idx, col in enumerate(columns, 1):
            tree.heading(col, text=f"列 {idx}")
            tree.column(col, width=100)
        return tree
    
    @staticmethod
    def create_button(parent, text, command, row=0, col=0, padx=10, pady=5):
        btn = Button(parent, text=text, command=command)
        btn.grid(row=row, column=col, padx=padx, pady=pady)
        return btn

class SectionFrame:
    """标签页内的分组容器(LabelFrame)"""
    def __init__(self, parent, title):
        self.frame = ttk.LabelFrame(parent, text=title)
        self.frame.pack(fill="both", expand=True, padx=20, ipady=20)
    
    def get_frame(self):
        return self.frame

class TabPage:
    """Notebook标签页类"""
    def __init__(self, notebook, tab_title):
        self.notebook = notebook
        self.tab_frame = Frame(notebook, width=1200, height=700)
        self.tab_frame.pack(fill="both", expand=True, pady=20)
        notebook.add(self.tab_frame, text=tab_title)
        
        # 存储当前标签页的分组容器
        self.sections = {}
    
    def add_section(self, section_title):
        """给当前标签页添加一个分组容器"""
        section = SectionFrame(self.tab_frame, section_title)
        self.sections[section_title] = section
        return section.get_frame()
    
    def get_section(self, section_title):
        """获取指定标题的分组容器"""
        return self.sections.get(section_title, None)

class InventoryApp:
    """主应用类"""
    def __init__(self, root):
        self.root = root
        self.root.title("Ges_Stock")
        self.root.geometry("1000x600")
        
        # 初始化Notebook
        self.notebook = ttk.Notebook(root)
        self.notebook.pack(pady=10, fill="both")
        
        # 存储所有标签页
        self.tabs = {}
    
    def add_tab(self, tab_title):
        """添加一个新的标签页"""
        tab = TabPage(self.notebook, tab_title)
        self.tabs[tab_title] = tab
        return tab
    
    def get_tab(self, tab_title):
        """获取指定标题的标签页"""
        return self.tabs.get(tab_title, None)

# 示例用法
def btn_add_handler():
    print("添加按钮被点击")

if __name__ == "__main__":
    root = Tk()
    app = InventoryApp(root)
    
    # 创建标签页1:Barcode
    barcode_tab = app.add_tab("Barcode")
    barcode_items_frame = barcode_tab.add_section("Items List")
    barcode_ops_frame = barcode_tab.add_section("Add and Remove")
    WidgetFactory.create_treeview(barcode_items_frame, columns=(1,2,3,4,5,6,7))
    WidgetFactory.create_button(barcode_ops_frame, "Add", btn_add_handler, row=0, col=0)
    WidgetFactory.create_button(barcode_ops_frame, "Remove", lambda: print("移除"), row=0, col=1)
    
    # 创建标签页2:Manual Code
    manual_tab = app.add_tab("Manual Code")
    manual_items_frame = manual_tab.add_section("Items List")
    manual_ops_frame = manual_tab.add_section("Add and Remove")
    WidgetFactory.create_treeview(manual_items_frame, columns=(1,2,3,4,5,6,7))
    WidgetFactory.create_button(manual_ops_frame, "Add", btn_add_handler)
    
    # 创建标签页3:Inventory Jumbo
    jumbo_tab = app.add_tab("Inventory Jumbo")
    jumbo_items_frame = jumbo_tab.add_section("Items List")
    jumbo_ops_frame = jumbo_tab.add_section("Add and Remove")
    WidgetFactory.create_treeview(jumbo_items_frame, columns=(1,2,3,4,5,6,7))
    
    root.mainloop()

3. 代码优势

  • 零重复代码:新增标签页、分组容器、控件只需调用对应方法,无需复制粘贴
  • 层级清晰:主应用→标签页→分组容器→控件,每个类只负责单一职责
  • 扩展性强:新增控件类型只需在WidgetFactory中添加静态方法;修改容器样式只需调整SectionFrame实现
  • 易于维护:模块职责明确,修改某部分功能不会影响其他模块

内容的提问来源于stack exchange,提问作者Leandro Bertarelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 20:03:19