当前位置:首页>python>Python Tkinter项目架构设计与大型界面组织

Python Tkinter项目架构设计与大型界面组织

  • 2026-03-23 20:12:16
Python Tkinter项目架构设计与大型界面组织

🏗️ 当你的GUI代码开始"失控"

写Tkinter的人,大多数都经历过这个阶段——

一个文件,几百行,全是ButtonLabelFrame堆在一起。起初还好,改个颜色、加个按钮,找得到。等到项目稍微复杂一点,那个文件就开始变成一头怪兽。你想加一个新功能,翻了十分钟代码,愣是不知道该往哪插。

这不是你的问题。Tkinter本身的学习曲线非常平缓,入门门槛低,但它几乎不强制你遵循任何架构规范。自由度太高,反而是个陷阱。

我在实际项目里见过一个单文件Tkinter应用,4000行,没有任何分层,所有逻辑、界面、数据访问全揉在一起。那个项目后来无人敢碰,只能推倒重来。代价很大。

这篇文章就是要解决这个问题——如何从一开始就把Tkinter项目的架构设计做对,或者如何把已经乱掉的项目重新整理清楚。


🧩 为什么Tkinter项目特别容易"腐烂"

Tkinter的组件本身就是对象,这一点很好。但问题在于,tk.Tk()实例和业务逻辑之间没有任何天然屏障。你可以在按钮回调里直接操作数据库,可以在数据处理函数里顺手改一下Label的文字——没人拦你。

这种"随便写"的自由,在小脚本里是优势,在中大型项目里就是定时炸弹。

具体来说,Tkinter项目腐烂的三个典型路径:

第一,回调函数膨胀。 一个按钮点击事件,开始只有三行,后来加了校验逻辑、加了网络请求、加了日志记录……最后那个回调函数有80行,谁也不敢动。

第二,组件引用到处传。 为了让某个子窗口能改主窗口的某个Label,你开始把self.root或者具体的组件对象到处传递。组件之间的依赖关系变成一张网,牵一发动全身。

第三,状态管理混乱。 程序的状态(当前用户、当前选中项、配置参数)散落在各个类的实例变量里,没有统一的地方管理,同步起来一团糟。


🏛️ MVC思想在Tkinter里的落地

解决上面这些问题,最经典的思路就是MVC(Model-View-Controller)。不过Tkinter里的MVC和Web框架里的MVC有些差别,咱们得结合实际来理解。

  • • Model(模型层):纯粹的数据和业务逻辑,完全不知道Tkinter的存在,不导入任何tkinter模块。
  • • View(视图层):只负责界面的创建和展示,不处理任何业务逻辑,只是"显示"数据和"传递"用户操作。
  • • Controller(控制层):连接Model和View的桥梁,处理用户事件,调用Model,更新View。

这个分层说起来简单,真正落地需要一些具体的设计决策。下面用一个实际的例子来演示。

假设我们在做一个员工信息管理系统,有列表展示、新增、编辑、删除功能。


📁 项目目录结构设计

先把文件结构定下来,这是架构的物理基础:

employee_manager/├── main.py                 # 程序入口,只做启动├── app.py                  # 应用主类,负责组装各层├── models/│   ├── __init__.py│   ├── employee.py         # Employee数据类│   └── employee_repo.py    # 数据访问层(读写文件/数据库)├── views/│   ├── __init__.py│   ├── base_view.py        # 视图基类│   ├── main_view.py        # 主窗口视图│   ├── employee_list.py    # 员工列表组件│   └── employee_form.py    # 员工表单组件(新增/编辑)├── controllers/│   ├── __init__.py│   └── employee_ctrl.py    # 员工功能控制器└── utils/    ├── __init__.py    └── event_bus.py        # 事件总线(解耦神器)

目录结构不是越复杂越好。这个结构对于中型项目来说刚刚好——职责清晰,又不至于文件太分散。


🔧 核心代码实现

1️⃣ Model层:干净的数据定义

# models/employee.pyfrom dataclasses import dataclass, fieldfrom typing importOptionalimport uuid@dataclassclassEmployee:    name: str    department: str    salary: float    emp_id: str = field(default_factory=lambdastr(uuid.uuid4())[:8])    email: Optional[str] = Nonedefvalidate(self) -> tuple[boolstr]:"""业务校验逻辑,与界面完全无关"""ifnotself.name.strip():returnFalse"姓名不能为空"ifself.salary < 0:returnFalse"薪资不能为负数"returnTrue""
# models/employee_repo.pyimport jsonfrom pathlib import Pathfrom .employee import EmployeeclassEmployeeRepository:"""数据访问层,封装所有持久化操作"""def__init__(self, data_file: str = "employees.json"):self.data_file = Path(data_file)self._cache: list[Employee] = []self._load()def_load(self):ifself.data_file.exists():withopen(self.data_file, "r", encoding="utf-8"as f:                raw = json.load(f)self._cache = [Employee(**item) for item in raw]def_save(self):withopen(self.data_file, "w", encoding="utf-8"as f:            json.dump([vars(e) for e inself._cache], f, ensure_ascii=False, indent=2)defget_all(self) -> list[Employee]:returnlist(self._cache)defadd(self, emp: Employee) -> bool:        valid, msg = emp.validate()ifnot valid:raise ValueError(msg)self._cache.append(emp)self._save()returnTruedefupdate(self, emp: Employee) -> bool:for i, e inenumerate(self._cache):if e.emp_id == emp.emp_id:self._cache[i] = empself._save()returnTruereturnFalsedefdelete(self, emp_id: str) -> bool:        before = len(self._cache)self._cache = [e for e inself._cache if e.emp_id != emp_id]iflen(self._cache) < before:self._save()returnTruereturnFalse

注意,Model层里一行Tkinter代码都没有。这层可以单独测试,可以被命令行工具复用,完全独立。


2️⃣ 事件总线:解耦的关键

组件之间需要通信,但不能直接持有彼此的引用——这时候事件总线就派上用场了。这玩意儿说白了就是一个全局的"消息广播站":

# utils/event_bus.pyfrom collections import defaultdictfrom typing importCallableAnyclassEventBus:"""轻量级事件总线,解耦视图组件间的通信"""def__init__(self):self._listeners: dict[strlist[Callable]] = defaultdict(list)defsubscribe(self, event: str, callback: Callable):self._listeners[event].append(callback)defunsubscribe(self, event: str, callback: Callable):self._listeners[event] = [            cb for cb inself._listeners[event] if cb != callback        ]defpublish(self, event: str, data: Any = None):for callback inself._listeners[event]:            callback(data)# 全局单例bus = EventBus()

有了事件总线,员工列表不需要知道表单的存在,表单也不需要知道列表的存在——它们只需要和bus打交道。


3️⃣ View层:只管"长什么样"

# views/base_view.pyimport tkinter as tkfrom tkinter import ttkclassBaseView(tk.Frame):"""所有视图的基类,提供公共方法"""def__init__(self, master, **kwargs):super().__init__(master, **kwargs)self._setup_ui()def_setup_ui(self):"""子类重写此方法来构建界面"""passdefshow_error(self, message: str):from tkinter import messagebox        messagebox.showerror("错误", message)defshow_info(self, message: str):from tkinter import messagebox        messagebox.showinfo("提示", message)
# views/employee_list.pyimport tkinter as tkfrom tkinter import ttkfrom .base_view import BaseViewfrom utils.event_bus import busclassEmployeeListView(BaseView):"""员工列表视图,只负责展示数据和发布用户操作事件"""def_setup_ui(self):# 工具栏        toolbar = tk.Frame(self, bg="#f0f0f0", pady=4)        toolbar.pack(fill="x")        tk.Button(toolbar, text="新增员工",                  command=lambda: bus.publish("employee.add_requested")                  ).pack(side="left", padx=4)        tk.Button(toolbar, text="编辑选中",                  command=self._on_edit_click                  ).pack(side="left", padx=4)        tk.Button(toolbar, text="删除选中",                  command=self._on_delete_click                  ).pack(side="left", padx=4)# 列表主体        cols = ("ID""姓名""部门""薪资")self.tree = ttk.Treeview(self, columns=cols, show="headings", height=15)for col in cols:self.tree.heading(col, text=col)self.tree.column(col, width=120, anchor="center")        scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.tree.yview)self.tree.configure(yscrollcommand=scrollbar.set)self.tree.pack(side="left", fill="both", expand=True)        scrollbar.pack(side="right", fill="y")defrefresh(self, employees: list):"""由控制器调用,刷新列表数据"""for item inself.tree.get_children():self.tree.delete(item)for emp in employees:self.tree.insert("""end", iid=emp.emp_id,                             values=(emp.emp_id, emp.name, emp.department, f"¥{emp.salary:,.0f}"))defget_selected_id(self):        selected = self.tree.selection()return selected[0if selected elseNonedef_on_edit_click(self):        emp_id = self.get_selected_id()if emp_id:            bus.publish("employee.edit_requested", emp_id)else:self.show_error("请先选择一条记录")def_on_delete_click(self):        emp_id = self.get_selected_id()if emp_id:            bus.publish("employee.delete_requested", emp_id)else:self.show_error("请先选择一条记录")
# views/employee_form.py  import tkinter as tk  from tkinter import ttk  from .base_view import BaseView  from utils.event_bus import bus  classEmployeeFormView(tk.Toplevel):  """      员工新增/编辑表单弹窗。      纯视图职责:构建表单、收集输入、发布提交事件。    不做任何业务校验,校验交给 Model 层。      """def__init__(self, master, employee=None):  super().__init__(master)  self.employee = employee          # None 表示新增,有值表示编辑  self.is_edit = employee isnotNoneself.title("编辑员工"ifself.is_edit else"新增员工")  self.geometry("400x300")  self.resizable(FalseFalse)  # 模态:阻止操作主窗口  self.transient(master)  self.grab_set()  # 订阅表单错误事件(由控制器发布)          bus.subscribe("form.show_error"self._on_form_error)  self._setup_ui()  self._fill_data()   # 编辑模式时回填数据  # 窗口关闭时取消订阅,防止内存泄漏  self.protocol("WM_DELETE_WINDOW"self._on_close)  def_setup_ui(self):          main = tk.Frame(self, padx=20, pady=16)          main.pack(fill="both", expand=True)  # 表单字段定义:(标签文字, 字段key, 是否必填)          fields = [              ("姓  名 *""name",       True),              ("部  门 *""department",  True),              ("薪  资 *""salary",      True),              ("邮  箱",   "email",       False),          ]  self._varsdict[str, tk.StringVar] = {}  self._entries: dict[str, ttk.Entry] = {}  for row, (label_text, key, required) inenumerate(fields):              tk.Label(main, text=label_text, anchor="w", width=10                     ).grid(row=row, column=0, sticky="w", pady=6)              var = tk.StringVar()              entry = ttk.Entry(main, textvariable=var, width=28)              entry.grid(row=row, column=1, sticky="ew", pady=6, padx=(80))  self._vars[key] = var  self._entries[key] = entry          main.columnconfigure(1, weight=1)  # 错误提示标签(默认隐藏)  self._error_var = tk.StringVar()  self._error_label = tk.Label(              main, textvariable=self._error_var,              fg="red", font=(""9), anchor="w"        )  self._error_label.grid(row=len(fields), column=0, columnspan=2,                                 sticky="w", pady=(40))  # 底部按钮区          btn_frame = tk.Frame(self)          btn_frame.pack(fill="x", padx=20, pady=(014))          ttk.Button(btn_frame, text="取消",                     command=self._on_close).pack(side="right", padx=(60))          ttk.Button(btn_frame, text="保存",                     command=self._on_submit).pack(side="right")  # 回车键快捷提交  self.bind("<Return>"lambda _: self._on_submit())  self.bind("<Escape>"lambda _: self._on_close())  # 焦点落在第一个输入框  self._entries["name"].focus_set()  def_fill_data(self):  ifnotself.is_edit:  return        emp = self.employee  self._vars["name"].set(emp.name)  self._vars["department"].set(emp.department)  self._vars["salary"].set(str(emp.salary))  self._vars["email"].set(emp.email or"")  def_on_submit(self):  """收集表单数据,发布提交事件,不做业务校验。"""self._clear_error()          data = {  "name":       self._vars["name"].get().strip(),  "department"self._vars["department"].get().strip(),  "email":      self._vars["email"].get().strip() orNone,          }  # 薪资需要前置类型转换,转换失败直接在视图层提示          salary_raw = self._vars["salary"].get().strip()  try:              data["salary"] = float(salary_raw)  except ValueError:  self._show_error("薪资必须是有效的数字")  self._entries["salary"].focus_set()  return# 编辑模式带上原始 ID,控制器据此判断新增还是更新  ifself.is_edit:              data["emp_id"] = self.employee.emp_id          bus.publish("employee.form_submitted", data)  def_on_form_error(self, message: str):  """控制器校验失败时回调,在表单内显示错误。"""self._show_error(message)  def_show_error(self, message: str):  self._error_var.set(f"⚠ {message}")  def_clear_error(self):  self._error_var.set("")  def_on_close(self):          bus.unsubscribe("form.show_error"self._on_form_error)  self.destroy()  defclose(self):  """供控制器在提交成功后主动关闭弹窗。"""self._on_close()

视图层里没有任何业务判断——它只是把"用户点了什么"通过事件总线广播出去,然后等控制器告诉它"现在显示什么"。


4️⃣ Controller层:业务逻辑的归宿

# controllers/employee_ctrl.py  from models.employee import Employee  from models.employee_repo import EmployeeRepository  from utils.event_bus import bus  classEmployeeController:  """员工功能控制器,连接Model与View"""def__init__(self, list_view, repo: EmployeeRepository):  self.list_view = list_view  self.repo = repo  self._register_events()  self.refresh_list()  # 初始加载  def_register_events(self):          bus.subscribe("employee.add_requested"self._handle_add)          bus.subscribe("employee.edit_requested"self._handle_edit)          bus.subscribe("employee.delete_requested"self._handle_delete)          bus.subscribe("employee.form_submitted"self._handle_form_submit)  defrefresh_list(self, _=None):          employees = self.repo.get_all()  self.list_view.refresh(employees)  def_handle_add(self, _):  # 打开新增表单(传入空数据)  self._open_form(None)  def_handle_edit(self, emp_id: str):          all_emps = self.repo.get_all()          target = next((e for e in all_emps if e.emp_id == emp_id), None)  if target:  self._open_form(target)  def_handle_delete(self, emp_id: str):  from tkinter import messagebox  if messagebox.askyesno("确认删除""确定要删除这条记录吗?"):  self.repo.delete(emp_id)  self.refresh_list()  def_handle_form_submit(self, data: dict):  try:              emp = Employee(**data)  if data.get("emp_id"):  self.repo.update(emp)  else:  self.repo.add(emp)  self.refresh_list()  # ✅ 提交成功后关闭弹窗  ifhasattr(self"_form_window"andself._form_window.winfo_exists():  self._form_window.close()  except ValueError as e:              bus.publish("form.show_error"str(e))  def_open_form(self, employee):  """          打开员工表单弹窗。          employee 为 None 时进入新增模式,传入 Employee 实例时进入编辑模式。          """# 防止重复打开多个表单窗口  ifhasattr(self"_form_window"andself._form_window.winfo_exists():  self._form_window.lift()  self._form_window.focus_force()  returnfrom views.employee_form import EmployeeFormView  self._form_window = EmployeeFormView(self.list_view.master, employee)

5️⃣ 程序入口:组装一切

# main.py  import tkinter as tk  from views.employee_list import EmployeeListView  from models.employee_repo import EmployeeRepository  from controllers.employee_ctrl import EmployeeController  classApplication:  def__init__(self):  self.root = tk.Tk()  self.root.title("员工信息管理系统")  self.root.geometry("800x500")  self._build()  def_build(self):          repo = EmployeeRepository()          list_view = EmployeeListView(self.root)          list_view.pack(fill="both", expand=True, padx=10, pady=10)  # 控制器持有视图和模型的引用,视图和模型互不知晓  self.ctrl = EmployeeController(list_view, repo)  defrun(self):  self.root.mainloop()  if __name__ == "__main__":      Application().run()

🚀 大型项目的进阶组织策略

当项目继续增长,比如有十几个功能模块,上面的结构还需要进一步演进。

模块化注册机制是一个很实用的思路:每个功能模块(员工管理、部门管理、权限管理)都实现一个统一的register(app)接口,在应用启动时统一注册,主程序不需要了解每个模块的细节。这类似于Flask的Blueprint机制,用在Tkinter里同样好使。

自定义Frame作为"页面",配合一个简单的页面路由器,可以实现多页面切换而不需要开多个Toplevel窗口。路由器维护一个{page_name: FrameClass}的字典,切换时pack_forget()当前页面,pack()目标页面,干净利落。

配置与主题分离也是值得做的事。把颜色、字体、间距等全部提取到一个theme.py文件里,所有视图引用这个文件的常量,而不是硬编码。换主题的时候,改一个文件就够了。


💡 三句话总结

架构的本质,是推迟决策的成本——好的架构让你以后改需求的时候少付代价。

Model层不碰Tkinter,View层不碰业务逻辑,Controller层负责撮合——这三条守住了,项目就不会失控。

事件总线不是必须的,但一旦组件超过五六个开始互相通信,它就是救命稻草。


🛠️ 实战挑战

文章看完了,来一个小练习:尝试在上面的架构基础上,新增一个"按部门筛选"的功能。思考一下:筛选逻辑应该放在哪一层?筛选条件的变化应该通过什么方式通知列表刷新?

欢迎在评论区分享你的设计思路,也欢迎聊聊你在实际项目里遇到的Tkinter架构问题。


标签#Python开发#Tkinter#GUI架构设计#MVC模式#Windows桌面开发

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-03-27 09:50:59 HTTP/2.0 GET : https://f.mffb.com.cn/a/481992.html
  2. 运行时间 : 0.129312s [ 吞吐率:7.73req/s ] 内存消耗:4,782.37kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=5d8e4404eec3049503d55e86d5b58385
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000990s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001427s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000723s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000670s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001372s ]
  6. SELECT * FROM `set` [ RunTime:0.003747s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001731s ]
  8. SELECT * FROM `article` WHERE `id` = 481992 LIMIT 1 [ RunTime:0.001161s ]
  9. UPDATE `article` SET `lasttime` = 1774576259 WHERE `id` = 481992 [ RunTime:0.019719s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000762s ]
  11. SELECT * FROM `article` WHERE `id` < 481992 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001152s ]
  12. SELECT * FROM `article` WHERE `id` > 481992 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001143s ]
  13. SELECT * FROM `article` WHERE `id` < 481992 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.004153s ]
  14. SELECT * FROM `article` WHERE `id` < 481992 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001981s ]
  15. SELECT * FROM `article` WHERE `id` < 481992 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002165s ]
0.133192s