乐于分享
好东西不私藏

用AI写了个工具(忠之托证易印),可快速完成身份证等证件的裁剪排版并直接打印任务『附下载链接』

用AI写了个工具(忠之托证易印),可快速完成身份证等证件的裁剪排版并直接打印任务『附下载链接』

声明:

    一、这是一个非常小众的工具类程序,如果您没有频繁的裁剪打印证件需求,这个工具将对您没用。

    二、使用本工具将会自动向服务器发送IP地址、操作系统、内存大小等非敏感数据(不涉及个人隐私特征数据,不收集MAC地址),此举仅出于数据统计考虑,以方便后期的优化升级。

功能简介:

    本工具可以导入证件照片,进行简单的四点定位即可完成证件裁剪,裁剪后的证件照会自动放到A4尺寸白色背景上,最终可以导出图片、导出PDF格式或直接发送到打印机打印文件。

    问:为什么要做这个工具?

    经常看我文章的知道,我是做代办行业的。大部分办事场景都需要客户提供身份证复印件,为了方便客户,也不需要客户带身份证去打印店扫描或复印,只需要发身份证的照片即可。我们拿到照片后需要打开PS或Word的裁剪功能进行拖拽裁剪,最终把身份证裁剪调整到合适的尺寸才能进行打印,整个过程耗时往往需要几分钟。而且如果客户拍摄的照片倾斜不正的情况下,只能借助PS工具修正。为了提升工作效率,特意用AI制作了这个工具。

    问:为什么不设计成自动识别证件轮廓完成裁剪,那样不更省事?

    我最开始想的就是软件能自动识别轮廓并完成裁剪,我想的还是太理想化了,AI写的代码对于证件轮廓的识别准确率太低了,对证件背景的要求太高,必须是纯色背景,不能有一丁点杂色。网上搜罗也没有找到靠谱的解决方案。索性采用了性价比最高的“四点定位法”,即依次用鼠标选取证件轮廓的左上、右上、右下、左下四个点即可快速完成裁剪,操作难度低,效率比想象的要高。

使用步骤:

1、解压zip文件,再双击“忠之托证易印 Zhicert PrintGo V1.0.0.exe”直接运行。(绿色软件,无需安装。解压后_internal目录里面的文件不能动,更不能删除,否则会运行出错。

2、将身份证正反面照片同时拖进来。(也可以切换其他证件类型,不同的证件类型,预设了不同的尺寸。)

3、在图片预览区,点击右上角编辑裁切,进入编辑页面。

4、依次选取证件的左上、右上、右下、左下四个点。再点击完成并保存即可完成证件的裁剪。有多个图片的依次操作多个图片。

5、根据自己的需求可以选择直接打印、导出图片、导出PDF文件。

(效果如下图所示,黑色边框是为了方便在文章中展示后加的,实际输出的图片没有黑色边框。)

下载地址(复制到浏览器打开):

https://open.zztuo.cn/tool/ZhicertPrintGo/download.html

Python源码:

import osimport mathimport tempfileimport tkinter as tkfrom tkinter import filedialog, messagebox, scrolledtext, ttkimport cv2import numpy as npfrom PIL import Image, ImageTk, ImageWinfrom reportlab.lib.pagesizes import A4from reportlab.pdfgen import canvasfrom datetime import datetimeimport threadingimport webbrowserimport win32printimport win32uiimport win32conimport platformimport socketimport sysimport ctypesimport subprocess# ====================== 拖拽模块预导入(原版可用核心写法,修复识别异常) ======================DND_AVAILABLE = FalseTkinterDnD = NoneDND_FILES = Nonetry:    from tkinterdnd2 import TkinterDnD, DND_FILES    DND_AVAILABLE = Trueexcept ImportError:    DND_AVAILABLE = False# ====================== 全局配置常量 ======================APP_VERSION = "V1.0.0"APP_NAME = "忠之托证易印 Zhicert PrintGo(证件照片快速裁剪打印工具)"# 全局唯一互斥名,防止程序二次启动弹出黑框MUTEX_NAME = "ZhicertPrintGo_SingleInstance_Mutex_20260628"DPI = 300MM_TO_INCH = 1 / 25.4A4_WIDTH_PX = int(210 * MM_TO_INCH * DPI)A4_HEIGHT_PX = int(297 * MM_TO_INCH * DPI)try:    RESAMPLE_LANCZOS = Image.Resampling.LANCZOSexcept AttributeError:    RESAMPLE_LANCZOS = Image.ANTIALIASCARD_SPECS = {    "居民身份证": {        "width_mm"85.6,        "height_mm"54.0,        "two_per_page"True,        "desc""正反面上下排版,一页两张",        "filename""身份证_A4"    },    "银行卡": {        "width_mm"85.5,        "height_mm"54.0,        "two_per_page"False,        "desc""单张排版",        "filename""银行卡_A4"    },    "驾驶证": {        "width_mm"80.0,        "height_mm"54.0,        "two_per_page"False,        "desc""单张排版",        "filename""驾驶证_A4"    },    "行驶证": {        "width_mm"88.0,        "height_mm"60.0,        "two_per_page"False,        "desc""单张排版",        "filename""行驶证_A4"    },    "一寸证件照": {        "width_mm"25.0,        "height_mm"35.0,        "two_per_page"False,        "desc""单张排版",        "filename""一寸证件照_A4"    }}DEFAULT_CARD_TYPE = "居民身份证"MARGIN_PX = int(10 * MM_TO_INCH * DPI)ID_GAP_PX = int(10 * MM_TO_INCH * DPI)TEMP_DIR = tempfile.gettempdir()TEMP_PREFIX = "idcard_tmp_"DEFAULT_BASE_NAME = CARD_SPECS[DEFAULT_CARD_TYPE]["filename"]SAVE_FOLDER = os.path.expanduser("~/Desktop")PREVIEW_FIX_WIDTH = 440PREVIEW_FIX_HEIGHT = 200# ====================== 判断是否为PyInstaller打包后的exe ======================def is_frozen_exe():    return getattr(sys, "frozen"Falseand hasattr(sys, "_MEIPASS")# ====================== 硬件信息采集函数(纯ctypes,无psutil,不再弹出wmic.exe) ======================def get_cpu_model():    try:        # 隐藏窗口执行wmic,不弹出黑框        startupinfo = subprocess.STARTUPINFO()        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW        result = subprocess.check_output(            ["wmic""cpu""get""name""/value"],            startupinfo=startupinfo,            stderr=subprocess.STDOUT        ).decode("utf-8", errors="ignore")        for line in result.splitlines():            if "=" in line:                k, v = line.split("="1)                if k.strip() == "Name":                    return v.strip()        return "未知CPU"    except Exception:        return "未知CPU"def get_total_memory_gb():    try:        import ctypes        kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)        class MEMORYSTATUSEX(ctypes.Structure):            _fields_ = [                ("dwLength", ctypes.c_uint),                ("dwMemoryLoad", ctypes.c_uint),                ("ullTotalPhys", ctypes.c_ulonglong),                ("ullAvailPhys", ctypes.c_ulonglong),                ("ullTotalPageFile", ctypes.c_ulonglong),                ("ullAvailPageFile", ctypes.c_ulonglong),                ("ullTotalVirtual", ctypes.c_ulonglong),                ("ullAvailVirtual", ctypes.c_ulonglong),                ("ullAvailExtendedVirtual", ctypes.c_ulonglong),            ]        ms = MEMORYSTATUSEX()        ms.dwLength = ctypes.sizeof(MEMORYSTATUSEX)        kernel32.GlobalMemoryStatusEx(ctypes.byref(ms))        total_gb = round(ms.ullTotalPhys / (1024**3), 2)        return f"{total_gb} GB"    except Exception:        return "未知内存"# 改用Windows API获取分辨率,子线程安全,不再新建Tkdef get_screen_resolution():    try:        import ctypes        user32 = ctypes.windll.user32        screen_w = user32.GetSystemMetrics(0)        screen_h = user32.GetSystemMetrics(1)        return f"{screen_w}×{screen_h}"    except Exception:        return "未知分辨率"# ====================== 窗口工具函数 ======================def center_window(win, parent):    win.update_idletasks()    w = win.winfo_width()    h = win.winfo_height()    x = parent.winfo_x() + (parent.winfo_width() - w) // 2    y = parent.winfo_y() + (parent.winfo_height() - h) // 2    win.geometry(f"{w}x{h}+{x}+{y}")def center_root_window(root, win_width, win_height):    sw = root.winfo_screenwidth()    sh = root.winfo_screenheight()    x = (sw - win_width) // 2    y = (sh - win_height) // 2    root.geometry(f"{win_width}x{win_height}+{x}+{y}")def get_printer_list():    printers = []    try:        enum_printer = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL, None1)        for item in enum_printer:            printers.append(item[2])    except Exception:        pass    return printers# ====================== 四点裁切编辑器 ======================class FourPointEditor:    def __init__(self, parent, src_pil, finish_callback, card_w_mm, card_h_mm):        self.parent = parent        self.on_finish = finish_callback        self.origin_pil = src_pil.copy()        self.work_cv = cv2.cvtColor(np.array(self.origin_pil), cv2.COLOR_RGB2BGR)        self.card_w_px = int(card_w_mm * MM_TO_INCH * DPI * 1.1)        self.card_h_px = int(card_h_mm * MM_TO_INCH * DPI * 1.1)        self.four_points = []        self.dragging_point_idx = -1        self.scale = 1.0        self.offset_x = 0        self.offset_y = 0        self.tk_img_cache = None        self.img_item_id = None        self.annotation_tags = []        self.img_view_x = 0        self.img_view_y = 0        self.fit_scale = 1.0        self.win = tk.Toplevel(parent)        self.win.withdraw()        self.win.title("四点手动裁切编辑器(点四点→可拖拽微调,保存退出自动裁切)")        self.win.geometry("900x700")        self.win.transient(parent)        self.win.grab_set()        self.build_ui()        self.bind_mouse()        self.win.after(120self.refresh_canvas)        center_window(self.win, parent)        self.win.deiconify()    def build_ui(self):        top_frame = tk.Frame(self.win, padx=5, pady=5)        top_frame.pack(fill=tk.X)        tk.Button(top_frame, text="重置整张图", command=self.reset_all, bg="#ffdd99").pack(side=tk.LEFT, padx=3)        tk.Button(top_frame, text="清空四个角点", command=self.clear_points).pack(side=tk.LEFT, padx=3)        tk.Label(top_frame, text="操作模式:四点透视裁切(点完可拖拽微调点位)", fg="#2277cc").pack(side=tk.LEFT, padx=20)        tk.Button(top_frame, text="保存并退出", command=self.apply_close, bg="#b2e8b2").pack(side=tk.RIGHT, padx=3)        tk.Button(top_frame, text="取消", command=self.win.destroy).pack(side=tk.RIGHT, padx=3)        tip_frame = tk.Frame(self.win)        tip_frame.pack(fill=tk.X, padx=5)        tip_text = "操作:依次点击【左上→右上→右下→左下】四个角;按住蓝点可拖动微调对齐;对齐后点击【保存并退出】自动完成裁切"        tk.Label(tip_frame, text=tip_text, fg="#c0392b").pack()        canvas_container = tk.Frame(self.win)        canvas_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)        self.canvas = tk.Canvas(canvas_container, bg="#dddddd")        self.canvas.pack(fill=tk.BOTH, expand=True)    def bind_mouse(self):        self.canvas.bind("<Button-1>"self.mouse_down)        self.canvas.bind("<B1-Motion>"self.mouse_drag_move)        self.canvas.bind("<ButtonRelease-1>"self.mouse_up_release)        self.canvas.bind("<MouseWheel>"self.mouse_wheel)        self.canvas.bind("<ButtonPress-2>"self.pan_start)        self.canvas.bind("<B2-Motion>"self.pan_move)        self.pan_origin_x = 0        self.pan_origin_y = 0    def pan_start(self, e):        self.pan_origin_x = e.x        self.pan_origin_y = e.y    def pan_move(self, e):        dx = e.x - self.pan_origin_x        dy = e.y - self.pan_origin_y        self.offset_x += dx        self.offset_y += dy        self.pan_origin_x = e.x        self.pan_origin_y = e.y        self.refresh_canvas()    def pos_to_img(self, cx, cy):        ix = (cx - self.img_view_x) / self.fit_scale        iy = (cy - self.img_view_y) / self.fit_scale        return ix, iy    def img_to_pos(self, ix, iy):        cx = self.img_view_x + ix * self.fit_scale        cy = self.img_view_y + iy * self.fit_scale        return cx, cy    def get_point_under_mouse(self, mx, my):        hit_radius = 10        for idx, (x, y) in enumerate(self.four_points):            px, py = self.img_to_pos(x, y)            dist = math.hypot(mx - px, my - py)            if dist < hit_radius:                return idx        return -1    def refresh_canvas(self):        cw = self.canvas.winfo_width()        ch = self.canvas.winfo_height()        if cw <= 1 or ch <= 1:            return        h_img, w_img = self.work_cv.shape[:2]        disp_pil = Image.fromarray(cv2.cvtColor(self.work_cv, cv2.COLOR_BGR2RGB))        base_fit = min(cw / w_img, ch / h_img)        self.fit_scale = base_fit * self.scale        disp_w = int(w_img * self.fit_scale)        disp_h = int(h_img * self.fit_scale)        disp_resize = disp_pil.resize((disp_w, disp_h), RESAMPLE_LANCZOS)        self.tk_img_cache = ImageTk.PhotoImage(disp_resize)        self.img_view_x = (cw - disp_w) / 2 + self.offset_x        self.img_view_y = (ch - disp_h) / 2 + self.offset_y        for tag in self.annotation_tags:            self.canvas.delete(tag)        self.annotation_tags.clear()        if self.img_item_id is None:            self.img_item_id = self.canvas.create_image(self.img_view_x, self.img_view_y, anchor=tk.NW, image=self.tk_img_cache)        else:            self.canvas.coords(self.img_item_id, self.img_view_x, self.img_view_y)            self.canvas.itemconfig(self.img_item_id, image=self.tk_img_cache)        corner_names = ["左上""右上""右下""左下"]        for idx, (x, y) in enumerate(self.four_points):            px, py = self.img_to_pos(x, y)            oval_id = self.canvas.create_oval(px-7, py-7, px+7, py+7, fill="blue", outline="white", tags=("anno",))            text_id = self.canvas.create_text(px, py-12, text=corner_names[idx], fill="blue", font=("Arial"9), tags=("anno",))            self.annotation_tags.append(oval_id)            self.annotation_tags.append(text_id)        if len(self.four_points) >= 2:            for i in range(len(self.four_points)-1):                x1, y1 = self.img_to_pos(*self.four_points[i])                x2, y2 = self.img_to_pos(*self.four_points[i+1])                line_id = self.canvas.create_line(x1, y1, x2, y2, fill="blue", width=2, tags=("anno",))                self.annotation_tags.append(line_id)        if len(self.four_points) == 4:            x1, y1 = self.img_to_pos(*self.four_points[3])            x2, y2 = self.img_to_pos(*self.four_points[0])            line_id = self.canvas.create_line(x1, y1, x2, y2, fill="blue", width=2, tags=("anno",))            self.annotation_tags.append(line_id)    def clear_points(self):        self.four_points.clear()        self.dragging_point_idx = -1        self.refresh_canvas()    def reset_all(self):        self.work_cv = cv2.cvtColor(np.array(self.origin_pil), cv2.COLOR_RGB2BGR)        self.four_points.clear()        self.dragging_point_idx = -1        self.scale = 1.0        self.offset_x = 0        self.offset_y = 0        self.img_item_id = None        self.annotation_tags.clear()        self.refresh_canvas()    def do_perspective_cut(self):        if len(self.four_points) != 4:            return False        pts = np.array(self.four_points, dtype=np.float32)        dst = np.array([[0,0],[self.card_w_px-1,0],[self.card_w_px-1,self.card_h_px-1],[0,self.card_h_px-1]], dtype=np.float32)        M = cv2.getPerspectiveTransform(pts, dst)        self.work_cv = cv2.warpPerspective(self.work_cv, M, (self.card_w_px, self.card_h_px), borderMode=cv2.BORDER_REPLICATE)        self.four_points.clear()        self.dragging_point_idx = -1        self.img_item_id = None        self.annotation_tags.clear()        return True    def mouse_down(self, e):        hit_idx = self.get_point_under_mouse(e.x, e.y)        if hit_idx != -1:            self.dragging_point_idx = hit_idx            return        ix, iy = self.pos_to_img(e.x, e.y)        h, w = self.work_cv.shape[:2]        if ix < 0 or iy < 0 or ix > w or iy > h:            return        if len(self.four_points) < 4:            self.four_points.append((ix, iy))            self.refresh_canvas()    def mouse_drag_move(self, e):        if self.dragging_point_idx == -1:            return        ix, iy = self.pos_to_img(e.x, e.y)        h, w = self.work_cv.shape[:2]        ix = max(0min(w, ix))        iy = max(0min(h, iy))        self.four_points[self.dragging_point_idx] = (ix, iy)        self.refresh_canvas()    def mouse_up_release(self, e):        self.dragging_point_idx = -1    def mouse_wheel(self, e):        factor = 1.1 if e.delta > 0 else 0.9        self.scale = max(0.2min(3.5self.scale * factor))        self.refresh_canvas()    def apply_close(self):        if len(self.four_points) == 4:            self.do_perspective_cut()        final_pil = Image.fromarray(cv2.cvtColor(self.work_cv, cv2.COLOR_BGR2RGB))        self.on_finish(final_pil)        self.win.destroy()# ====================== 主程序类 ======================class IDCardProcessor:    def __init__(self, root):        self.root = root        self.root.title(f"{APP_NAME}{APP_VERSION}")        win_w = 1090        win_h = 800        center_root_window(self.root, win_w, win_h)        self.root.minsize(960780)        self.img_list = []        self.selected_origin_paths = []        self.output_pdf_path = ""        self.current_card_type = DEFAULT_CARD_TYPE        spec = CARD_SPECS[self.current_card_type]        self.card_w_mm = spec["width_mm"]        self.card_h_mm = spec["height_mm"]        self.card_w_px = int(self.card_w_mm * MM_TO_INCH * DPI)        self.card_h_px = int(self.card_h_mm * MM_TO_INCH * DPI)        self.tk_preview1 = None        self.tk_preview2 = None        self.frame_up = None        self.label_up = None        self.btn_edit1 = None        self.frame_down = None        self.label_down = None        self.btn_edit2 = None        self.swap_btn = None        self.save_path_var = tk.StringVar(value=SAVE_FOLDER)        self.save_name_var = tk.StringVar(value=DEFAULT_BASE_NAME)        self.printer_list = get_printer_list()        self.selected_printer = tk.StringVar()        self.build_ui()        self.root.bind("<Configure>"self.window_resize_event)        self.root.protocol("WM_DELETE_WINDOW"self.on_app_close)        self.init_printer_combobox()        self.update_two_side_visibility()        self.update_swap_btn_visible()        self.set_log_drag_tip()    def set_log_drag_tip(self):        self.text_log.tag_config("red_tip", foreground="#dd0000")        tip_text = "【提示:可直接将图片拖拽到此区域快速导入】\n【处理身份证照片时需同时选中身份证正反面。】\n"        self.text_log.insert(tk.END, tip_text, "red_tip")    def window_resize_event(self, event):        pass    def on_app_close(self):        self.clear_temp_files()        self.root.destroy()    def init_printer_combobox(self):        if self.printer_list:            try:                default_printer = win32print.GetDefaultPrinter()            except Exception:                default_printer = ""            self.printer_combo['values'] = self.printer_list            if default_printer in self.printer_list:                self.selected_printer.set(default_printer)            else:                self.selected_printer.set(self.printer_list[0])    def build_ui(self):        pad_x = 8        pad_y = 4        root_pane = ttk.PanedWindow(self.root, orient=tk.VERTICAL)        root_pane.pack(fill=tk.BOTH, expand=True)        top_container = ttk.Frame(root_pane)        root_pane.add(top_container, weight=3)        bottom_all = ttk.Frame(root_pane)        root_pane.add(bottom_all, weight=1)        top_frame = ttk.Frame(top_container)        top_frame.pack(fill=tk.X)        type_frame = ttk.Frame(top_frame)        type_frame.pack(fill=tk.X, padx=6, pady=pad_y)        ttk.Label(type_frame, text="证件类型:").pack(side=tk.LEFT)        self.card_type_var = tk.StringVar(value=self.current_card_type)        card_cbx = ttk.Combobox(type_frame, textvariable=self.card_type_var, state="readonly", width=18)        card_cbx["values"] = list(CARD_SPECS.keys())        card_cbx.pack(side=tk.LEFT, padx=5)        card_cbx.bind("<<ComboboxSelected>>"self.on_card_type_change)        self.card_tip_label = ttk.Label(type_frame, text=CARD_SPECS[self.current_card_type]["desc"], foreground="#226622")        self.card_tip_label.pack(side=tk.LEFT, padx=15)        ttk.Separator(top_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=6, pady=3)        toolbar_frame = ttk.Frame(top_frame)        toolbar_frame.pack(fill=tk.X, padx=6, pady=pad_y)        ttk.Button(toolbar_frame, text="选择图片", command=self.select_files, width=18).pack(side=tk.LEFT, padx=4)        ttk.Label(toolbar_frame, text="说明:快速裁切证件并生成A4打印排版", foreground="#c0392b").pack(side=tk.LEFT, padx=12)        ttk.Button(toolbar_frame, text="关于软件", command=self.show_about_window).pack(side=tk.RIGHT, padx=4)        ttk.Separator(top_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=6, pady=3)        middle_container = ttk.Frame(top_container)        middle_container.pack(fill=tk.BOTH, expand=True, padx=4, pady=4)        middle_container.columnconfigure(0, weight=1)        middle_container.columnconfigure(1, weight=1)        left_frame = ttk.LabelFrame(middle_container, text="处理日志")        left_frame.grid(row=0, column=0, sticky="nsew", padx=4)        left_inner = ttk.Frame(left_frame)        left_inner.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)        self.text_log = scrolledtext.ScrolledText(left_inner)        self.text_log.pack(fill=tk.BOTH, expand=True)        # 绑定日志框拖拽        if DND_AVAILABLE:            self.text_log.drop_target_register(DND_FILES)            self.text_log.dnd_bind('<<Drop>>'self.drop_files)        right_frame = ttk.LabelFrame(middle_container, text="预览")        right_frame.grid(row=0, column=1, sticky="nsew", padx=4)        right_inner = ttk.Frame(right_frame)        right_inner.pack(fill=tk.BOTH, expand=True, padx=6, pady=6)        top_bar = ttk.Frame(right_inner)        top_bar.pack(fill=tk.X, pady=(08))        ttk.Label(top_bar, text="", width=1).pack(side=tk.LEFT)        self.swap_btn = ttk.Button(top_bar, text="互换正反面", command=self.swap_two_face, width=12)        self.swap_btn.pack(side=tk.RIGHT)        card1 = ttk.LabelFrame(right_inner, text="图片一")        card1.pack(fill=tk.X, pady=(010))        self.frame_up = card1        card1_inner = ttk.Frame(card1)        card1_inner.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)        bar1 = ttk.Frame(card1_inner)        bar1.pack(fill=tk.X, pady=(0,5))        ttk.Label(bar1, text="").pack(side=tk.LEFT)        self.btn_edit1 = ttk.Button(bar1, text="编辑裁切", command=lambda:self.open_editor(0), width=10)        self.btn_edit1.pack(side=tk.RIGHT)        canvas1 = tk.Canvas(card1_inner, bg="#f7f7f7", relief=tk.SUNKEN, width=PREVIEW_FIX_WIDTH, height=PREVIEW_FIX_HEIGHT)        canvas1.pack(pady=2)        self.label_up = canvas1        card2 = ttk.LabelFrame(right_inner, text="图片二")        card2.pack(fill=tk.X)        self.frame_down = card2        card2_inner = ttk.Frame(card2)        card2_inner.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)        bar2 = ttk.Frame(card2_inner)        bar2.pack(fill=tk.X, pady=(0,5))        ttk.Label(bar2, text="").pack(side=tk.LEFT)        self.btn_edit2 = ttk.Button(bar2, text="编辑裁切", command=lambda:self.open_editor(1), width=10)        self.btn_edit2.pack(side=tk.RIGHT)        canvas2 = tk.Canvas(card2_inner, bg="#f7f7f7", relief=tk.SUNKEN, width=PREVIEW_FIX_WIDTH, height=PREVIEW_FIX_HEIGHT)        canvas2.pack(pady=2)        self.label_down = canvas2        save_cfg_frame = ttk.Frame(bottom_all)        save_cfg_frame.pack(fill=tk.X, padx=6, pady=pad_y)        ttk.Label(save_cfg_frame, text="保存目录:").pack(side=tk.LEFT)        ttk.Entry(save_cfg_frame, textvariable=self.save_path_var, width=32).pack(side=tk.LEFT, padx=4)        ttk.Button(save_cfg_frame, text="选择目录", command=self.select_save_dir).pack(side=tk.LEFT, padx=3)        ttk.Label(save_cfg_frame, text="文件名:").pack(side=tk.LEFT, padx=10)        ttk.Entry(save_cfg_frame, textvariable=self.save_name_var, width=18).pack(side=tk.LEFT, padx=4)        ttk.Label(save_cfg_frame, text="打印机:").pack(side=tk.LEFT, padx=10)        self.printer_combo = ttk.Combobox(save_cfg_frame, textvariable=self.selected_printer, state="readonly", width=26)        self.printer_combo.pack(side=tk.LEFT, padx=4)        ttk.Separator(bottom_all, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=6, pady=3)        bottom_btn_frame = ttk.Frame(bottom_all)        bottom_btn_frame.pack(pady=8)        btn_reload = ttk.Button(bottom_btn_frame, text="重新载入原图", command=self.reimport_clear, width=15)        btn_reload.grid(row=0, column=0, padx=6)        btn_print = ttk.Button(bottom_btn_frame, text="直接打印", command=self.print_file, width=15)        btn_print.grid(row=0, column=1, padx=6)        btn_clear = ttk.Button(bottom_btn_frame, text="清空全部", command=self.clear_all, width=15)        btn_clear.grid(row=0, column=2, padx=6)        btn_export_img = ttk.Button(bottom_btn_frame, text="导出图片", command=self.save_combine_img, width=15)        btn_export_img.grid(row=0, column=3, padx=6)        btn_export_pdf = ttk.Button(bottom_btn_frame, text="导出PDF", command=self.save_pdf, width=15)        btn_export_pdf.grid(row=0, column=4, padx=6)    def update_swap_btn_visible(self):        if len(self.img_list) >= 2:            self.swap_btn.pack(side=tk.RIGHT)        else:            self.swap_btn.pack_forget()    def update_two_side_visibility(self):        spec = CARD_SPECS[self.current_card_type]        if spec["two_per_page"]:            self.frame_down.pack(fill=tk.X, pady=(2,6))            self.btn_edit2.config(state=tk.NORMAL)        else:            self.frame_down.pack_forget()            self.btn_edit2.config(state=tk.DISABLED)    def show_about_window(self):        about_win = tk.Toplevel(self.root)        about_win.withdraw()        about_win.title("关于软件")        about_win.geometry("420x260")        about_win.resizable(FalseFalse)        about_win.transient(self.root)        about_win.grab_set()        main_frame = ttk.Frame(about_win, padding=15)        main_frame.pack(fill=tk.BOTH, expand=True)        ttk.Label(main_frame, text="忠之托·证易印  Zhicert PrintGo", font=("微软雅黑",12,"bold")).pack(anchor="w")        ttk.Label(main_frame, text=f"软件版本:{APP_VERSION}", font=("微软雅黑",10)).pack(anchor="w", pady=(6,0))        desc_text = """功能简介:支持身份证、银行卡、驾驶证、行驶证、一寸照四点透视矫正自动A4排版、一键导出图片/PDF、直连打印机打印        """        ttk.Label(main_frame, text=desc_text, justify="left").pack(anchor="w", pady=(8,12))        btn_frame = ttk.Frame(main_frame)        btn_frame.pack(fill=tk.X)        def check_update():            messagebox.showinfo("版本检测"f"当前已是最新版本 {APP_VERSION}")        def open_more():            webbrowser.open("https://open.zztuo.cn/tool/ZhicertPrintGo/")        ttk.Button(btn_frame, text="检查更新", command=check_update).pack(side=tk.LEFT, expand=True, fill=tk.X, padx=2)        ttk.Button(btn_frame, text="了解更多", command=open_more).pack(side=tk.LEFT, expand=True, fill=tk.X, padx=2)        ttk.Button(btn_frame, text="关闭", command=about_win.destroy).pack(side=tk.LEFT, expand=True, fill=tk.X, padx=2)        center_window(about_win, self.root)        about_win.deiconify()    def select_save_dir(self):        path = filedialog.askdirectory(title="选择文件保存目录")        if path:            self.save_path_var.set(path)            global SAVE_FOLDER            SAVE_FOLDER = path            self.log(f"已设置保存目录:{path}")    def on_card_type_change(self, event):        new_type = self.card_type_var.get()        if new_type == self.current_card_type:            return        res = messagebox.askyesno("确认切换"f"切换证件类型为【{new_type}】将清空当前所有图片数据,是否继续?")        if not res:            self.card_type_var.set(self.current_card_type)            return        self.current_card_type = new_type        spec = CARD_SPECS[self.current_card_type]        self.card_tip_label.config(text=spec["desc"])        self.card_w_mm = spec["width_mm"]        self.card_h_mm = spec["height_mm"]        self.card_w_px = int(self.card_w_mm * MM_TO_INCH * DPI)        self.card_h_px = int(self.card_h_mm * MM_TO_INCH * DPI)        self.save_name_var.set(spec["filename"])        self.log(f"切换证件类型:{self.current_card_type},尺寸 {self.card_w_mm}×{self.card_h_mm} mm")        self.update_two_side_visibility()        self.clear_all()    def swap_two_face(self):        spec = CARD_SPECS[self.current_card_type]        if spec["two_per_page"is False:            messagebox.showinfo("提示""当前证件设置为单张模式,无需正反面互换")            return        if len(self.img_list) < 2:            messagebox.showinfo("提示""需要同时两张图片才可互换")            return        self.img_list[0], self.img_list[1] = self.img_list[1], self.img_list[0]        self.log("已互换【图片一 ↔ 图片二】位置")        self.refresh_two_preview()    def reimport_clear(self):        if not self.selected_origin_paths:            messagebox.showinfo("提示""没有已选择的原图")            return        self.img_list.clear()        self.process_manual_raw()    def open_editor(self, idx):        spec = CARD_SPECS[self.current_card_type]        if idx == 1 and not spec["two_per_page"]:            messagebox.showinfo("提示""当前证件仅需单张,无需编辑第二张")            return        if idx >= len(self.img_list):            messagebox.showinfo("提示""该位置暂无图片")            return        src_img = self.img_list[idx].copy()        pic_name = "图片一" if idx == 0 else "图片二"        def done(new_img):            self.img_list[idx] = new_img            self.log(f"{pic_name}四点裁切调整完成")            self.refresh_two_preview()            self.update_swap_btn_visible()        FourPointEditor(self.root, src_img, done, self.card_w_mm, self.card_h_mm)    def log(self, msg):        now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")        line = f"[{now_str}{msg}"        self.text_log.insert(tk.END, line + "\n")        self.text_log.see(tk.END)        self.root.update_idletasks()    def clear_temp_files(self):        for fname in os.listdir(TEMP_DIR):            if fname.startswith(TEMP_PREFIX):                try:                    os.remove(os.path.join(TEMP_DIR, fname))                except Exception:                    pass    def process_manual_raw(self):        self.img_list.clear()        spec = CARD_SPECS[self.current_card_type]        self.log(f"===== 当前证件:{self.current_card_type} =====")        max_img = 2 if spec["two_per_page"else 1        for path in self.selected_origin_paths[:max_img]:            try:                pil = Image.open(path).convert("RGB")                self.img_list.append(pil)                self.log(f"载入原图:{os.path.basename(path)},可点击对应编辑按钮裁切")            except Exception as e:                self.log(f"读取失败:{path}{str(e)}")        self.refresh_two_preview()        self.update_swap_btn_visible()    def select_files(self):        spec = CARD_SPECS[self.current_card_type]        max_select = 2 if spec["two_per_page"else 1        tips = f"选择图片(最多{max_select}张)"        paths = filedialog.askopenfilenames(            title=tips,            filetypes=[("图片""*.jpg *.jpeg *.png *.bmp"), ("所有文件""*.*")]        )        if not paths:            return        self.selected_origin_paths = list(paths[:max_select])        self.img_list.clear()        self.process_manual_raw()    def drop_files(self, event):        # 使用tkinterdnd2原生splitlist解析,官方适配{}包裹多路径,杜绝手动分割bug        paths = self.root.splitlist(event.data)        spec = CARD_SPECS[self.current_card_type]        max_select = 2 if spec["two_per_page"else 1        valid = []        for p in paths:            p = p.strip()            if not os.path.isfile(p):                continue            ext = os.path.splitext(p)[1].lower()            if ext in (".jpg"".jpeg"".png"".bmp"):                valid.append(p)        if not valid:            self.log("拖拽内容不含有效图片文件,已忽略")            return        content = self.text_log.get("1.0", tk.END).strip()        tip_keyword = "【提示:可直接将图片拖拽到此区域快速导入】\n【处理身份证照片时需同时选中身份证正反面。】\n"        if tip_keyword in content:            self.text_log.delete("1.0", tk.END)        self.selected_origin_paths = valid[:max_select]        self.img_list.clear()        self.process_manual_raw()    def fit_preview_fixed(self, pil_img):        if pil_img is None:            return None        w, h = pil_img.size        max_w = PREVIEW_FIX_WIDTH - 20        max_h = PREVIEW_FIX_HEIGHT - 20        scale = min(max_w / w, max_h / h)        new_w = int(w * scale)        new_h = int(h * scale)        return pil_img.resize((new_w, new_h), RESAMPLE_LANCZOS)    def refresh_two_preview(self):        self.label_up.delete("preview_img")        self.label_down.delete("preview_img")        self.tk_preview1 = None        self.tk_preview2 = None        if len(self.img_list) >= 1:            fit1 = self.fit_preview_fixed(self.img_list[0])            self.tk_preview1 = ImageTk.PhotoImage(fit1)            x_off = (PREVIEW_FIX_WIDTH - fit1.width) // 2            y_off = (PREVIEW_FIX_HEIGHT - fit1.height) // 2            self.label_up.create_image(x_off, y_off, anchor="nw", image=self.tk_preview1, tags="preview_img")        if len(self.img_list) >= 2:            fit2 = self.fit_preview_fixed(self.img_list[1])            self.tk_preview2 = ImageTk.PhotoImage(fit2)            x_off = (PREVIEW_FIX_WIDTH - fit2.width) // 2            y_off = (PREVIEW_FIX_HEIGHT - fit2.height) // 2            self.label_down.create_image(x_off, y_off, anchor="nw", image=self.tk_preview2, tags="preview_img")    def layout_a4_canvas(self):        a4_canvas = Image.new("RGB", (A4_WIDTH_PX, A4_HEIGHT_PX), "white")        if not self.img_list:            return a4_canvas        spec = CARD_SPECS[self.current_card_type]        two_page = spec["two_per_page"]        if two_page and len(self.img_list)>=2:            total_h = self.card_h_px * 2 + ID_GAP_PX        else:            total_h = self.card_h_px        area_w = A4_WIDTH_PX - MARGIN_PX * 2        area_h = A4_HEIGHT_PX - MARGIN_PX * 2        base_x = MARGIN_PX + (area_w - self.card_w_px) // 2        base_y = MARGIN_PX + (area_h - total_h) // 2        img1 = self.img_list[0].resize((self.card_w_px, self.card_h_px), RESAMPLE_LANCZOS)        a4_canvas.paste(img1, (base_x, base_y))        if two_page and len(self.img_list) == 2:            img2 = self.img_list[1].resize((self.card_w_px, self.card_h_px), RESAMPLE_LANCZOS)            a4_canvas.paste(img2, (base_x, base_y + self.card_h_px + ID_GAP_PX))        return a4_canvas    def save_combine_img(self):        if not self.img_list:            messagebox.showwarning("提示""暂无处理完成图片")            return        folder = self.save_path_var.get().strip()        filename = self.save_name_var.get().strip()        if not folder:            messagebox.showwarning("提示""请先设置保存目录")            return        full_path = os.path.join(folder, f"{filename}.jpg")        canvas = self.layout_a4_canvas()        canvas.save(full_path, quality=99, subsampling=0)        self.log(f"图片已保存:{full_path}")        messagebox.showinfo("保存成功", full_path)    def save_pdf(self):        if not self.img_list:            messagebox.showwarning("提示""暂无处理完成图片")            return        folder = self.save_path_var.get().strip()        filename = self.save_name_var.get().strip()        if not folder:            messagebox.showwarning("提示""请先设置保存目录")            return        full_path = os.path.join(folder, f"{filename}.pdf")        canvas_img = self.layout_a4_canvas()        tmp_png = os.path.join(TEMP_DIR, TEMP_PREFIX + "pdf_temp.png")        canvas_img.save(tmp_png, quality=99)        c = canvas.Canvas(full_path, pagesize=A4)        w, h = A4        c.drawImage(tmp_png, 00, width=w, height=h)        c.save()        self.output_pdf_path = full_path        self.log(f"PDF已保存:{full_path}")        messagebox.showinfo("保存成功", full_path)    def print_file(self):        if not self.img_list:            messagebox.showwarning("提示""暂无排版图片,无法打印")            return        printer_name = self.selected_printer.get().strip()        if not printer_name:            messagebox.showwarning("提示""请先选择一台打印机")            return        try:            img = self.layout_a4_canvas()            hprinter = win32print.OpenPrinter(printer_name)            hdc = win32ui.CreateDC()            hdc.CreatePrinterDC(printer_name)            hdc.StartDoc("证件排版打印任务")            hdc.StartPage()            print_dpi_x = hdc.GetDeviceCaps(win32con.LOGPIXELSX)            print_dpi_y = hdc.GetDeviceCaps(win32con.LOGPIXELSY)            inch_w = 210 / 25.4            inch_h = 297 / 25.4            dst_w = int(inch_w * print_dpi_x)            dst_h = int(inch_h * print_dpi_y)            img_print = img.resize((dst_w, dst_h), RESAMPLE_LANCZOS)            dib = ImageWin.Dib(img_print)            dib.draw(hdc.GetHandleOutput(), (00, dst_w, dst_h))            hdc.EndPage()            hdc.EndDoc()            hdc.DeleteDC()            win32print.ClosePrinter(hprinter)            self.log(f"打印任务已提交至打印机:{printer_name}")            messagebox.showinfo("提交成功"f"图像已发送至【{printer_name}】!")        except Exception as e:            err_msg = str(e)            self.log(f"打印失败:{err_msg}")            messagebox.showerror("打印异常"f"失败原因:{err_msg}\n请检查打印机是否在线、驱动是否正常")    def clear_all(self):        self.selected_origin_paths.clear()        self.img_list.clear()        self.refresh_two_preview()        self.text_log.delete(1.0, tk.END)        self.set_log_drag_tip()        self.clear_temp_files()        self.log("已清空全部图片与日志数据")        self.update_swap_btn_visible()# ====================== 程序入口(保留单例互斥锁 + 拖拽窗口创建逻辑) ======================if __name__ == "__main__":    mutex = None    if is_frozen_exe():        mutex = ctypes.windll.kernel32.CreateMutexW(NoneFalse, MUTEX_NAME)        last_err = ctypes.windll.kernel32.GetLastError()        if last_err == 183:            ctypes.windll.kernel32.CloseHandle(mutex)            ctypes.windll.user32.MessageBoxW(0"程序已经正在运行,请勿重复打开""重复启动提示"0x10)            sys.exit(0)    # 拖拽库存在则创建TkinterDnD根窗口,否则普通Tk    if DND_AVAILABLE:        root = TkinterDnD.Tk()    else:        root = tk.Tk()    app = IDCardProcessor(root)    root.mainloop()    app.clear_temp_files()    # 释放互斥内核对象    if is_frozen_exe() and mutex is not None:        ctypes.windll.kernel32.ReleaseMutex(mutex)        ctypes.windll.kernel32.CloseHandle(mutex)
基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-06-29 15:35:36 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/816168.html
  2. 运行时间 : 0.129386s [ 吞吐率:7.73req/s ] 内存消耗:5,054.78kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=125df891afff9062ae851dc8eb798039
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000462s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000775s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000347s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000243s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000495s ]
  6. SELECT * FROM `set` [ RunTime:0.000199s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000585s ]
  8. SELECT * FROM `article` WHERE `id` = 816168 LIMIT 1 [ RunTime:0.000598s ]
  9. UPDATE `article` SET `lasttime` = 1782718536 WHERE `id` = 816168 [ RunTime:0.011938s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000389s ]
  11. SELECT * FROM `article` WHERE `id` < 816168 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000501s ]
  12. SELECT * FROM `article` WHERE `id` > 816168 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000395s ]
  13. SELECT * FROM `article` WHERE `id` < 816168 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000687s ]
  14. SELECT * FROM `article` WHERE `id` < 816168 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000701s ]
  15. SELECT * FROM `article` WHERE `id` < 816168 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000904s ]
0.131200s