当前位置:首页>python>Python源码 | 用Python写出自己的python代码打包成exe工具!

Python源码 | 用Python写出自己的python代码打包成exe工具!

  • 2026-03-12 10:21:10
Python源码 | 用Python写出自己的python代码打包成exe工具!

Python代码打包成exe工具

大家好,我是灵感!

好长时间没更新了,我相信大部分人都需要写完python代码把它分享给自己的亲朋好友,但是你单独发代码他们可能运行不了,所以你需要把代码打包成exe文件,他们双击就能用你做的程序了,所以做了个可视化打包工具,以后也有可能继续更新版本,我把代码给你们自己改一下用也行。

今天就用代码实现一下,如果你们有能力,自己优化一下,也可以按自己的要求进行修改或者美化啥的!

今天我把程序的代码分享给大家,有兴趣的可以自己试试,能力有限可能不太美观,或者有一些小问题,也有可能对你们来说一点都看不上,如果真要是这样,那我希望你们自己搞一个,或者代码给你们了自己修改成自己喜欢的样子,然后再使用!

内容完全原创,所以希望你们用或者修改的时候给我留点关于我公众号的一些信息,谢谢大家!代码页面是这样的:

下面我把程序给你们简单的介绍一下:

也没什么好介绍的,就是个用Python写的打包工具,而且支持两种打包方式,最重要的是免费,下面看图片就知道了

其它地方你们自己测试,反正源码已经给你们了,按装号需要的Python和它的库就可以正常运行,你们只需要具备环境,然后就是复制,粘贴后运行代码就能看到上面介绍图片里的那个程序和页面!

完整版代码:

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""灵感江Python打包exe工具 v1.0 - 精简美化版支持:PyInstaller 和 Nuitka 双引擎打包功能:UPX压缩、文件夹打包、多格式图标支持、居中进度弹窗、自动依赖扫描"""import osimport sysimport subprocessimport shutilimport threadingimport queueimport reimport tempfilefrom pathlib import Pathfrom typing import Listfrom dataclasses import dataclass, field, asdict# 设置控制台编码if sys.platform == 'win32':    os.environ['PYTHONIOENCODING'] = 'utf-8'try:    import tkinter as tk    from tkinter import ttk, filedialog, messagebox    GUI_AVAILABLE = Trueexcept ImportError:    GUI_AVAILABLE = False    print("错误: tkinter未安装")    sys.exit(1)# 尝试导入PIL用于图标转换try:    from PIL import Image    PIL_AVAILABLE = Trueexcept ImportError:    PIL_AVAILABLE = False# ============== 主题配置 ==============class Theme:    LIGHT = {        'name''light''bg_primary''#f8fafc''bg_secondary''#ffffff',        'bg_tertiary''#f1f5f9''bg_card''#ffffff''bg_input''#ffffff',        'accent''#6366f1''accent_hover''#818cf8''accent_light''#eef2ff',        'text_primary''#1e293b''text_secondary''#64748b''text_muted''#94a3b8',        'text_success''#22c55e''text_error''#ef4444''text_warning''#f59e0b',        'border''#e2e8f0''btn_secondary''#f1f5f9''btn_danger''#ef4444',        'progress_bg''#e2e8f0''progress_fg''#6366f1',        'log_bg''#1e293b''log_fg''#94a3b8''log_success''#4ade80',        'log_error''#f87171''log_warning''#fbbf24''log_info''#60a5fa',        'overlay_bg''#4a5568',    }    DARK = {        'name''dark''bg_primary''#0f172a''bg_secondary''#1e293b',        'bg_tertiary''#334155''bg_card''#1e293b''bg_input''#0f172a',        'accent''#818cf8''accent_hover''#a5b4fc''accent_light''#312e81',        'text_primary''#f1f5f9''text_secondary''#cbd5e1''text_muted''#64748b',        'text_success''#4ade80''text_error''#f87171''text_warning''#fbbf24',        'border''#334155''btn_secondary''#334155''btn_danger''#ef4444',        'progress_bg''#334155''progress_fg''#818cf8',        'log_bg''#0f172a''log_fg''#94a3b8''log_success''#4ade80',        'log_error''#f87171''log_warning''#fbbf24''log_info''#60a5fa',        'overlay_bg''#1a202c',    }# ============== 配置数据类 ==============@dataclassclass PackConfig:    python_path: str = ""    main_file: str = ""    output_name: str = ""    icon_path: str = ""    output_path: str = ""    packer: str = "pyinstaller"    one_file: bool = False    no_console: bool = True    speed_mode: bool = True    enable_upx: bool = False    upx_path: str = ""    add_folders: List[str] = field(default_factory=list)# ============== 依赖扫描器 ==============class DependencyScanner:    """自动扫描Python文件中的依赖库"""    # Python标准库模块列表(常见的一部分)    STDLIB_MODULES = {        # 内置模块        'abc''aifc''argparse''array''ast''asynchat''asyncio''asyncore',        'atexit''audioop''base64''bdb''binascii''binhex''bisect',        'builtins''bz2''calendar''cgi''cgitb''chunk''cmath''cmd',        'code''codecs''codeop''collections''colorsys''compileall',        'concurrent''configparser''contextlib''contextvars''copy''copyreg',        'cProfile''crypt''csv''ctypes''curses''dataclasses''datetime',        'dbm''decimal''difflib''dis''distutils''doctest''email',        'encodings''enum''errno''faulthandler''fcntl''filecmp''fileinput',        'fnmatch''fractions''ftplib''functools''gc''getopt''getpass',        'gettext''glob''graphlib''grp''gzip''hashlib''heapq''hmac',        'html''http''imaplib''imghdr''imp''importlib''inspect''io',        'ipaddress''itertools''json''keyword''lib2to3''linecache''locale',        'logging''lzma''mailbox''mailcap''marshal''math''mimetypes',        'mmap''modulefinder''multiprocessing''netrc''nis''nntplib',        'numbers''operator''optparse''os''ossaudiodev''pathlib''pdb',        'pickle''pickletools''pipes''pkgutil''platform''plistlib''poplib',        'posix''posixpath''pprint''profile''pstats''pty''pwd''py_compile',        'pyclbr''pydoc''queue''quopri''random''re''readline''reprlib',        'resource''rlcompleter''runpy''sched''secrets''select''selectors',        'shelve''shlex''shutil''signal''site''smtpd''smtplib''sndhdr',        'socket''socketserver''spwd''sqlite3''ssl''stat''statistics',        'string''stringprep''struct''subprocess''sunau''symtable''sys',        'sysconfig''syslog''tabnanny''tarfile''telnetlib''tempfile''termios',        'test''textwrap''threading''time''timeit''tkinter''token',        'tokenize''trace''traceback''tracemalloc''tty''turtle''turtledemo',        'types''typing''unicodedata''unittest''urllib''uu''uuid''venv',        'warnings''wave''weakref''webbrowser''winreg''winsound''wsgiref',        'xdrlib''xml''xmlrpc''zipapp''zipfile''zipimport''zlib',        # 常见的标准库子模块        'tkinter.ttk''tkinter.filedialog''tkinter.messagebox''tkinter.colorchooser',        'tkinter.font''tkinter.scrolledtext''tkinter.simpledialog',        'http.client''http.server''http.cookies''http.cookiejar',        'urllib.request''urllib.parse''urllib.error''urllib.robotparser',        'email.mime''email.mime.text''email.mime.multipart''email.mime.base',        'xml.etree''xml.etree.ElementTree''xml.dom''xml.sax',        'json.decoder''json.encoder',        'collections.abc''collections.abc',        'concurrent.futures''concurrent.futures.thread''concurrent.futures.process',        'multiprocessing.pool''multiprocessing.process''multiprocessing.Queue',        'asyncio.coroutines''asyncio.events''asyncio.tasks''asyncio.queues',        'logging.handlers''logging.config',    }    # 需要特殊处理的第三方库映射(包名 -> 导入名)    PACKAGE_IMPORT_MAP = {        'cv2''cv2',        'opencv-python''cv2',        'opencv-contrib-python''cv2',        'PIL''PIL',        'pillow''PIL',        'sklearn''sklearn',        'scikit-learn''sklearn',        'skimage''skimage',        'scikit-image''skimage',        'bs4''bs4',        'beautifulsoup4''bs4',        'dateutil''dateutil',        'python-dateutil''dateutil',        'yaml''yaml',        'pyyaml''yaml',        'Crypto''Crypto',        'pycryptodome''Crypto',        'usb''usb',        'pyusb''usb',        'serial''serial',        'pyserial''serial',        'Image''PIL',        'sqlite3'None,  # 标准库    }    # 常见需要隐藏导入的库(自动添加)    COMMON_HIDDEN_IMPORTS = {        'cv2': ['cv2'],        'numpy': ['numpy''numpy.core''numpy.core._methods''numpy.lib''numpy.lib.format'],        'pandas': ['pandas''pandas._libs''pandas._libs.tslibs'],        'PIL': ['PIL''PIL._imagingtk''PIL._imaging'],        'sklearn': ['sklearn''sklearn.utils''sklearn.utils._cython_blas''sklearn.neighbors.typedefs'],        'torch': ['torch''torch._C'],        'tensorflow': ['tensorflow''tensorflow.python'],        'matplotlib': ['matplotlib''matplotlib.backends''matplotlib.backends.backend_tkagg'],        'requests': ['requests''urllib3'],        'flask': ['flask''jinja2''werkzeug''click'],        'django': ['django''django.core'],        'PyQt5': ['PyQt5''PyQt5.QtCore''PyQt5.QtGui''PyQt5.QtWidgets'],        'PyQt6': ['PyQt6''PyQt6.QtCore''PyQt6.QtGui''PyQt6.QtWidgets'],        'PySide2': ['PySide2''PySide2.QtCore''PySide2.QtGui''PySide2.QtWidgets'],        'PySide6': ['PySide6''PySide6.QtCore''PySide6.QtGui''PySide6.QtWidgets'],        'pytz': ['pytz'],        'dateutil': ['dateutil''dateutil.relativedelta'],        'lxml': ['lxml''lxml.etree''lxml._elementpath'],        'bs4': ['bs4''bs4.builder''bs4.builder.html_parser'],        'selenium': ['selenium''selenium.webdriver'],        'serial': ['serial''serial.tools''serial.tools.list_ports'],        'usb': ['usb''usb.core''usb.util'],        'pynput': ['pynput''pynput.keyboard''pynput.mouse'],        'pygame': ['pygame''pygame.base''pygame.locals'],        'sounddevice': ['sounddevice'],        'soundfile': ['soundfile'],        'pyaudio': ['pyaudio''_portaudio'],        'librosa': ['librosa''librosa.core''librosa.util'],        'scipy': ['scipy''scipy.sparse''scipy.sparse.csgraph''scipy.special''scipy.spatial'],    }    @classmethod    def scan_file(cls, file_path: str) -> set:        """扫描单个Python文件,提取所有导入的模块"""        imports = set()        try:            with open(file_path, 'r', encoding='utf-8', errors='ignore'as f:                content = f.read()            # 使用正则表达式匹配 import 语句            # 匹配: import xxx, import xxx as yyy, import xxx, yyy, zzz            import_pattern = r'^\s*import\s+([\w\s,\.]+)(?:\s+as\s+\w+)?\s*$'            # 匹配: from xxx import yyy            from_pattern = r'^\s*from\s+([\w\.]+)\s+import'            for line in content.split('\n'):                line = line.strip()                # 跳过注释                if line.startswith('#'):                    continue                # 匹配 from xxx import yyy                match = re.match(from_pattern, line)                if match:                    module = match.group(1).split('.')[0]  # 取顶层模块                    imports.add(module)                    continue                # 匹配 import xxx                match = re.match(import_pattern, line)                if match:                    modules = match.group(1)                    for mod in modules.split(','):                        mod = mod.strip().split('.')[0]  # 取顶层模块                        if mod and not mod.startswith('as'):                            imports.add(mod)        except Exception as e:            print(f"扫描文件失败 {file_path}{e}")        return imports    @classmethod    def scan_project(cls, main_file: str) -> set:        """扫描整个项目目录下的所有Python文件"""        imports = set()        project_dir = Path(main_file).parent        # 扫描主文件        imports.update(cls.scan_file(main_file))        # 扫描项目目录下的所有 .py 文件        for py_file in project_dir.rglob('*.py'):            try:                imports.update(cls.scan_file(str(py_file)))            except BaseException:                pass        # 扫描附加文件夹        # 这里可以扩展,暂时只扫描主文件所在目录        return imports    @classmethod    def filter_third_party(cls, imports: set) -> list:        """过滤出第三方库(非标准库)"""        third_party = []        for mod in imports:            # 跳过标准库            if mod in cls.STDLIB_MODULES:                continue            # 跳过相对导入            if mod.startswith('.'):                continue            # 跳过本地模块(检查是否存在对应的 .py 文件)            # 这部分可能需要更智能的处理            third_party.append(mod)        return third_party    @classmethod    def get_hidden_imports(cls, main_file: str) -> list:        """获取需要隐藏导入的模块列表"""        imports = cls.scan_project(main_file)        third_party = cls.filter_third_party(imports)        hidden_imports = []        for mod in third_party:            # 检查是否有预定义的隐藏导入            if mod in cls.COMMON_HIDDEN_IMPORTS:                hidden_imports.extend(cls.COMMON_HIDDEN_IMPORTS[mod])            else:                # 添加基础模块名                hidden_imports.append(mod)        # 去重        hidden_imports = list(set(hidden_imports))        return hidden_importsclass IconConverter:    @staticmethod    def convert_to_ico(image_path: str) -> str:        """将PNG/JPG/WEBP等格式转换为ICO临时文件"""        if not PIL_AVAILABLE:            return None        try:            img = Image.open(image_path)            # 转换为RGBA模式            if img.mode != 'RGBA':                img = img.convert('RGBA')            # 调整大小为标准图标尺寸            sizes = [(1616), (3232), (4848), (6464), (128128), (256256)]            icons = []            for size in sizes:                resized = img.resize(size, Image.Resampling.LANCZOS)                icons.append(resized)            # 创建临时ICO文件            temp_dir = tempfile.gettempdir()            temp_ico = os.path.join(temp_dir, f"packager_icon_{os.getpid()}.ico")            icons[0].save(temp_ico, format='ICO', sizes=[(i.width, i.height) for i in icons])            return temp_ico        except Exception as e:            print(f"图标转换失败: {e}")            return None# ============== 打包器基类 ==============class BasePacker:    def __init__(self, config, log_callback=None, progress_callback=None, status_callback=None):        self.config = config        self.log_callback = log_callback        self.progress_callback = progress_callback        self.status_callback = status_callback        self.process = None        self.cancelled = False    def log(self, message, level="INFO"):        if self.log_callback:            self.log_callback(message, level)    def update_progress(self, value, status=""):        if self.progress_callback:            self.progress_callback(value)        if self.status_callback and status:            self.status_callback(status)    def cancel(self):        self.cancelled = True        if self.process:            self.process.terminate()    def cleanup(self, project_dir):        for d in ['build''__pycache__']:            p = Path(project_dir) / d            if p.exists():                try:                    shutil.rmtree(p)                except BaseException:                    pass        for f in Path(project_dir).glob("*.spec"):            try:                f.unlink()            except BaseException:                pass# ============== PyInstaller 打包器 ==============class PyInstallerPacker(BasePacker):    def check_install(self):        try:            import PyInstaller            self.log(f"✓ PyInstaller {PyInstaller.__version__}""SUCCESS")            return True        except ImportError:            self.log("安装 PyInstaller...""WARNING")            try:                python_exe = self.config.python_path or sys.executable                subprocess.run([python_exe, "-m""pip""install""pyinstaller""-q"],                               check=True, capture_output=True, timeout=180)                self.log("✓ PyInstaller 安装成功""SUCCESS")                return True            except Exception as e:                self.log(f"✗ 安装失败: {e}""ERROR")                return False    def build_args(self):        args = ['-y']        if self.config.one_file:            args.append('--onefile')        else:            args.append('--onedir')        if self.config.no_console:            args.append('--noconsole')        if self.config.output_name:            args.extend(['--name'self.config.output_name])        if self.config.icon_path:            args.extend(['--icon'self.config.icon_path])        if self.config.output_path:            args.extend(['--distpath'self.config.output_path])        if self.config.enable_upx and self.config.upx_path:            args.extend(['--upx-dir'self.config.upx_path])        for folder in self.config.add_folders:            if folder and Path(folder).exists():                folder_name = Path(folder).name                sep = ';' if sys.platform == 'win32' else ':'                args.extend(['--add-data'f"{folder}{sep}{folder_name}"])        if self.config.speed_mode:            args.extend(['--noupx''-s'])            # 排除一些不必要的模块(但不排除tkinter,因为用户可能需要)            for mod in ['pytest''unittest''IPython''jupyter''numpy.f2py']:                args.extend(['--exclude-module', mod])        # 确保tkinter相关模块被包含        args.extend(['--hidden-import''tkinter'])        args.extend(['--hidden-import''tkinter.filedialog'])        args.extend(['--hidden-import''tkinter.messagebox'])        args.extend(['--hidden-import''tkinter.ttk'])        # 自动扫描并添加依赖库的隐藏导入        if self.config.main_file:            try:                self.log("📦 扫描项目依赖...""INFO")                hidden_imports = DependencyScanner.get_hidden_imports(self.config.main_file)                if hidden_imports:                    self.log(f"✓ 发现依赖: {', '.join(hidden_imports[:10])}{'...'iflen(hidden_imports) > 10else''}",                             "SUCCESS")                    for mod in hidden_imports:                        args.extend(['--hidden-import', mod])                else:                    self.log("✓ 未发现额外依赖""INFO")            except Exception as e:                self.log(f"⚠ 依赖扫描失败: {e}""WARNING")        return args    def pack(self):        self.log("🚀 PyInstaller 打包模式""INFO")        if not self.config.main_file:            self.log("✗ 未选择主文件""ERROR")            return False        main_path = Path(self.config.main_file)        if not main_path.exists():            self.log(f"✗ 文件不存在""ERROR")            return False        self.update_progress(5"检查环境...")        if not self.check_install():            return False        project_dir = str(main_path.parent)        self.update_progress(10"准备打包...")        python_exe = self.config.python_path or sys.executable        args = self.build_args()        cmd = [python_exe, "-m""PyInstaller"] + args + [str(main_path)]        return self._run_command(cmd, project_dir)    def _run_command(self, cmd, cwd):        self.update_progress(20"打包中...")        try:            env = os.environ.copy()            env['PYTHONIOENCODING'] = 'utf-8'            env['PYTHONUTF8'] = '1'            self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd, env=env)            stages = [("Analyzing""分析"30), ("Processing""处理"45),                      ("Collecting""收集"60), ("Building""构建"75),                      ("completed successfully""完成"95)]            current_stage = 0            while True:                if self.cancelled:                    self.process.terminate()                    return False                try:                    line_bytes = self.process.stdout.readline()                    if not line_bytes:                        if self.process.poll() is not None:                            break                        continue                    try:                        line = line_bytes.decode('utf-8', errors='replace').rstrip()                    except BaseException:                        line = line_bytes.decode('gbk', errors='replace').rstrip()                    if line:                        for kw, desc, prog in stages:                            if kw.lower() in line.lower() and prog > current_stage:                                current_stage = prog                                self.update_progress(prog, f"{desc}...")                                break                        lvl = "INFO"                        ll = line.lower()                        if "error" in ll or "failed" in ll:                            lvl = "ERROR"                        elif "warning" in ll:                            lvl = "WARNING"                        elif "completed" in ll or "successfully" in ll:                            lvl = "SUCCESS"                        self.log(line, lvl)                except BaseException:                    break            self.process.wait()            if self.process.returncode == 0:                self.update_progress(100"完成!")                if self.config.one_file:                    self.cleanup(cwd)                self.log("✓ 打包成功!""SUCCESS")                main_path = Path(self.config.main_file)                output_dir = Path(self.config.output_path or main_path.parent) / 'dist'                output_name = self.config.output_name or main_path.stem                output_file = output_dir / f"{output_name}.exe" if self.config.one_file else output_dir / output_name / f"{output_name}.exe"                if output_file.exists():                    size = output_file.stat().st_size / (1024 * 1024)                    self.log(f"输出: {output_file} ({size:.1f}MB)""SUCCESS")                return True            else:                self.log(f"✗ 打包失败""ERROR")                return False        except Exception as e:            self.log(f"✗ 出错: {e}""ERROR")            return False# ============== Nuitka 打包器 ==============class NuitkaPacker(BasePacker):    def check_install(self):        try:            result = subprocess.run([sys.executable, "-m""nuitka""--version"],                                    capture_output=True, text=True, timeout=10)            if result.returncode == 0:                self.log("✓ Nuitka 已安装""SUCCESS")                return True        except BaseException:            pass        self.log("安装 Nuitka...""WARNING")        try:            python_exe = self.config.python_path or sys.executable            subprocess.run([python_exe, "-m""pip""install""nuitka""-q"],                           check=True, capture_output=True, timeout=300)            self.log("✓ Nuitka 安装成功""SUCCESS")            return True        except Exception as e:            self.log(f"✗ 安装失败: {e}""ERROR")            return False    def build_args(self):        args = ['--follow-imports''--assume-yes-for-downloads']        if self.config.one_file:            args.append('--onefile')        if self.config.no_console:            args.append('--disable-console')        if self.config.output_name:            args.extend(['-o'f"{self.config.output_name}.exe"])        if self.config.icon_path:            args.extend(['--windows-icon-from-ico'self.config.icon_path])        if self.config.output_path:            args.extend(['--output-dir'self.config.output_path])        if self.config.enable_upx and self.config.upx_path:            args.extend(['--upx-dir'self.config.upx_path])        for folder in self.config.add_folders:            if folder and Path(folder).exists():                args.extend(['--include-package-data', folder])        if self.config.speed_mode:            args.append('--lto=no')        return args    def pack(self):        self.log("🔥 Nuitka 编译模式""INFO")        if not self.config.main_file:            self.log("✗ 未选择主文件""ERROR")            return False        main_path = Path(self.config.main_file)        if not main_path.exists():            self.log(f"✗ 文件不存在""ERROR")            return False        self.update_progress(5"检查环境...")        if not self.check_install():            return False        project_dir = str(main_path.parent)        self.update_progress(10"准备编译...")        python_exe = self.config.python_path or sys.executable        args = self.build_args()        cmd = [python_exe, "-m""nuitka"] + args + [str(main_path)]        return self._run_command(cmd, project_dir)    def _run_command(self, cmd, cwd):        self.update_progress(15"初始化编译环境...")        self.log("⚠ Nuitka编译可能需要10-30分钟,请耐心等待...""WARNING")        self.log(f"命令: {' '.join(cmd[:5])}...""INFO")        try:            env = os.environ.copy()            env['PYTHONIOENCODING'] = 'utf-8'            self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd, env=env)            progress = 15            line_count = 0            while True:                if self.cancelled:                    self.process.terminate()                    return False                try:                    line_bytes = self.process.stdout.readline()                    if not line_bytes:                        if self.process.poll() is not None:                            break                        continue                    try:                        line = line_bytes.decode('utf-8', errors='replace').rstrip()                    except BaseException:                        line = line_bytes.decode('gbk', errors='replace').rstrip()                    line_count += 1                    # 更频繁地更新进度                    if line:                        ll = line.lower()                        # 根据输出内容更新进度                        if "compiling" in ll:                            progress = min(progress + 170)                            self.update_progress(progress, "编译Python代码...")                        elif "linking" in ll:                            progress = max(progress, 80)                            self.update_progress(progress, "链接可执行文件...")                        elif "optimizing" in ll:                            progress = max(progress, 75)                            self.update_progress(progress, "优化代码...")                        elif "generating" in ll:                            progress = max(progress, 50)                            self.update_progress(progress, "生成中间代码...")                        elif "downloading" in ll:                            self.update_progress(20"下载依赖组件...")                        elif "success" in ll or "completed" in ll:                            progress = max(progress, 95)                            self.update_progress(progress, "编译完成!")                        elif "error" in ll:                            self.log(line, "ERROR")                            continue                        elif line_count % 50 == 0:                            # 每50行更新一次进度,给用户反馈                            progress = min(progress + 0.585)                            self.update_progress(progress, f"编译进行中... ({int(progress)}%)")                        # 确定日志级别                        lvl = "INFO"                        if "error" in ll:                            lvl = "ERROR"                        elif "warning" in ll:                            lvl = "WARNING"                        elif "success" in ll or "completed" in ll:                            lvl = "SUCCESS"                        # 只输出重要信息                        if any(kw in ll for kw in ["error""warning""success""completed""compiling""linking"]):                            self.log(line, lvl)                except BaseException:                    break            self.process.wait()            if self.process.returncode == 0:                self.update_progress(100"完成!")                self.log("✓ 编译成功!""SUCCESS")                # 查找输出文件                main_path = Path(self.config.main_file)                output_dir = Path(self.config.output_path or main_path.parent)                output_name = self.config.output_name or main_path.stem                # Nuitka默认输出到当前目录的output目录                possible_outputs = [                    output_dir / f"{output_name}.exe",                    output_dir / f"{output_name}.onefile-build" / f"{output_name}.exe",                    Path(cwd) / f"{output_name}.exe",                    Path(cwd) / "output" / f"{output_name}.exe",                ]                for output_file in possible_outputs:                    if output_file.exists():                        size = output_file.stat().st_size / (1024 * 1024)                        self.log(f"输出: {output_file} ({size:.1f}MB)""SUCCESS")                        break                return True            else:                self.log("✗ 编译失败,请检查错误信息""ERROR")                return False        except Exception as e:            self.log(f"✗ 出错: {e}""ERROR")            return False# ============== 检测器 ==============class PythonDetector:    @staticmethod    def find_python_interpreters():        interpreters = []        current = sys.executable        version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"        interpreters.append({'path': current, 'version': version, 'name'f"Python {version} (当前)"'current'True})        if sys.platform == 'win32':            import glob            for pattern in [os.path.expandvars(r'%LOCALAPPDATA%\Programs\Python\Python*\python.exe'),                            os.path.expandvars(r'%PROGRAMFILES%\Python*\python.exe')]:                for path in glob.glob(pattern):                    if path != current and os.path.exists(path):                        try:                            result = subprocess.run([path, '--version'], capture_output=True, text=True, timeout=5)                            match = re.search(r'Python (\d+\.\d+\.\d+)', result.stdout + result.stderr)                            if match:                                interpreters.append(                                    {'path': path, 'version'match.group(1), 'name'f"Python {match.group(1)}",                                     'current'False})                        except BaseException:                            pass        seen = set()        return [i for i in interpreters if i['path'not in seen and not seen.add(i['path'])]class UPXDetector:    @staticmethod    def find_upx():        search_paths = [Path(__file__).parent if '__file__' in dir() else Path.cwd(), Path.cwd(),                        Path(sys.argv[0]).parent if sys.argv else Path.cwd()]        for base_path in search_paths:            if not base_path.exists():                continue            for pattern in ['upx-*''upx''UPX*']:                for upx_dir in base_path.glob(pattern):                    if upx_dir.is_dir():                        upx_exe = upx_dir / ('upx.exe' if sys.platform == 'win32' else 'upx')                        if upx_exe.exists():                            return str(upx_dir), upx_dir.name            upx_exe = base_path / ('upx.exe' if sys.platform == 'win32' else 'upx')            if upx_exe.exists():                return str(base_path), base_path.name        return NoneNone# ============== GUI界面 ==============class PackagerGUI:    def __init__(self):        self.root = tk.Tk()        self.root.title("灵感江Python打包exe工具 v1.0")        # 固定窗口大小,确保所有内容完整显示        self.win_width = 580        self.win_height = 780        # 获取屏幕尺寸用于居中        screen_width = self.root.winfo_screenwidth()        screen_height = self.root.winfo_screenheight()        # 居中显示        x = (screen_width - self.win_width) // 2        y = (screen_height - self.win_height) // 2        self.root.geometry(f"{self.win_width}x{self.win_height}+{x}+{y}")        # 设置最小尺寸等于当前尺寸,禁止缩小        self.root.minsize(self.win_width, self.win_height)        self.root.maxsize(self.win_width, self.win_height)        self.theme_name = 'light'        self.theme = Theme.LIGHT        self.config = PackConfig()        self.packer = None        self.log_queue = queue.Queue()        self.python_interpreters = []        self.upx_path = None        self.is_packing = False        self.converted_icon_path = None  # 转换后的图标路径        self.progress_var = tk.DoubleVar(value=0)        self.status_var = tk.StringVar(value="就绪")        self.root.configure(bg=self.theme['bg_primary'])        self._create_ui()        self._process_log_queue()        self.root.after(100self._auto_detect_python)        self.root.after(150self._auto_detect_upx)    def _create_ui(self):        # 主框架 - 使用Canvas和Scrollbar实现滚动        self.main_container = tk.Frame(self.root, bg=self.theme['bg_primary'])        self.main_container.pack(fill=tk.BOTH, expand=True)        # 主内容框架        self.main_frame = tk.Frame(self.main_container, bg=self.theme['bg_primary'])        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=25, pady=20)        # 标题区域        self._create_header()        # 设置卡片(单列布局)        self._create_settings_card()        # 底部按钮区域        self._create_bottom_panel()        # 打包进度弹窗(居中小方块)        self._create_progress_popup()    def _create_header(self):        header = tk.Frame(self.main_frame, bg=self.theme['bg_primary'])        header.pack(fill=tk.X, pady=(015))        # Logo和标题        left = tk.Frame(header, bg=self.theme['bg_primary'])        left.pack(side=tk.LEFT)        # Logo圆形        logo_canvas = tk.Canvas(left, width=42, height=42, bg=self.theme['bg_primary'],                                highlightthickness=0)        logo_canvas.pack(side=tk.LEFT, padx=(012))        logo_canvas.create_oval(443838, fill=self.theme['accent'], outline='')        logo_canvas.create_text(2121, text="Py", font=('Segoe UI'12'bold'), fill='white')        # 标题文字        title_frame = tk.Frame(left, bg=self.theme['bg_primary'])        title_frame.pack(side=tk.LEFT)        self.title_label = tk.Label(title_frame, text="灵感江Python打包exe工具",                                    font=('Microsoft YaHei UI'15'bold'),                                    bg=self.theme['bg_primary'], fg=self.theme['text_primary'])        self.title_label.pack(anchor='w')        self.subtitle_label = tk.Label(title_frame, text="v1.0 精简美化版",                                       font=('Microsoft YaHei UI'9),                                       bg=self.theme['bg_primary'], fg=self.theme['text_muted'])        self.subtitle_label.pack(anchor='w')        # 主题切换按钮        self.theme_btn = tk.Button(header, text="🌙", font=('Segoe UI'14),                                   bg=self.theme['bg_tertiary'], fg=self.theme['text_primary'],                                   relief='flat', width=3, cursor='hand2',                                   command=self._toggle_theme)        self.theme_btn.pack(side=tk.RIGHT)    def _create_settings_card(self):        # 主卡片容器        card = tk.Frame(self.main_frame, bg=self.theme['bg_card'],                        highlightthickness=1, highlightbackground=self.theme['border'])        card.pack(fill=tk.BOTH, expand=True, pady=(012))        # 内边距容器        content = tk.Frame(card, bg=self.theme['bg_card'])        content.pack(fill=tk.BOTH, expand=True, padx=18, pady=15)        # ===== 打包引擎 =====        row = tk.Frame(content, bg=self.theme['bg_card'])        row.pack(fill=tk.X, pady=5)        tk.Label(row, text="打包引擎", font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'],                 fg=self.theme['text_secondary'], width=10, anchor='w').pack(side=tk.LEFT)        engine_frame = tk.Frame(row, bg=self.theme['bg_card'])        engine_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)        self.packer_var = tk.StringVar(value="pyinstaller")        pyinstaller_rb = tk.Radiobutton(engine_frame, text="PyInstaller", variable=self.packer_var,                                        value="pyinstaller", font=('Microsoft YaHei UI'10),                                        bg=self.theme['bg_card'], fg=self.theme['text_primary'],                                        selectcolor=self.theme['bg_tertiary'],                                        activebackground=self.theme['bg_card'])        pyinstaller_rb.pack(side=tk.LEFT)        nuitka_rb = tk.Radiobutton(engine_frame, text="Nuitka", variable=self.packer_var,                                   value="nuitka", font=('Microsoft YaHei UI'10),                                   bg=self.theme['bg_card'], fg=self.theme['text_primary'],                                   selectcolor=self.theme['bg_tertiary'],                                   activebackground=self.theme['bg_card'])        nuitka_rb.pack(side=tk.LEFT, padx=(200))        # ===== Python环境 =====        row = tk.Frame(content, bg=self.theme['bg_card'])        row.pack(fill=tk.X, pady=5)        tk.Label(row, text="Python", font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'],                 fg=self.theme['text_secondary'], width=10, anchor='w').pack(side=tk.LEFT)        self.python_combo = ttk.Combobox(row, font=('Microsoft YaHei UI'10))        self.python_combo.pack(side=tk.LEFT, fill=tk.X, expand=True)        # 选择Python解释器按钮        tk.Button(row, text="选择", font=('Microsoft YaHei UI'9), bg=self.theme['bg_tertiary'],                  fg=self.theme['text_primary'], relief='flat', padx=10, cursor='hand2',                  command=self._browse_python).pack(side=tk.RIGHT, padx=(80))        # 刷新自动检测按钮        tk.Button(row, text="🔄", font=('Segoe UI'9), bg=self.theme['bg_tertiary'],                  fg=self.theme['text_primary'], relief='flat', padx=8, cursor='hand2',                  command=self._auto_detect_python).pack(side=tk.RIGHT, padx=(80))        # ===== 主文件 =====        self.main_file_var = tk.StringVar()        self._create_input_row(content, "*主文件"self.main_file_var, "选择",                               lambdaself._browse_file(self.main_file_var, [("Python文件""*.py")]), True)        # ===== 输出名称 =====        self.output_name_var = tk.StringVar()        self._create_input_row(content, "输出名称"self.output_name_var, NoneFalse)        # ===== 应用图标(支持多格式)=====        self.icon_path_var = tk.StringVar()        self.icon_entry = self._create_input_row(content, "应用图标"self.icon_path_var, "选择",                                                 lambdaself._browse_icon(), False"可选可不选,不选自动默认图标")        # 图标格式提示        icon_hint = tk.Label(content, text="支持 .ico / .png / .jpg / .webp 格式",                             font=('Microsoft YaHei UI'8), bg=self.theme['bg_card'],                             fg=self.theme['text_muted'])        icon_hint.pack(anchor='w', padx=(100), pady=(03))        # ===== 输出路径 =====        self.output_path_var = tk.StringVar()        self._create_input_row(content, "输出路径"self.output_path_var, "选择",                               lambdaself._browse_folder(self.output_path_var), False)        # ===== 选项 =====        options_frame = tk.Frame(content, bg=self.theme['bg_card'])        options_frame.pack(fill=tk.X, pady=(85))        self.one_file_var = tk.BooleanVar(value=False)        self.no_console_var = tk.BooleanVar(value=True)        self.speed_mode_var = tk.BooleanVar(value=True)        tk.Checkbutton(options_frame, text="单文件", variable=self.one_file_var,                       font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'], fg=self.theme['text_primary'],                       selectcolor=self.theme['bg_tertiary'], activebackground=self.theme['bg_card'],                       highlightthickness=0).pack(side=tk.LEFT)        tk.Checkbutton(options_frame, text="隐藏控制台", variable=self.no_console_var,                       font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'], fg=self.theme['text_primary'],                       selectcolor=self.theme['bg_tertiary'], activebackground=self.theme['bg_card'],                       highlightthickness=0).pack(side=tk.LEFT, padx=(150))        tk.Checkbutton(options_frame, text="极速模式", variable=self.speed_mode_var,                       font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'], fg=self.theme['text_primary'],                       selectcolor=self.theme['bg_tertiary'], activebackground=self.theme['bg_card'],                       highlightthickness=0).pack(side=tk.LEFT, padx=(150))        # ===== 分隔线 =====        tk.Frame(content, bg=self.theme['border'], height=1).pack(fill=tk.X, pady=12)        # ===== UPX压缩 =====        upx_row = tk.Frame(content, bg=self.theme['bg_card'])        upx_row.pack(fill=tk.X, pady=5)        self.enable_upx_var = tk.BooleanVar(value=False)        tk.Checkbutton(upx_row, text="UPX压缩", variable=self.enable_upx_var,                       font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'], fg=self.theme['text_primary'],                       selectcolor=self.theme['bg_tertiary'], activebackground=self.theme['bg_card'],                       highlightthickness=0).pack(side=tk.LEFT)        self.upx_status_label = tk.Label(upx_row, text="检测中...",                                         font=('Microsoft YaHei UI'9), bg=self.theme['bg_card'],                                         fg=self.theme['text_muted'])        self.upx_status_label.pack(side=tk.LEFT, padx=(100))        # ===== 附加文件夹 =====        folder_label = tk.Label(content, text="附加文件夹", font=('Microsoft YaHei UI'10),                                bg=self.theme['bg_card'], fg=self.theme['text_secondary'])        folder_label.pack(anchor='w', pady=(83))        folder_row = tk.Frame(content, bg=self.theme['bg_card'])        folder_row.pack(fill=tk.X, pady=3)        self.folder_path_var = tk.StringVar()        tk.Entry(folder_row, textvariable=self.folder_path_var, font=('Microsoft YaHei UI'10),                 bg=self.theme['bg_input'], fg=self.theme['text_primary'], relief='flat',                 highlightthickness=1, highlightbackground=self.theme['border']).pack(            side=tk.LEFT, fill=tk.X, expand=True)        tk.Button(folder_row, text="+", font=('Segoe UI'12'bold'), bg=self.theme['accent'],                  fg='white', relief='flat', padx=12, cursor='hand2',                  command=self._add_folder).pack(side=tk.RIGHT, padx=(80))        tk.Button(folder_row, text="📁", font=('Segoe UI'10), bg=self.theme['bg_tertiary'],                  fg=self.theme['text_primary'], relief='flat', padx=10, cursor='hand2',                  command=lambdaself._browse_folder(self.folder_path_var)).pack(side=tk.RIGHT, padx=(80))        # 文件夹列表        list_frame = tk.Frame(content, bg=self.theme['bg_card'])        list_frame.pack(fill=tk.BOTH, expand=True, pady=5)        self.folder_listbox = tk.Listbox(list_frame, font=('Microsoft YaHei UI'9), height=3,                                         bg=self.theme['bg_input'], fg=self.theme['text_primary'],                                         selectbackground=self.theme['accent_light'], relief='flat',                                         highlightthickness=1, highlightbackground=self.theme['border'])        self.folder_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)        tk.Button(content, text="删除选中", font=('Microsoft YaHei UI'9), bg=self.theme['btn_danger'],                  fg='white', relief='flat', padx=12, cursor='hand2',                  command=self._remove_folder).pack(anchor='w', pady=(50))    def _create_input_row(self, parent, label, var, btn_text, btn_cmd, required=False, placeholder=None):        row = tk.Frame(parent, bg=self.theme['bg_card'])        row.pack(fill=tk.X, pady=5)        fg = self.theme['text_error'if required else self.theme['text_secondary']        tk.Label(row, text=label, font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'],                 fg=fg, width=10, anchor='w').pack(side=tk.LEFT)        entry = tk.Entry(row, textvariable=var, font=('Microsoft YaHei UI'10), bg=self.theme['bg_input'],                         fg=self.theme['text_primary'], relief='flat', highlightthickness=1,                         highlightbackground=self.theme['border'])        entry.pack(side=tk.LEFT, fill=tk.X, expand=True)        # 显示占位符        if placeholder:            entry.insert(0, placeholder)            entry.config(fg=self.theme['text_muted'])            def on_focus_in(event):                if entry.get() == placeholder:                    entry.delete(0, tk.END)                    entry.config(fg=self.theme['text_primary'])            def on_focus_out(event):                if not entry.get():                    entry.insert(0, placeholder)                    entry.config(fg=self.theme['text_muted'])            entry.bind('<FocusIn>', on_focus_in)            entry.bind('<FocusOut>', on_focus_out)        if btn_text:            tk.Button(row, text=btn_text, font=('Microsoft YaHei UI'9), bg=self.theme['bg_tertiary'],                      fg=self.theme['text_primary'], relief='flat', padx=12, cursor='hand2',                      command=btn_cmd).pack(side=tk.RIGHT, padx=(80))        return entry    def _create_bottom_panel(self):        # 按钮区域        btn_frame = tk.Frame(self.main_frame, bg=self.theme['bg_primary'])        btn_frame.pack(pady=(80))        self.start_btn = tk.Button(btn_frame, text="🚀 开始打包", font=('Microsoft YaHei UI'12'bold'),                                   bg=self.theme['bg_tertiary'], fg=self.theme['text_muted'],                                   relief='flat', padx=40, pady=12, cursor='arrow',                                   state=tk.DISABLED, command=self._start_pack)        self.start_btn.pack(side=tk.LEFT, padx=8)        self.cancel_btn = tk.Button(btn_frame, text="取消打包", font=('Microsoft YaHei UI'11),                                    bg=self.theme['bg_tertiary'], fg=self.theme['text_muted'],                                    relief='flat', padx=25, pady=10, cursor='arrow',                                    state=tk.DISABLED, command=self._cancel_pack)        self.cancel_btn.pack(side=tk.LEFT, padx=8)        # 状态栏        self.status_bar = tk.Frame(self.main_frame, bg=self.theme['bg_tertiary'], height=30)        self.status_bar.pack(fill=tk.X, pady=(120))        self.status_bar.pack_propagate(False)        self.footer = tk.Label(self.status_bar, text="⚠ 请先选择主文件", font=('Microsoft YaHei UI'9),                               bg=self.theme['bg_tertiary'], fg=self.theme['text_warning'])        self.footer.pack(side=tk.LEFT, padx=12, pady=5)        tk.Label(self.status_bar, text="v1.0", font=('Microsoft YaHei UI'9),                 bg=self.theme['bg_tertiary'], fg=self.theme['text_muted']).pack(side=tk.RIGHT, padx=12, pady=5)        # 初始化按钮状态        self._update_btn_state()    def _update_btn_state(self):        """更新按钮状态"""        main_file = self.main_file_var.get().strip()        has_main_file = bool(main_file and not main_file.startswith('点击选择'))        if self.is_packing:            # 打包进行中            self.start_btn.config(state=tk.DISABLED, bg=self.theme['bg_tertiary'],                                  fg=self.theme['text_muted'], cursor='arrow')            self.cancel_btn.config(state=tk.NORMAL, bg=self.theme['btn_danger'],                                   fg='white', cursor='hand2')        else:            # 非打包状态            self.cancel_btn.config(state=tk.DISABLED, bg=self.theme['bg_tertiary'],                                   fg=self.theme['text_muted'], cursor='arrow')            if has_main_file:                # 有主文件,可以打包                self.start_btn.config(state=tk.NORMAL, bg=self.theme['accent'],                                      fg='white', cursor='hand2')                self.footer.config(text="✅ 就绪", fg=self.theme['text_secondary'])            else:                # 没有主文件,不能打包                self.start_btn.config(state=tk.DISABLED, bg=self.theme['bg_tertiary'],                                      fg=self.theme['text_muted'], cursor='arrow')                self.footer.config(text="⚠ 请先选择主文件", fg=self.theme['text_warning'])    def _create_progress_popup(self):        """创建居中的小方块进度弹窗"""        # 半透明背景遮罩        self.overlay = tk.Frame(self.root, bg=self.theme['overlay_bg'])        # 居中的小方块容器        self.popup_frame = tk.Frame(self.overlay, bg=self.theme['bg_card'],                                    highlightthickness=1, highlightbackground=self.theme['border'])        # 弹窗标题        self.popup_header = tk.Frame(self.popup_frame, bg=self.theme['bg_card'])        self.popup_header.pack(fill=tk.X, padx=18, pady=(1510))        self.popup_title = tk.Label(self.popup_header, text="正在打包...",                                    font=('Microsoft YaHei UI'12'bold'),                                    bg=self.theme['bg_card'], fg=self.theme['text_primary'])        self.popup_title.pack(side=tk.LEFT)        self.popup_percent = tk.Label(self.popup_header, text="0%",                                      font=('Microsoft YaHei UI'12'bold'),                                      bg=self.theme['bg_card'], fg=self.theme['accent'])        self.popup_percent.pack(side=tk.RIGHT)        # 进度条        self.progress_container = tk.Frame(self.popup_frame, bg=self.theme['border'], height=10)        self.progress_container.pack(fill=tk.X, padx=18, pady=6)        self.progress_container.pack_propagate(False)        self.popup_progress = tk.Canvas(self.progress_container, height=10,                                        bg=self.theme['progress_bg'], highlightthickness=0)        self.popup_progress.pack(fill=tk.BOTH, expand=True, padx=1, pady=1)        # 状态文本        self.popup_status = tk.Label(self.popup_frame, text="准备中...",                                     font=('Microsoft YaHei UI'10), bg=self.theme['bg_card'],                                     fg=self.theme['text_secondary'])        self.popup_status.pack(pady=(210))        # 日志区域(小尺寸)        self.log_frame = tk.Frame(self.popup_frame, bg=self.theme['log_bg'])        self.log_frame.pack(fill=tk.BOTH, expand=True, padx=18, pady=(015))        self.popup_log = tk.Text(self.log_frame, font=('Consolas'9), bg=self.theme['log_bg'],                                 fg=self.theme['log_fg'], relief='flat', state=tk.DISABLED,                                 wrap=tk.WORD, padx=10, pady=8, height=8, width=45)        self.popup_log.pack(fill=tk.BOTH, expand=True)        self.popup_log.tag_configure('INFO', foreground=self.theme['log_info'])        self.popup_log.tag_configure('SUCCESS', foreground=self.theme['log_success'])        self.popup_log.tag_configure('ERROR', foreground=self.theme['log_error'])        self.popup_log.tag_configure('WARNING', foreground=self.theme['log_warning'])        # 结果显示区域(初始隐藏)        self.result_container = tk.Frame(self.popup_frame, bg=self.theme['bg_card'])        self.result_icon = tk.Label(self.result_container, text="", font=('Segoe UI'36),                                    bg=self.theme['bg_card'])        self.result_icon.pack(pady=12)        self.result_text = tk.Label(self.result_container, text="", font=('Microsoft YaHei UI'13'bold'),                                    bg=self.theme['bg_card'], fg=self.theme['text_primary'])        self.result_text.pack(pady=6)        self.result_detail = tk.Label(self.result_container, text="", font=('Microsoft YaHei UI'10),                                      bg=self.theme['bg_card'], fg=self.theme['text_secondary'],                                      wraplength=300, justify='center')        self.result_detail.pack(pady=6)    def _show_progress_popup(self):        """显示进度弹窗"""        # 显示遮罩层        self.overlay.place(x=0, y=0, relwidth=1, relheight=1)        # 使用after确保遮罩层已经渲染        self.root.after(10self._center_popup)        # 禁用开始按钮,启用取消按钮        self.start_btn.config(state=tk.DISABLED, bg=self.theme['bg_tertiary'])        self.cancel_btn.config(state=tk.NORMAL, bg=self.theme['btn_danger'], fg='white')    def _center_popup(self):        """将弹窗居中显示"""        self.root.update_idletasks()        # 获取窗口大小        overlay_width = self.overlay.winfo_width()        overlay_height = self.overlay.winfo_height()        # 设置弹窗大小        popup_width = 420        popup_height = 350        # 计算居中位置        x = (overlay_width - popup_width) // 2        y = (overlay_height - popup_height) // 2        # 放置弹窗        self.popup_frame.place(x=x, y=y, width=popup_width, height=popup_height)    def _hide_progress_popup(self):        """隐藏进度弹窗"""        self.popup_frame.place_forget()        self.overlay.place_forget()        # 重置按钮状态        self.start_btn.config(state=tk.NORMAL, bg=self.theme['accent'])        self.cancel_btn.config(state=tk.DISABLED, bg=self.theme['bg_tertiary'], fg=self.theme['text_muted'])        # 隐藏结果显示        self.result_container.pack_forget()        # 恢复进度元素显示        self.popup_header.pack(fill=tk.X, padx=18, pady=(1510))        self.popup_title.pack(side=tk.LEFT)        self.popup_percent.pack(side=tk.RIGHT)        self.progress_container.pack(fill=tk.X, padx=18, pady=6)        self.popup_status.pack(pady=(210))        self.log_frame.pack(fill=tk.BOTH, expand=True, padx=18, pady=(015))    def _show_result_in_popup(self, success, message, detail=""):        """在弹窗中显示结果"""        # 隐藏进度相关元素        self.popup_header.pack_forget()        self.progress_container.pack_forget()        self.popup_status.pack_forget()        self.log_frame.pack_forget()        # 显示结果        if success:            self.result_icon.config(text="✅", fg=self.theme['text_success'])            self.result_text.config(text="打包成功!", fg=self.theme['text_success'])        else:            self.result_icon.config(text="❌", fg=self.theme['text_error'])            self.result_text.config(text="打包失败", fg=self.theme['text_error'])        self.result_detail.config(text=detail)        self.result_container.pack(fill=tk.BOTH, expand=True, padx=18, pady=12)        # 禁用取消按钮        self.cancel_btn.config(state=tk.DISABLED, bg=self.theme['bg_tertiary'], fg=self.theme['text_muted'])        # 2秒后自动隐藏        self.root.after(2000self._hide_progress_popup)    def _update_progress_ui(self, value):        self.progress_var.set(value)        self._draw_popup_progress()    def _update_status_ui(self, status):        self.status_var.set(status)        self.popup_status.config(text=status)        self.popup_percent.config(text=f"{int(self.progress_var.get())}%")    def _draw_popup_progress(self, event=None):        self.popup_progress.delete('all')        width = self.popup_progress.winfo_width()        height = self.popup_progress.winfo_height()        progress = self.progress_var.get()        fill_width = int(width * progress / 100)        self.popup_percent.config(text=f"{int(progress)}%")        if fill_width > 0:            self.popup_progress.create_rectangle(00, fill_width, height,                                                 fill=self.theme['progress_fg'], outline='')    def _log_callback(self, message, level='INFO'):        self.log_queue.put((message, level))    def _process_log_queue(self):        try:            while True:                message, level = self.log_queue.get_nowait()                self.popup_log.config(state=tk.NORMAL)                self.popup_log.insert(tk.END, message + '\n', level)                self.popup_log.see(tk.END)                self.popup_log.config(state=tk.DISABLED)        except queue.Empty:            pass        self.root.after(100self._process_log_queue)    # ============== 其他方法 ==============    def _auto_detect_python(self):        self.python_interpreters = PythonDetector.find_python_interpreters()        values = [f"{p['name']} - {p['path']}" for p in self.python_interpreters]        self.python_combo['values'] = values        if values:            for i, p in enumerate(self.python_interpreters):                if p.get('current'):                    self.python_combo.current(i)                    break            else:                self.python_combo.current(0)    def _auto_detect_upx(self):        upx_path, upx_name = UPXDetector.find_upx()        if upx_path:            self.upx_path = upx_path            self.upx_status_label.config(text=f"✅ {upx_name}", fg=self.theme['text_success'])        else:            self.upx_path = None            self.upx_status_label.config(text="未检测到", fg=self.theme['text_muted'])    def _toggle_theme(self):        if self.theme_name == 'light':            self.theme_name = 'dark'            self.theme = Theme.DARK            self.theme_btn.config(text="☀️")        else:            self.theme_name = 'light'            self.theme = Theme.LIGHT            self.theme_btn.config(text="🌙")        self._apply_theme()    def _apply_theme(self):        t = self.theme        self.root.configure(bg=t['bg_primary'])        self.main_frame.configure(bg=t['bg_primary'])        self.title_label.configure(bg=t['bg_primary'], fg=t['text_primary'])        self.subtitle_label.configure(bg=t['bg_primary'], fg=t['text_muted'])        self.theme_btn.configure(bg=t['bg_tertiary'], fg=t['text_primary'])        self.footer.configure(bg=t['bg_tertiary'], fg=t['text_secondary'])        self.status_bar.configure(bg=t['bg_tertiary'])        self.overlay.configure(bg=t['overlay_bg'])        self.popup_frame.configure(bg=t['bg_card'], highlightbackground=t['border'])        self.popup_header.configure(bg=t['bg_card'])        self.popup_title.configure(bg=t['bg_card'], fg=t['text_primary'])        self.popup_percent.configure(bg=t['bg_card'], fg=t['accent'])        self.progress_container.configure(bg=t['border'])        self.popup_progress.configure(bg=t['progress_bg'])        self.popup_status.configure(bg=t['bg_card'], fg=t['text_secondary'])        self.log_frame.configure(bg=t['log_bg'])        self.popup_log.configure(bg=t['log_bg'], fg=t['log_fg'])        self.result_container.configure(bg=t['bg_card'])        self.result_icon.configure(bg=t['bg_card'])        self.result_text.configure(bg=t['bg_card'])        self.result_detail.configure(bg=t['bg_card'], fg=t['text_secondary'])    def _browse_file(self, var, filetypes):        f = filedialog.askopenfilename(filetypes=filetypes)        if f:            var.set(f)            # 如果是主文件选择,更新按钮状态            if var == self.main_file_var:                self._update_btn_state()    def _browse_folder(self, var):        f = filedialog.askdirectory()        if f:            var.set(f)    def _browse_icon(self):        """选择图标文件,支持多种格式"""        filetypes = [            ("图标文件""*.ico *.png *.jpg *.jpeg *.webp *.bmp *.gif"),            ("ICO 文件""*.ico"),            ("PNG 文件""*.png"),            ("JPG 文件""*.jpg *.jpeg"),            ("WEBP 文件""*.webp"),            ("所有文件""*.*")        ]        f = filedialog.askopenfilename(filetypes=filetypes)        if f:            self.icon_path_var.set(f)    def _browse_python(self):        """手动选择Python解释器"""        if sys.platform == 'win32':            filetypes = [("Python解释器""python.exe"), ("所有文件""*.*")]        else:            filetypes = [("Python解释器""python python3"), ("所有文件""*.*")]        f = filedialog.askopenfilename(filetypes=filetypes, title="选择Python解释器")        if f:            # 获取Python版本            try:                result = subprocess.run([f, '--version'], capture_output=True, text=True, timeout=5)                match = re.search(r'Python (\d+\.\d+\.\d+)', result.stdout + result.stderr)                if match:                    version = match.group(1)                    display_text = f"Python {version} - {f}"                else:                    display_text = f            except BaseException:                display_text = f            # 更新下拉框            current_values = list(self.python_combo['values'])            if display_text not in current_values:                current_values.insert(0, display_text)                self.python_combo['values'] = current_values            self.python_combo.set(display_text)    def _add_folder(self):        folder = self.folder_path_var.get().strip()        if folder and Path(folder).exists() and folder not in self.folder_listbox.get(0, tk.END):            self.folder_listbox.insert(tk.END, folder)            self.folder_path_var.set('')    def _remove_folder(self):        sel = self.folder_listbox.curselection()        if sel:            self.folder_listbox.delete(sel[0])    def _get_config_from_ui(self):        py_text = self.python_combo.get()        python_path = py_text.split(' - ')[-1if ' - ' in py_text else ''        # 处理图标转换        icon_path = self.icon_path_var.get()        # 检查是否是占位符文本        placeholder_texts = ['可选''默认''不选''自动''选择图标']        is_placeholder = any(txt in icon_path for txt in placeholder_texts) if icon_path else False        if is_placeholder or not icon_path:            icon_path = ''        elif icon_path:            ext = Path(icon_path).suffix.lower()            if ext != '.ico':                # 需要转换                if PIL_AVAILABLE:                    converted_icon = IconConverter.convert_to_ico(icon_path)                    if converted_icon:                        self.converted_icon_path = converted_icon                        icon_path = converted_icon                else:                    # PIL不可用,提示用户                    messagebox.showwarning('提示',                                           '选择的是非ICO格式图标,但PIL库未安装。\n图标将不会被应用到打包结果中。')        return PackConfig(            python_path=python_path,            main_file=self.main_file_var.get(),            output_name=self.output_name_var.get(),            icon_path=icon_path,            output_path=self.output_path_var.get(),            packer=self.packer_var.get(),            one_file=self.one_file_var.get(),            no_console=self.no_console_var.get(),            speed_mode=self.speed_mode_var.get(),            enable_upx=self.enable_upx_var.get() and self.upx_path is not None,            upx_path=self.upx_path or "",            add_folders=list(self.folder_listbox.get(0, tk.END)),        )    def _start_pack(self):        config = self._get_config_from_ui()        if not config.main_file:            messagebox.showerror('错误''请选择主Python文件')            return        # 设置打包状态        self.is_packing = True        self._update_btn_state()        # 重置进度弹窗        self.result_container.pack_forget()        self.popup_log.config(state=tk.NORMAL)        self.popup_log.delete(1.0, tk.END)        self.popup_log.config(state=tk.DISABLED)        self.progress_var.set(0)        self.popup_percent.config(text="0%")        # 显示进度弹窗        self._show_progress_popup()        self.footer.config(text='🔄 打包中...')        # 选择打包器        if config.packer == 'nuitka':            self.packer = NuitkaPacker(config, self._log_callback,                                       lambda v: self.root.after(0lambdaself._update_progress_ui(v)),                                       lambda s: self.root.after(0lambdaself._update_status_ui(s)))        else:            self.packer = PyInstallerPacker(config, self._log_callback,                                            lambda v: self.root.after(0lambdaself._update_progress_ui(v)),                                            lambda s: self.root.after(0lambdaself._update_status_ui(s)))        def pack_thread():            try:                success = self.packer.pack()                self.root.after(0lambdaself.progress_var.set(100))                self.root.after(0self._draw_popup_progress)                if success:                    main_path = Path(config.main_file)                    output_dir = Path(config.output_path or main_path.parent) / 'dist'                    output_name = config.output_name or main_path.stem                    output_file = output_dir / f"{output_name}.exe"                    self.root.after(0lambdaself.footer.config(text='✅ 打包完成'))                    self.root.after(0lambdaself._show_result_in_popup(                        True"打包成功!"f"输出: {output_file}"))                else:                    self.root.after(0lambdaself.footer.config(text='❌ 打包失败'))                    self.root.after(0lambdaself._show_result_in_popup(                        False"打包失败""请查看日志了解详情"))            except Exception as e:                self.root.after(0lambdaself.footer.config(text='❌ 打包失败'))                self.root.after(0lambdaself._show_result_in_popup(                    False"打包失败"str(e)))            finally:                self.is_packing = False                self.root.after(0self._update_btn_state)                self.packer = None                # 清理临时图标文件                if self.converted_icon_path and os.path.exists(self.converted_icon_path):                    try:                        os.remove(self.converted_icon_path)                    except BaseException:                        pass                    self.converted_icon_path = None        threading.Thread(target=pack_thread, daemon=True).start()    def _cancel_pack(self):        if self.packer:            self.packer.cancel()            self.is_packing = False            self._update_btn_state()            self._show_result_in_popup(False"已取消""打包已被用户取消")            self.footer.config(text='⏹️ 已取消')    def run(self):        self.root.mainloop()if __name__ == '__main__':    app = PackagerGUI()    app.run()
给粉丝朋友们的一封信:
最近开了流量主功能,就是发布的内容底下会有图片形式的广告,希望大家点击进去看看,(不想看,点击进去出来就行)就是我有钱赚了,虽然就十几块,几十块,但是可能够我租用服务器和域名为你们服务了,反正网站一直免费那也得需要点钱,可能对有些人来说不多,但是一直自己花钱买那些东西,一直免费还是会有点难处,也希望你们的支持,我这不是要把网站改成付费的意思,就是希望你们帮我挣点零钱租用所需东西为你们继续服务,谢谢大家!

记得给亲朋好友推荐一下此公众号,让更多的人享受免费,开源,破解资源!

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-03-27 18:22:16 HTTP/2.0 GET : https://f.mffb.com.cn/a/479381.html
  2. 运行时间 : 0.214620s [ 吞吐率:4.66req/s ] 内存消耗:5,102.02kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=c9751625f7e278499414aa82e81a3603
  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.001051s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001564s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000708s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000627s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001195s ]
  6. SELECT * FROM `set` [ RunTime:0.000534s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001347s ]
  8. SELECT * FROM `article` WHERE `id` = 479381 LIMIT 1 [ RunTime:0.001620s ]
  9. UPDATE `article` SET `lasttime` = 1774606936 WHERE `id` = 479381 [ RunTime:0.028179s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.004559s ]
  11. SELECT * FROM `article` WHERE `id` < 479381 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001139s ]
  12. SELECT * FROM `article` WHERE `id` > 479381 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001279s ]
  13. SELECT * FROM `article` WHERE `id` < 479381 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.003065s ]
  14. SELECT * FROM `article` WHERE `id` < 479381 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003005s ]
  15. SELECT * FROM `article` WHERE `id` < 479381 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.005877s ]
0.216536s