当前位置:首页>python>基于 Python 标准库 tkinter 的桌面 GUI 程序

基于 Python 标准库 tkinter 的桌面 GUI 程序

  • 2026-08-18 23:11:33
基于 Python 标准库 tkinter 的桌面 GUI 程序

🔐 验证码方式演示器 项目文档

一个基于 Python 标准库 tkinter 的桌面 GUI 程序,集中演示 5 种常见验证码 的实现原理与验证效果。零第三方依赖,双击即可运行。


目录

一、项目简介

验证码方式演示器captcha_demo.py)是一个用于教学与演示的桌面小工具,把业界最常见的几类人机验证方式集中在一个窗口里,直观展示它们各自的实现原理和验证交互过程。

项目属性
说明
语言
Python 3
GUI 框架
tkinter
(Python 内置,无需安装)
第三方依赖
代码规模
约 579 行,单文件
主题风格
浅色扁平化,与同目录 datetime_viewer.py 保持一致
运行平台
Windows / macOS / Linux(凡带 tkinter 的 Python 均可)

设计目标:

  • 用最小的依赖(仅标准库)还原真实验证码的核心逻辑
  • 每种方式都给出「原理说明 + 可交互演示 + 即时验证反馈」
  • 验证失败时展示失败原因(正确答案、偏差像素、点击顺序等),便于学习理解

⚠️ 说明:本项目为原理演示用途,所有校验均在客户端本地完成,不具备真实生产环境所需的服务端安全强度(如轨迹风控、加密下发等),请勿直接用于生产。


二、快速开始

环境要求

  • 已安装 Python 3.x
  • Python 自带 tkinter(Windows / macOS 官方安装包默认包含;部分 Linux 需 sudo apt install python3-tk

运行方式

python captcha_demo.py

程序启动后会弹出一个 640 × 560 的窗口并自动居中,顶部为标题,下方为 5 个标签页(Tab),点击切换即可体验不同验证码。


三、界面总览

┌───────────────────────────────────────────────┐│              🔐 验证码方式演示器                 ││         展示常见验证码的实现原理与验证效果         │├───────────────────────────────────────────────┤│ [🔤字符] [➕算术] [🧩滑块] [👆点选] [📱短信]      │  ← Notebook 标签页│                                                 ││   ┌─────────────────────────────────────┐       ││   │        当前标签页的演示内容            │       ││   │   (画布 / 输入框 / 按钮 / 结果反馈)    │       ││   └─────────────────────────────────────┘       ││                                                 │└───────────────────────────────────────────────┘
  • 使用 ttk.Notebook 组织 5 个功能页
  • 每页统一结构:标题 → 原理说明 → 交互区 → 结果反馈
  • 验证结果统一用 绿色 ✅ / 红色 ❌ 表示成功或失败

四、整体架构

程序由单一类 CaptchaDemo 承载,构造函数创建主窗口并调用 _build_ui() 组装界面。5 种验证码各自对应一组「构建 + 刷新 + 验证」方法,彼此独立、互不干扰。

类结构一览

CaptchaDemo├── __init__()                 初始化窗口、居中、调用 _build_ui├── _build_ui()                创建标题 + Notebook,循环挂载 5 个 Tab├── 【通用工具】│   ├── _result_label()        创建结果反馈标签│   ├── _show_result()         统一设置成功/失败文案与颜色│   ├── _hint()                创建灰色原理说明文字│   └── _draw_noise()          在画布上绘制干扰线 + 噪点├── 【Tab1 字符验证码】│   ├── _build_char_tab()│   ├── _refresh_char_captcha()│   └── _verify_char()├── 【Tab2 算术验证码】│   ├── _build_math_tab()│   ├── _refresh_math_captcha()│   └── _verify_math()├── 【Tab3 滑块验证码】│   ├── _build_slider_tab()│   ├── _reset_slider()│   ├── _slider_press()  / _slider_drag() / _slider_release()├── 【Tab4 点选验证码】│   ├── _build_click_tab()│   ├── _reset_click_captcha()│   └── _on_click_captcha()├── 【Tab5 短信验证码】│   ├── _build_sms_tab()│   ├── _send_sms()│   ├── _sms_tick()            倒计时 + 有效期刷新(after 循环)│   └── _verify_sms()└── run()                      进入 mainloop

全局配置

文件顶部集中定义了配色常量(BG / CARD / ACCENT / GREEN / RED 等)、字体常量(F_TITLE / F_BODY / F_MONO 等)以及三个字符池:

常量
用途
CHAR_POOL
字符验证码字符集,刻意去除易混淆的 0 O 1 l I
HANZI_POOL
点选验证码的候选汉字
NOISE_COLORS
 / CHAR_COLORS
干扰元素与字符的随机配色池

五、五种验证码详解

1. 字符图形验证码

原理: 随机生成 4 位字符,逐个渲染到画布上,每个字符施加随机的位置偏移、旋转角度、字号和颜色,再叠加干扰线与噪点,使机器难以通过 OCR 识别,而人眼仍可辨认。

关键实现(_refresh_char_captcha):

  • random.choices(CHAR_POOL, k=4) 生成验证码文本
  • 循环绘制每个字符:x 基准间隔 50px ± 6px 抖动,y 上下 ±10px,angle 在 -30°~30° 之间,字号 22~30 随机
  • 调用 _draw_noise() 铺设 6 条干扰线 + 60 个噪点

验证逻辑(_verify_char):

  • 提供「区分大小写」复选框,勾选时严格比较,否则统一转小写比较
  • 失败时直接显示正确答案,便于学习
  • 成功后 1.5 秒自动刷新一张新图
  • 点击图片或「🔄 换一张」按钮均可刷新

2. 算术验证码

原理: 随机生成一道简单四则运算题(加 / 减 / 乘),渲染成扭曲图片。用户需先理解语义再计算,比纯字符识别多一层认知门槛。

关键实现(_refresh_math_captcha):

运算
取值范围
说明
加法 +
1~50 + 1~50
结果可能超过 50
减法 -
10~99 − 1~9
保证结果为正
乘法 ×
2~9 × 2~9
结果为两位数以内
  • 算式形如 37 + 8 = ?,逐字符扭曲绘制(角度 ±15°)
  • 减法特意让被减数远大于减数,避免出现负数

验证逻辑(_verify_math):

  • 用 int() 解析输入,非数字自动判为失败
  • 支持回车键直接提交
  • 失败显示正确答案,成功后 1.5 秒换新题

3. 滑块拼图验证码

原理: 页面展示一张带缺口的背景图和一个拼图块,用户拖动滑块使拼图块水平对齐缺口。真实系统除校验最终落点外,还会分析拖动轨迹(人手拖动存在细微抖动和加减速,而机器脚本轨迹往往过于平滑匀速)。

关键实现:

  • 两块画布:上方 slider_canvas 显示背景 + 缺口 + 拼图块,下方 track_canvas 是可拖动的滑轨
  • _reset_slider():随机生成缺口横坐标 gap_x(避开左侧起始区),拼图块起点固定在最左
  • 滑轨行程通过比例 ratio 映射到画布行程,实现滑块与拼图块联动

三段式拖动事件:

事件
方法
作用
按下
_slider_press
判断是否点在滑块上,记录起始轨迹点
拖动
_slider_drag
移动滑块与拼图块,持续采集 (时间, x) 轨迹
释放
_slider_release
计算与缺口的偏差,判定是否在容差内

验证逻辑:

  • 偏差 ≤ TOLERANCE(6px)判为通过,拼图块变绿
  • 结果同时展示偏差像素、拖动耗时、轨迹采样点数,模拟真实的轨迹分析维度
  • 失败后 1.2 秒自动重置

4. 文字点选验证码

原理: 在图片上随机散布多个汉字,要求用户按指定顺序点击其中几个。同时考验文字识别能力与语序理解,是「12306」式验证码的简化版。

关键实现(_reset_click_captcha):

  • 从 HANZI_POOL 中 random.sample 选取 5 个不重复汉字,前 3 个作为需点击的目标
  • 随机摆放时通过距离平方判断(间距 > 70px)尽量避免文字重叠,最多尝试 50 次
  • 顶部提示栏动态显示点击顺序,如「请依次点击:「山」→「水」→「月」」

验证逻辑(_on_click_captcha):

  • 点击时以 CLICK_R(22px)为判定半径寻找命中的汉字
  • 每命中一个即绘制序号圈标记,并记入 clicked_seq
  • 点满 3 个后比对顺序:完全一致则通过,否则显示实际点击顺序并在 1.5 秒后重置

5. 短信验证码(模拟)

原理: 服务端生成 6 位随机数字下发到用户手机,附带有效期重发冷却限制,用于验证「用户持有该手机号」这一事实。本项目用一个「模拟短信收件箱」面板展示收到的验证码。

关键实现:

  • 手机号校验: 必须是以 1 开头的 11 位纯数字
  • 验证码生成:random.choices(string.digits, k=6)
  • 脱敏显示: 手机号中间四位用 **** 遮挡(138****1234
  • 倒计时机制(_sms_tick): 通过 root.after(1000, ...) 每秒刷新
    • 重发冷却:SMS_RESEND_SEC(60 秒)内按钮禁用并显示剩余秒数
    • 有效期:SMS_VALID_SEC(120 秒)后验证码自动失效

验证逻辑(_verify_sms):

  • 未发送 / 已过期时给出对应提示
  • 验证成功后清空验证码、取消倒计时任务、恢复发送按钮
  • 支持回车键提交

六、通用设计

干扰绘制 _draw_noise

被字符、算术、点选三种验证码复用,在指定画布上绘制随机干扰线与噪点,参数 lines / dots 可控制干扰强度。

结果反馈 _show_result

统一的成功 / 失败展示入口:成功显示绿色文案,失败显示红色文案,各 Tab 传入自定义的成功 / 失败文本。

定时任务

  • 验证成功后的自动刷新,全部使用 root.after(毫秒, 回调) 实现,不阻塞主线程
  • 短信倒计时用 after 自循环,并在成功 / 过期时通过 after_cancel 及时取消,避免任务堆积

七、配置参数速查

以下参数均定义为类属性或模块常量,可按需调整:

参数
默认值
含义
窗口尺寸
640 × 560
主窗口大小(不可缩放)
TOLERANCE6
滑块对齐允许的像素误差
SLIDER_W / SLIDER_H480 / 160
滑块背景画布尺寸
PIECE_SIZE44
拼图块边长
CLICK_W / CLICK_H480 / 200
点选画布尺寸
CLICK_R22
点选命中判定半径
SMS_RESEND_SEC60
短信重发冷却(秒)
SMS_VALID_SEC120
短信验证码有效期(秒)
字符验证码位数
4
见 _refresh_char_captcha 中的 k=4
点选目标个数
3
见 chars[:3]

八、常见问题

Q1:运行报错 ModuleNotFoundError: No module named 'tkinter'A:部分 Linux 发行版未默认安装 tkinter,执行 sudo apt install python3-tk(Debian/Ubuntu)即可。Windows / macOS 官方 Python 一般自带。

Q2:字符 / 汉字显示为方框或乱码?A:这是字体缺失问题。程序使用「Consolas」和「Microsoft YaHei」,非 Windows 系统可将代码中的字体名替换为系统已有字体。

Q3:滑块拖不动?A:需按住滑轨上的蓝色滑块(而非空白轨道)再拖动,_slider_press 只在点中滑块时才启动拖动。

Q4:这个能防住真实的机器人吗?A:不能。本项目所有验证均在客户端完成,仅用于演示原理。真实场景需服务端生成、校验,并配合加密、风控、轨迹分析等手段。


九、可扩展方向

  • 🔄 旋转图片验证码:要求把倾斜图片转正
  • 🖱️ 行为无感验证:采集鼠标移动轨迹自动判定人机
  • 📊 成功率统计面板:记录各验证码的尝试次数与通过率
  • 🖼️ 接入 Pillow:用真实图片素材替代画布绘制,效果更接近生产
  • 🌐 前后端分离演示:拆出一个 mock 服务端,体现「服务端校验」的正确姿势

十、完整源代码

文件路径:captcha_demo.py

# -*- coding: utf-8 -*-"""验证码方式演示器 - 浅色主题 GUI功能:展示常见验证码的实现与验证效果  1. 字符图形验证码(扭曲字符 + 干扰线/噪点)  2. 算术验证码(随机四则运算)  3. 滑块拼图验证码(拖动滑块对齐缺口)  4. 文字点选验证码(按顺序点击指定文字)  5. 短信验证码(模拟发送 + 倒计时 + 有效期)依赖:tkinter(内置)"""import tkinter as tkfrom tkinter import ttkimport randomimport stringimport time# ══════ 浅色主题配色(与 datetime_viewer 保持一致)══════BG = "#f8fafc"CARD = "#ffffff"BORDER = "#e2e8f0"TEXT = "#1e293b"TEXT_SEC = "#64748b"TEXT_WEAK = "#94a3b8"ACCENT = "#3b82f6"GREEN = "#10b981"YELLOW = "#f59e0b"RED = "#ef4444"BTN_BG = "#eff6ff"BTN_FG = "#1d4ed8"INPUT_BG = "#f1f5f9"F_TITLE = ("Microsoft YaHei"11"bold")F_BODY = ("Microsoft YaHei"10)F_SMALL = ("Microsoft YaHei"9)F_MONO = ("Consolas"11)F_BIG_MONO = ("Consolas"20"bold")# 验证码字符集(去掉易混淆的 0O1lI)CHAR_POOL = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz"# 点选验证码汉字池HANZI_POOL = "春夏秋冬风花雪月山水木火土日星云雨虹桥船车马牛羊鱼鸟龙"# 干扰色NOISE_COLORS = ["#94a3b8""#cbd5e1""#fca5a5""#93c5fd""#86efac""#fcd34d"]CHAR_COLORS = ["#1d4ed8""#dc2626""#059669""#7c3aed""#db2777""#ea580c"]classCaptchaDemo:def__init__(self):        self.root = tk.Tk()        self.root.title("验证码方式演示器")        sw = self.root.winfo_screenwidth()        sh = self.root.winfo_screenheight()        w, h = 640560        self.root.geometry(f"{w}x{h}+{(sw-w)//2}+{(sh-h)//2}")        self.root.configure(bg=BG)        self.root.resizable(FalseFalse)        self._build_ui()# ══════ 总体布局 ══════def_build_ui(self):        tk.Label(self.root, text="🔐 验证码方式演示器", font=("Microsoft YaHei"14"bold"),                 bg=BG, fg=TEXT).pack(pady=(142))        tk.Label(self.root, text="展示常见验证码的实现原理与验证效果",                 font=F_SMALL, bg=BG, fg=TEXT_SEC).pack(pady=(08))        style = ttk.Style()        style.configure("TNotebook", background=BG)        style.configure("TNotebook.Tab", background=BTN_BG, foreground=TEXT,                        padding=[104], font=F_SMALL)        style.map("TNotebook.Tab", background=[("selected", CARD)],                  foreground=[("selected", ACCENT)])        notebook = ttk.Notebook(self.root)        notebook.pack(fill="both", expand=True, padx=16, pady=(012))        tabs = [            ("  🔤 字符验证码  ", self._build_char_tab),            ("  ➕ 算术验证码  ", self._build_math_tab),            ("  🧩 滑块验证码  ", self._build_slider_tab),            ("  👆 点选验证码  ", self._build_click_tab),            ("  📱 短信验证码  ", self._build_sms_tab),        ]for text, builder in tabs:            frame = tk.Frame(notebook, bg=CARD)            notebook.add(frame, text=text)            builder(frame)# ══════ 通用小部件 ══════def_result_label(self, parent):        lbl = tk.Label(parent, text="", font=F_TITLE, bg=CARD, fg=TEXT_SEC)        lbl.pack(anchor="w", padx=16, pady=(84))return lbldef_show_result(self, lbl, ok, ok_text="✅ 验证通过", fail_text="❌ 验证失败"):        lbl.configure(text=ok_text if ok else fail_text, fg=GREEN if ok else RED)def_hint(self, parent, text):        tk.Label(parent, text=text, font=F_SMALL, bg=CARD, fg=TEXT_WEAK,                 wraplength=560, justify="left").pack(anchor="w", padx=16, pady=(20))# ════════════════════════════════════════# Tab1: 字符图形验证码# ════════════════════════════════════════def_build_char_tab(self, parent):        tk.Label(parent, text="字符图形验证码", font=F_TITLE, bg=CARD, fg=TEXT).pack(            anchor="w", padx=16, pady=(142))        self._hint(parent, "原理:随机字符渲染成图片,加入旋转、错位、干扰线和噪点,""机器难以 OCR 识别。点击图片可刷新。")        self.char_canvas = tk.Canvas(parent, width=220, height=70, bg="#f1f5f9",                                     highlightthickness=1, highlightbackground=BORDER)        self.char_canvas.pack(anchor="w", padx=16, pady=10)        self.char_canvas.bind("<Button-1>"lambda e: self._refresh_char_captcha())        row = tk.Frame(parent, bg=CARD)        row.pack(fill="x", padx=16, pady=4)        tk.Label(row, text="输入验证码:", font=F_BODY, bg=CARD, fg=TEXT).pack(side="left")        self.char_input = tk.Entry(row, font=F_MONO, bg=INPUT_BG, fg=TEXT,                                   relief="flat", bd=1, width=10)        self.char_input.pack(side="left", padx=6, ipady=3)        tk.Button(row, text="验证", font=F_SMALL, bg=ACCENT, fg="#ffffff", relief="flat",                  padx=12, command=self._verify_char).pack(side="left", padx=4)        tk.Button(row, text="🔄 换一张", font=F_SMALL, bg=BTN_BG, fg=BTN_FG, relief="flat",                  padx=8, command=self._refresh_char_captcha).pack(side="left", padx=4)        self.char_case_var = tk.BooleanVar(value=False)        tk.Checkbutton(parent, text="区分大小写", variable=self.char_case_var,                       font=F_SMALL, bg=CARD, fg=TEXT_SEC, activebackground=CARD,                       selectcolor=CARD).pack(anchor="w", padx=16)        self.char_result = self._result_label(parent)        self._refresh_char_captcha()def_draw_noise(self, canvas, w, h, lines=6, dots=60):"""绘制干扰线与噪点"""for _ in range(lines):            canvas.create_line(random.randint(0, w), random.randint(0, h),                               random.randint(0, w), random.randint(0, h),                               fill=random.choice(NOISE_COLORS), width=random.choice([12]))for _ in range(dots):            x, y = random.randint(0, w), random.randint(0, h)            canvas.create_oval(x, y, x + 2, y + 2,                               fill=random.choice(NOISE_COLORS), outline="")def_refresh_char_captcha(self):        self.char_canvas.delete("all")        self.char_code = "".join(random.choices(CHAR_POOL, k=4))        w, h = 22070        self._draw_noise(self.char_canvas, w, h)# 逐字符绘制:随机位置偏移 + 随机角度 + 随机颜色for i, ch in enumerate(self.char_code):            x = 35 + i * 50 + random.randint(-66)            y = 35 + random.randint(-1010)            angle = random.randint(-3030)            size = random.randint(2230)            self.char_canvas.create_text(                x, y, text=ch, font=("Consolas", size, "bold"),                fill=random.choice(CHAR_COLORS), angle=angle)        self.char_result.configure(text="")        self.char_input.delete(0"end")def_verify_char(self):        user = self.char_input.get().strip()if self.char_case_var.get():            ok = user == self.char_codeelse:            ok = user.lower() == self.char_code.lower()        self._show_result(self.char_result, ok,                          fail_text=f"❌ 验证失败(正确答案: {self.char_code})")if ok:            self.root.after(1500, self._refresh_char_captcha)# ════════════════════════════════════════# Tab2: 算术验证码# ════════════════════════════════════════def_build_math_tab(self, parent):        tk.Label(parent, text="算术验证码", font=F_TITLE, bg=CARD, fg=TEXT).pack(            anchor="w", padx=16, pady=(142))        self._hint(parent, "原理:随机生成简单算式渲染成图片,用户需理解语义并计算,""比纯字符识别多一层门槛。")        self.math_canvas = tk.Canvas(parent, width=240, height=70, bg="#f1f5f9",                                     highlightthickness=1, highlightbackground=BORDER)        self.math_canvas.pack(anchor="w", padx=16, pady=10)        self.math_canvas.bind("<Button-1>"lambda e: self._refresh_math_captcha())        row = tk.Frame(parent, bg=CARD)        row.pack(fill="x", padx=16, pady=4)        tk.Label(row, text="计算结果:", font=F_BODY, bg=CARD, fg=TEXT).pack(side="left")        self.math_input = tk.Entry(row, font=F_MONO, bg=INPUT_BG, fg=TEXT,                                   relief="flat", bd=1, width=10)        self.math_input.pack(side="left", padx=6, ipady=3)        self.math_input.bind("<Return>"lambda e: self._verify_math())        tk.Button(row, text="验证", font=F_SMALL, bg=ACCENT, fg="#ffffff", relief="flat",                  padx=12, command=self._verify_math).pack(side="left", padx=4)        tk.Button(row, text="🔄 换一题", font=F_SMALL, bg=BTN_BG, fg=BTN_FG, relief="flat",                  padx=8, command=self._refresh_math_captcha).pack(side="left", padx=4)        self.math_result = self._result_label(parent)        self._refresh_math_captcha()def_refresh_math_captcha(self):        self.math_canvas.delete("all")        op = random.choice(["+""-""×"])if op == "+":            a, b = random.randint(150), random.randint(150)            self.math_answer = a + belif op == "-":            a, b = random.randint(1099), random.randint(19)            self.math_answer = a - belse:            a, b = random.randint(29), random.randint(29)            self.math_answer = a * b        w, h = 24070        self._draw_noise(self.math_canvas, w, h, lines=5, dots=40)        expr = f"{a}{op}{b} = ?"# 逐字符绘制并加随机扰动        x = 30for ch in expr:            y = 35 + random.randint(-66)            angle = random.randint(-1515)            self.math_canvas.create_text(                x, y, text=ch, font=("Consolas"24"bold"),                fill=random.choice(CHAR_COLORS), angle=angle)            x += 24        self.math_result.configure(text="")        self.math_input.delete(0"end")def_verify_math(self):        user = self.math_input.get().strip()try:            ok = int(user) == self.math_answerexcept ValueError:            ok = False        self._show_result(self.math_result, ok,                          fail_text=f"❌ 验证失败(正确答案: {self.math_answer})")if ok:            self.root.after(1500, self._refresh_math_captcha)# ════════════════════════════════════════# Tab3: 滑块拼图验证码# ════════════════════════════════════════    SLIDER_W, SLIDER_H = 480160    PIECE_SIZE = 44    TOLERANCE = 6# 允许误差(像素)def_build_slider_tab(self, parent):        tk.Label(parent, text="滑块拼图验证码", font=F_TITLE, bg=CARD, fg=TEXT).pack(            anchor="w", padx=16, pady=(142))        self._hint(parent, "原理:拖动滑块使拼图块对齐背景缺口,服务端校验最终位置与""拖动轨迹(人手拖动有抖动,机器轨迹过于平滑)。")        self.slider_canvas = tk.Canvas(parent, width=self.SLIDER_W, height=self.SLIDER_H,                                       bg="#e0f2fe", highlightthickness=1,                                       highlightbackground=BORDER)        self.slider_canvas.pack(anchor="w", padx=16, pady=(106))# 滑轨        self.track_canvas = tk.Canvas(parent, width=self.SLIDER_W, height=44,                                      bg=INPUT_BG, highlightthickness=1,                                      highlightbackground=BORDER)        self.track_canvas.pack(anchor="w", padx=16, pady=(06))        self.track_canvas.bind("<Button-1>", self._slider_press)        self.track_canvas.bind("<B1-Motion>", self._slider_drag)        self.track_canvas.bind("<ButtonRelease-1>", self._slider_release)        row = tk.Frame(parent, bg=CARD)        row.pack(fill="x", padx=16, pady=2)        tk.Button(row, text="🔄 重置", font=F_SMALL, bg=BTN_BG, fg=BTN_FG, relief="flat",                  padx=8, command=self._reset_slider).pack(side="left")        self.slider_info = tk.Label(row, text="", font=F_SMALL, bg=CARD, fg=TEXT_WEAK)        self.slider_info.pack(side="left", padx=10)        self.slider_result = self._result_label(parent)        self._reset_slider()def_reset_slider(self):        c = self.slider_canvas        c.delete("all")        ps = self.PIECE_SIZE# 背景装饰(随机圆点模拟图片纹理)for _ in range(40):            x, y = random.randint(0, self.SLIDER_W), random.randint(0, self.SLIDER_H)            r = random.randint(416)            color = random.choice(["#bae6fd""#7dd3fc""#a5f3fc""#c7d2fe""#ddd6fe"])            c.create_oval(x - r, y - r, x + r, y + r, fill=color, outline="")# 缺口位置(避开左侧起始区)        self.gap_x = random.randint(ps + 100, self.SLIDER_W - ps - 20)        self.gap_y = random.randint(20, self.SLIDER_H - ps - 20)        c.create_rectangle(self.gap_x, self.gap_y, self.gap_x + ps, self.gap_y + ps,                           fill="#94a3b8", outline="#64748b", width=2, stipple="gray50")# 拼图块(起始在最左侧,与缺口同一水平线)        self.piece_x = 10        self.piece = c.create_rectangle(            self.piece_x, self.gap_y, self.piece_x + ps, self.gap_y + ps,            fill=ACCENT, outline="#1d4ed8", width=2)# 滑轨        t = self.track_canvas        t.delete("all")        t.create_text(self.SLIDER_W // 222, text="按住滑块,拖动完成拼图 →",                      font=F_SMALL, fill=TEXT_WEAK)        self.handle_x = 10        self.handle = t.create_rectangle(self.handle_x, 4, self.handle_x + 4040,                                         fill=ACCENT, outline="#1d4ed8", width=2)        t.create_text(self.handle_x + 2022, text="⇒", font=("Segoe UI"14"bold"),                      fill="white", tags="arrow")        self.dragging = False        self.drag_track = []  # 记录拖动轨迹 (时间, x)        self.slider_done = False        self.slider_result.configure(text="")        self.slider_info.configure(text=f"缺口位置 x={self.gap_x}(演示用,实际不可见)")def_slider_press(self, event):if self.slider_done:return# 只有点在滑块上才开始拖动if self.handle_x <= event.x <= self.handle_x + 40:            self.dragging = True            self.drag_offset = event.x - self.handle_x            self.drag_track = [(time.time(), event.x)]def_slider_drag(self, event):ifnot self.dragging or self.slider_done:return        ps = self.PIECE_SIZE        new_x = event.x - self.drag_offset        new_x = max(10, min(new_x, self.SLIDER_W - 50))        dx = new_x - self.handle_x        self.handle_x = new_x        self.track_canvas.move(self.handle, dx, 0)        self.track_canvas.move("arrow", dx, 0)# 拼图块同步移动(滑轨行程映射到画布行程)        ratio = (self.SLIDER_W - ps - 20) / (self.SLIDER_W - 60)        self.piece_x = 10 + (self.handle_x - 10) * ratio        coords = self.slider_canvas.coords(self.piece)        self.slider_canvas.move(self.piece, self.piece_x - coords[0], 0)        self.drag_track.append((time.time(), event.x))def_slider_release(self, event):ifnot self.dragging or self.slider_done:return        self.dragging = False        diff = abs(self.piece_x - self.gap_x)        ok = diff <= self.TOLERANCE# 简易轨迹分析:拖动耗时 + 采样点数if len(self.drag_track) >= 2:            duration = self.drag_track[-1][0] - self.drag_track[0][0]            points = len(self.drag_track)            track_info = f"耗时 {duration:.2f}s,采样 {points} 点"else:            track_info = "无有效轨迹"if ok:            self.slider_done = True            self.slider_canvas.itemconfigure(self.piece, fill=GREEN, outline="#059669")            self.slider_result.configure(                text=f"✅ 验证通过(偏差 {diff:.0f}px,{track_info})", fg=GREEN)else:            self.slider_result.configure(                text=f"❌ 未对齐缺口(偏差 {diff:.0f}px,容差 {self.TOLERANCE}px),已重置", fg=RED)            self.root.after(1200, self._reset_slider)# ════════════════════════════════════════# Tab4: 文字点选验证码# ════════════════════════════════════════    CLICK_W, CLICK_H = 480200    CLICK_R = 22# 点击判定半径def_build_click_tab(self, parent):        tk.Label(parent, text="文字点选验证码", font=F_TITLE, bg=CARD, fg=TEXT).pack(            anchor="w", padx=16, pady=(142))        self._hint(parent, "原理:图片上随机放置多个文字,要求按指定顺序点击其中几个,""同时考验文字识别与语序理解。")        self.click_tip = tk.Label(parent, text="", font=F_BODY, bg=CARD, fg=ACCENT)        self.click_tip.pack(anchor="w", padx=16, pady=(62))        self.click_canvas = tk.Canvas(parent, width=self.CLICK_W, height=self.CLICK_H,                                      bg="#fef9c3", highlightthickness=1,                                      highlightbackground=BORDER)        self.click_canvas.pack(anchor="w", padx=16, pady=6)        self.click_canvas.bind("<Button-1>", self._on_click_captcha)        row = tk.Frame(parent, bg=CARD)        row.pack(fill="x", padx=16, pady=2)        tk.Button(row, text="🔄 换一张", font=F_SMALL, bg=BTN_BG, fg=BTN_FG, relief="flat",                  padx=8, command=self._reset_click_captcha).pack(side="left")        self.click_result = self._result_label(parent)        self._reset_click_captcha()def_reset_click_captcha(self):        c = self.click_canvas        c.delete("all")        self._draw_noise(c, self.CLICK_W, self.CLICK_H, lines=4, dots=30)# 随机选 5 个不重复汉字,前 3 个为需点击目标        chars = random.sample(HANZI_POOL, 5)        self.click_targets = chars[:3]        self.click_positions = {}  # 字 -> (x, y)# 随机摆放,简单避免重叠        placed = []for ch in chars:for _ in range(50):                x = random.randint(40, self.CLICK_W - 40)                y = random.randint(35, self.CLICK_H - 35)if all((x - px) ** 2 + (y - py) ** 2 > 70 ** 2for px, py in placed):break            placed.append((x, y))            self.click_positions[ch] = (x, y)            c.create_text(x, y, text=ch,                          font=("Microsoft YaHei", random.randint(1826), "bold"),                          fill=random.choice(CHAR_COLORS),                          angle=random.randint(-2525), tags=f"char_{ch}")        self.clicked_seq = []        self.click_done = False        self.click_tip.configure(            text="请依次点击:" + " → ".join(f"「{t}」"for t in self.click_targets))        self.click_result.configure(text="")def_on_click_captcha(self, event):if self.click_done:return# 找最近的字        hit = Nonefor ch, (x, y) in self.click_positions.items():if (event.x - x) ** 2 + (event.y - y) ** 2 <= self.CLICK_R ** 2:                hit = chbreakif hit isNoneor hit in self.clicked_seq:return        self.clicked_seq.append(hit)        idx = len(self.clicked_seq)        x, y = self.click_positions[hit]# 标记序号        self.click_canvas.create_oval(x - 14, y - 14, x + 14, y + 14,                                      outline=ACCENT, width=2)        self.click_canvas.create_text(x + 20, y - 18, text=str(idx),                                      font=("Microsoft YaHei"10"bold"), fill=ACCENT)if idx == len(self.click_targets):            self.click_done = True            ok = self.clicked_seq == self.click_targets            seq = " → ".join(self.clicked_seq)            self._show_result(self.click_result, ok,                              ok_text=f"✅ 验证通过({seq})",                              fail_text=f"❌ 顺序错误(你点的是 {seq}),已重置")ifnot ok:                self.root.after(1500, self._reset_click_captcha)# ════════════════════════════════════════# Tab5: 短信验证码(模拟)# ════════════════════════════════════════    SMS_RESEND_SEC = 60# 重发冷却    SMS_VALID_SEC = 120# 有效期def_build_sms_tab(self, parent):        tk.Label(parent, text="短信验证码(模拟)", font=F_TITLE, bg=CARD, fg=TEXT).pack(            anchor="w", padx=16, pady=(142))        self._hint(parent, "原理:服务端生成 6 位随机数字下发到手机,附带有效期与重发""冷却限制,验证「持有手机」这一事实。此处用弹出面板模拟收到的短信。")        row = tk.Frame(parent, bg=CARD)        row.pack(fill="x", padx=16, pady=(124))        tk.Label(row, text="手机号:", font=F_BODY, bg=CARD, fg=TEXT).pack(side="left")        self.phone_input = tk.Entry(row, font=F_MONO, bg=INPUT_BG, fg=TEXT,                                    relief="flat", bd=1, width=14)        self.phone_input.pack(side="left", padx=6, ipady=3)        self.phone_input.insert(0"13800001234")        self.sms_send_btn = tk.Button(row, text="发送验证码", font=F_SMALL, bg=ACCENT,                                      fg="#ffffff", relief="flat", padx=12,                                      command=self._send_sms)        self.sms_send_btn.pack(side="left", padx=4)# 模拟手机短信面板        sms_box = tk.Frame(parent, bg="#f0fdf4", bd=1, relief="solid")        sms_box.pack(fill="x", padx=16, pady=8)        tk.Label(sms_box, text="📱 模拟手机短信收件箱", font=F_SMALL,                 bg="#f0fdf4", fg=TEXT_SEC).pack(anchor="w", padx=10, pady=(60))        self.sms_msg = tk.Label(sms_box, text="(暂无短信)", font=F_BODY,                                bg="#f0fdf4", fg=TEXT, wraplength=540, justify="left")        self.sms_msg.pack(anchor="w", padx=10, pady=(28))        row2 = tk.Frame(parent, bg=CARD)        row2.pack(fill="x", padx=16, pady=4)        tk.Label(row2, text="输入验证码:", font=F_BODY, bg=CARD, fg=TEXT).pack(side="left")        self.sms_input = tk.Entry(row2, font=F_BIG_MONO, bg=INPUT_BG, fg=TEXT,                                  relief="flat", bd=1, width=8, justify="center")        self.sms_input.pack(side="left", padx=6, ipady=2)        self.sms_input.bind("<Return>"lambda e: self._verify_sms())        tk.Button(row2, text="验证", font=F_SMALL, bg=GREEN, fg="#ffffff", relief="flat",                  padx=14, command=self._verify_sms).pack(side="left", padx=4)        self.sms_status = tk.Label(parent, text="", font=F_SMALL, bg=CARD, fg=TEXT_WEAK)        self.sms_status.pack(anchor="w", padx=16, pady=(40))        self.sms_result = self._result_label(parent)        self.sms_code = None        self.sms_sent_time = 0        self.sms_countdown_job = Nonedef_send_sms(self):        phone = self.phone_input.get().strip()ifnot (phone.isdigit() and len(phone) == 11and phone.startswith("1")):            self.sms_result.configure(text="❌ 请输入 11 位有效手机号", fg=RED)return        self.sms_code = "".join(random.choices(string.digits, k=6))        self.sms_sent_time = time.time()        masked = phone[:3] + "****" + phone[7:]        self.sms_msg.configure(            text=f"【验证码演示】您的验证码是 {self.sms_code},"f"{self.SMS_VALID_SEC // 60} 分钟内有效,请勿泄露。(发送至 {masked})")        self.sms_result.configure(text="")        self.sms_input.delete(0"end")        self._sms_tick()def_sms_tick(self):"""重发倒计时 + 有效期状态刷新"""if self.sms_countdown_job:            self.root.after_cancel(self.sms_countdown_job)            self.sms_countdown_job = Noneif self.sms_code isNone:return        elapsed = time.time() - self.sms_sent_time        resend_left = int(self.SMS_RESEND_SEC - elapsed)        valid_left = int(self.SMS_VALID_SEC - elapsed)if resend_left > 0:            self.sms_send_btn.configure(text=f"重发({resend_left}s)", state="disabled",                                        bg="#93c5fd")else:            self.sms_send_btn.configure(text="发送验证码", state="normal", bg=ACCENT)if valid_left > 0:            self.sms_status.configure(text=f"验证码剩余有效期: {valid_left}s")            self.sms_countdown_job = self.root.after(1000, self._sms_tick)else:            self.sms_status.configure(text="验证码已过期,请重新发送")            self.sms_code = None            self.sms_send_btn.configure(text="发送验证码", state="normal", bg=ACCENT)def_verify_sms(self):        user = self.sms_input.get().strip()if self.sms_code isNone:            self.sms_result.configure(text="❌ 请先发送验证码(或已过期)", fg=RED)returnif time.time() - self.sms_sent_time > self.SMS_VALID_SEC:            self.sms_result.configure(text="❌ 验证码已过期,请重新发送", fg=RED)            self.sms_code = Nonereturn        ok = user == self.sms_code        self._show_result(self.sms_result, ok,                          fail_text="❌ 验证码错误")if ok:            self.sms_code = Noneif self.sms_countdown_job:                self.root.after_cancel(self.sms_countdown_job)                self.sms_countdown_job = None            self.sms_status.configure(text="")            self.sms_send_btn.configure(text="发送验证码", state="normal", bg=ACCENT)defrun(self):        self.root.mainloop()if __name__ == "__main__":    app = CaptchaDemo()    app.run()

文档结束 · 本项目仅用于验证码原理演示与学习

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 15:45:53 HTTP/2.0 GET : https://f.mffb.com.cn/a/508020.html
  2. 运行时间 : 0.341383s [ 吞吐率:2.93req/s ] 内存消耗:4,544.30kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=8c9a719b486ce57faa42e6ce80c9af0d
  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.001215s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001807s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000779s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000705s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001371s ]
  6. SELECT * FROM `set` [ RunTime:0.000662s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001471s ]
  8. SELECT * FROM `article` WHERE `id` = 508020 LIMIT 1 [ RunTime:0.001471s ]
  9. UPDATE `article` SET `lasttime` = 1787298353 WHERE `id` = 508020 [ RunTime:0.061912s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000599s ]
  11. SELECT * FROM `article` WHERE `id` < 508020 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001180s ]
  12. SELECT * FROM `article` WHERE `id` > 508020 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.023384s ]
  13. SELECT * FROM `article` WHERE `id` < 508020 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.017668s ]
  14. SELECT * FROM `article` WHERE `id` < 508020 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.035594s ]
  15. SELECT * FROM `article` WHERE `id` < 508020 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.009590s ]
0.345658s