当前位置:首页>python>Python 用 MVP 模式解耦 Tkinter 界面与业务逻辑

Python 用 MVP 模式解耦 Tkinter 界面与业务逻辑

  • 2026-06-30 04:15:49
Python 用 MVP 模式解耦 Tkinter 界面与业务逻辑

🤔 你的 Tkinter 代码,是不是长这样?

我见过太多这种代码了。一个 MainWindow 类,几百行,按钮回调里直接写数据库查询,StringVar 和业务计算混在一起,改个需求要在三个地方同时动刀——改完还不一定对。

说实话,这不怪你。Tkinter 的官方示例本来就是这么写的,入门教程也是。但当项目规模超过 500 行,这种写法的代价就开始显现了:界面逻辑和业务逻辑完全耦合,测试无从下手,维护像拆炸弹。

MVP 模式能解决这个问题。不是那种"看起来很美但实际用不上"的架构理论,而是真正能让你的 Tkinter 项目活得更久、改得更顺的工程实践。


🧩 先搞清楚 MVP 到底在解决什么

MVP 是 Model-View-Presenter 的缩写。三层各司其职:

  • ▸ Model:纯粹的业务逻辑和数据操作,不知道界面的存在
  • ▸ View:只管显示和接收用户输入,不做任何业务判断
  • ▸ Presenter:中间人,协调 Model 和 View 之间的通信

你可能听过 MVC,MVP 和它很像,但有个关键区别——在 MVP 里,View 和 Model 完全不认识对方,所有交互都通过 Presenter 中转。这在 Tkinter 这种事件驱动框架里特别合适,因为 Tkinter 的控件本身就是"哑的"——它只负责渲染,不应该懂业务。

用一个生活比喻:Model 是厨房,View 是餐桌,Presenter 是服务员。客人(用户)在餐桌点单,服务员去厨房传达,厨房做好了菜,服务员端上来。厨房不需要知道餐桌长什么样,餐桌也不需要知道菜怎么做的。


🏗️ 项目结构设计

咱们用一个"用户管理"的小应用来演示,功能很简单:查询用户列表、添加用户。但麻雀虽小,五脏俱全。

1user_manager/2├── main.py              # 入口文件3├── model/4│   ├── __init__.py5│   └── user_model.py    # 数据与业务逻辑6├── view/7│   ├── __init__.py8│   └── user_view.py     # Tkinter 界面9└── presenter/10    ├── __init__.py11    └── user_presenter.py  # 协调层

这个结构不是强制的,但目录分层能让你在打开文件之前就知道去哪里找东西——这本身就是价值。


🔧 Model 层:业务逻辑的家

Model 层是最纯粹的部分。它不 import tkinter,不操作任何控件,就是老老实实处理数据。

python1from dataclasses import dataclass, field2from typing import ListOptional3import re456@dataclass7class User:8"""用户数据实体"""9    user_id: int10    name: str11    email: str121314class UserModel:15"""16    用户业务逻辑层。17    注意:这里完全没有任何 Tkinter 相关代码。18    可以独立运行,可以独立测试。19    """2021def __init__(self):22# 模拟数据库,实际项目替换成 SQLite 或其他存储23        self._users: List[User] = [24User(1"张三""zhangsan@example.com"),25User(2"李四""lisi@example.com"),26User(3"王五""wangwu@example.com"),27        ]28        self._next_id = 42930def get_all_users(self) -> List[User]:31"""返回所有用户"""32return list(self._users)3334def add_user(self, name: str, email: str) -> tuple[boolstr]:35"""36        添加用户,返回 (成功标志, 消息)。37        业务校验逻辑全部在这里,不在界面里。38        """39        name = name.strip()40        email = email.strip()4142if not name:43return False"姓名不能为空"4445if not self._is_valid_email(email):46return False"邮箱格式不正确"4748if any(u.email == email for u in self._users):49return Falsef"邮箱 {email} 已被注册"5051        new_user = User(self._next_id, name, email)52        self._users.append(new_user)53        self._next_id += 154return Truef"用户 {name} 添加成功"5556def search_users(self, keyword: str) -> List[User]:57"""按姓名或邮箱模糊搜索"""58        keyword = keyword.lower().strip()59if not keyword:60return self.get_all_users()61return [62            u for u in self._users63if keyword in u.name.lower() or keyword in u.email.lower()64        ]6566@staticmethod67def _is_valid_email(email: str) -> bool:68        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'69return bool(re.match(pattern, email))

注意 add_user 返回的是 (bool, str) 元组,而不是直接弹窗提示。Model 不应该知道怎么展示错误,它只负责告诉调用方"成功了还是失败了,以及原因"。这个细节很多人会搞错。


🖼️ View 层:只管显示,不管逻辑

View 层的核心原则是:它是"哑的"。它暴露控件的值,接收外部的指令,但自己不做任何判断。

python1import tkinter as tk2from tkinter import ttk, messagebox3from typing import CallableListOptional456class UserView:7"""8    用户管理界面。9    不包含任何业务逻辑,所有用户操作通过回调通知 Presenter。10    """11def __init__(self, root: tk.Tk):12        self.root = root13        self.root.title("用户管理系统")14        self.root.geometry("700x500")15        self.root.resizable(TrueTrue)1617# 回调函数,由 Presenter 注入18        self._on_add_user: Optional[Callable] = None19        self._on_search: Optional[Callable] = None20        self._on_refresh: Optional[Callable] = None2122        self._build_ui()2324def _build_ui(self):25"""构建界面布局"""26# ── 顶部搜索区 ──        search_frame = ttk.LabelFrame(self.root, text="搜索", padding=8)27        search_frame.pack(fill=tk.X, padx=10, pady=(105))2829        self._search_var = tk.StringVar()30        ttk.Entry(search_frame, textvariable=self._search_var, width=30).pack(31            side=tk.LEFT, padx=(08)32        )33        ttk.Button(34            search_frame, text="搜索",35            command=self._trigger_search36        ).pack(side=tk.LEFT, padx=(04))37        ttk.Button(38            search_frame, text="刷新列表",39            command=self._trigger_refresh40        ).pack(side=tk.LEFT)4142# ── 中部列表区 ──        list_frame = ttk.LabelFrame(self.root, text="用户列表", padding=8)43        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)4445        columns = ("ID""姓名""邮箱")46        self._tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=12)47for col in columns:48            self._tree.heading(col, text=col)49        self._tree.column("ID", width=50, anchor=tk.CENTER)50        self._tree.column("姓名", width=120)51        self._tree.column("邮箱", width=250)5253        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self._tree.yview)54        self._tree.configure(yscrollcommand=scrollbar.set)55        self._tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)56        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)5758# ── 底部添加区 ──        add_frame = ttk.LabelFrame(self.root, text="添加用户", padding=8)59        add_frame.pack(fill=tk.X, padx=10, pady=(510))6061        ttk.Label(add_frame, text="姓名:").grid(row=0, column=0, padx=(04))62        self._name_var = tk.StringVar()63        ttk.Entry(add_frame, textvariable=self._name_var, width=20).grid(row=0, column=1, padx=(012))6465        ttk.Label(add_frame, text="邮箱:").grid(row=0, column=2, padx=(04))66        self._email_var = tk.StringVar()67        ttk.Entry(add_frame, textvariable=self._email_var, width=28).grid(row=0, column=3, padx=(012))6869        ttk.Button(70            add_frame, text="添加",71            command=self._trigger_add_user72        ).grid(row=0, column=4)7374# ── 状态栏 ──        self._status_var = tk.StringVar(value="就绪")75        ttk.Label(self.root, textvariable=self._status_var, relief=tk.SUNKEN, anchor=tk.W).pack(76            fill=tk.X, padx=10, pady=(06)77        )7879# ── 对外暴露的数据获取接口 ──80def get_name_input(self) -> str:81return self._name_var.get()8283def get_email_input(self) -> str:84return self._email_var.get()8586def get_search_keyword(self) -> str:87return self._search_var.get()8889# ── 对外暴露的展示接口 ──90def display_users(self, users: list):91"""刷新列表显示"""92        self._tree.delete(*self._tree.get_children())93for user in users:94            self._tree.insert("", tk.END, values=(user.user_id, user.name, user.email))9596def show_success(self, message: str):97        self._status_var.set(f"✓ {message}")98        messagebox.showinfo("成功", message)99100def show_error(self, message: str):101        self._status_var.set(f"✗ {message}")102        messagebox.showerror("错误", message)103104def clear_add_form(self):105        self._name_var.set("")106        self._email_var.set("")107108# ── 回调注册接口(由 Presenter 调用)──109110def bind_add_user(self, callback: Callable):111        self._on_add_user = callback112113def bind_search(self, callback: Callable):114        self._on_search = callback115116def bind_refresh(self, callback: Callable):117        self._on_refresh = callback118119# ── 内部触发器 ──120def _trigger_add_user(self):121if self._on_add_user:122            self._on_add_user()123124def _trigger_search(self):125if self._on_search:126            self._on_search()127128def _trigger_refresh(self):129if self._on_refresh:130            self._on_refresh()

这里有个设计细节值得注意:View 里的按钮命令指向的是 _trigger_xxx 这类内部方法,而不是直接调用外部回调。这样做的好处是,即使 Presenter 还没绑定,也不会抛出 NoneType 报错——界面可以独立启动预览,这在开发阶段很有用。


🎯 Presenter 层:把两边黏合起来

Presenter 是整个 MVP 的灵魂。它持有 Model 和 View 的引用,把 View 的事件翻译成 Model 的调用,再把 Model 的结果翻译成 View 的展示。

python1from model.user_model import UserModel2from view.user_view import UserView345class UserPresenter:6"""7    用户管理 Presenter。8    协调 Model 和 View,自身不包含业务逻辑,也不操作控件。9    """1011def __init__(self, model: UserModel, view: UserView):12        self._model = model13        self._view = view14        self._bind_events()15        self._load_initial_data()1617def _bind_events(self):18"""将 View 的用户操作绑定到对应的处理方法"""19        self._view.bind_add_user(self._handle_add_user)20        self._view.bind_search(self._handle_search)21        self._view.bind_refresh(self._handle_refresh)2223def _load_initial_data(self):24"""初始加载数据"""25        users = self._model.get_all_users()26        self._view.display_users(users)2728def _handle_add_user(self):29"""处理添加用户事件"""30        name = self._view.get_name_input()31        email = self._view.get_email_input()3233        success, message = self._model.add_user(name, email)3435if success:36            self._view.show_success(message)37            self._view.clear_add_form()38# 添加成功后刷新列表39            self._handle_refresh()40else:41            self._view.show_error(message)4243def _handle_search(self):44"""处理搜索事件"""45        keyword = self._view.get_search_keyword()46        users = self._model.search_users(keyword)47        self._view.display_users(users)4849def _handle_refresh(self):50"""刷新完整列表"""51        users = self._model.get_all_users()52        self._view.display_users(users)

看到没,Presenter 的每个方法都是这个套路:从 View 取数据 → 调 Model 处理 → 把结果交给 View 展示。逻辑清晰到像在读流程图。


🚀 入口文件:组装三层

python1# main.py23import tkinter as tk4from model.user_model import UserModel5from view.user_view import UserView6from presenter.user_presenter import UserPresenter789def main():10    root = tk.Tk()1112# 依赖注入:手动组装三层13    model = UserModel()14    view = UserView(root)15    presenter = UserPresenter(model, view)  # noqa: F841(presenter 持有引用,不能被 GC)1617    root.mainloop()181920if __name__ == "__main__":21main()

就这么简单。三行组装,清晰得不能再清晰。


✅ MVP 带来的实际收益

可测试性是最直接的收益。以前你没法单元测试界面逻辑,因为业务代码和 Tkinter 控件绑死了。现在 Model 完全独立,直接测:

python1# tests/test_user_model.py23import pytest4from model.user_model import UserModel567def test_add_valid_user():8    model = UserModel()9    success, msg = model.add_user("赵六""zhaoliu@test.com")10assert success is True11assert "赵六" in msg121314def test_add_duplicate_email():15    model = UserModel()16    model.add_user("测试用户""dup@test.com")17    success, msg = model.add_user("另一个用户""dup@test.com")18assert success is False19assert "已被注册" in msg202122def test_add_invalid_email():23    model = UserModel()24    success, msg = model.add_user("用户A""not-an-email")25assert success is False

不需要启动 Tkinter,不需要模拟点击,直接跑,秒出结果。这种测试体验,在耦合代码里是奢望。

可维护性也大幅提升。产品说"把邮箱校验规则改一下",你只改 UserModel._is_valid_email,一处改动,全局生效,完全不用担心界面那边。反过来,UI 改版换个更现代的布局,Model 和 Presenter 完全不动。


⚠️ 几个容易踩的坑

坑一:Presenter 里偷偷写了业务逻辑。 比如在 _handle_add_user 里直接做邮箱格式判断,而不是交给 Model。这是最常见的滑坡,一旦开了头,Presenter 就会越来越臃肿,最终退化成另一种形式的大杂烩。

坑二:View 里偷偷读了 Model 的数据。 有时候图方便,直接在 View 里 import model 取数据,绕过了 Presenter。这种"走后门"的行为会让架构的边界变得模糊,调试时你会发现数据流向完全说不清楚。

坑三:忘记处理 Tkinter 的线程安全问题。 如果 Model 里有耗时操作(比如网络请求、文件读写),一定要用 threading 异步执行,然后通过 root.after() 把结果安全地回传给 View。直接在子线程里操作 Tkinter 控件,早晚出幺蛾子。


💬 最后说几句

MVP 不是银弹,小脚本用不着这套。但只要你的 Tkinter 项目超过了"一次性工具"的规模,打算长期维护、团队协作,或者需要写测试——那 MVP 就值得投入这点前期设计成本。

我在实际项目里推行这个模式后,最明显的感受是:新人接手代码时不再一脸懵。他们打开目录,看到 model/view/presenter 三个文件夹,立刻就知道去哪里找什么。这种"代码可读性",比任何注释都管用。

欢迎在评论区聊聊你在 Tkinter 项目里遇到的架构问题,或者你用过的其他解耦方式——比如 MVC、MVVM 在 Python 桌面开发里的实践经验,大家一起探讨。


#Python#Tkinter#MVP架构#桌面开发#软件设计模式

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 10:32:07 HTTP/2.0 GET : https://f.mffb.com.cn/a/501299.html
  2. 运行时间 : 0.100545s [ 吞吐率:9.95req/s ] 内存消耗:4,825.87kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=fa0093d6e3634811a30935905eb5ffcc
  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.000576s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000795s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000310s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000273s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000479s ]
  6. SELECT * FROM `set` [ RunTime:0.000198s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000555s ]
  8. SELECT * FROM `article` WHERE `id` = 501299 LIMIT 1 [ RunTime:0.003059s ]
  9. UPDATE `article` SET `lasttime` = 1783045927 WHERE `id` = 501299 [ RunTime:0.006690s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000365s ]
  11. SELECT * FROM `article` WHERE `id` < 501299 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000595s ]
  12. SELECT * FROM `article` WHERE `id` > 501299 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000491s ]
  13. SELECT * FROM `article` WHERE `id` < 501299 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000954s ]
  14. SELECT * FROM `article` WHERE `id` < 501299 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002622s ]
  15. SELECT * FROM `article` WHERE `id` < 501299 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.014791s ]
0.102125s