Python打包exe小工具,解决打包后体积过大痛点,基于坛友分享

admin
🌐 经济型:买域名、轻量云服务器、用途:游戏 网站等 《腾讯云》特点:特价机便宜 适合初学者用 点我优惠购买
🚀 拓展型:买域名、轻量云服务器、用途:游戏 网站等 《阿里云》特点:中档服务器便宜 域名备案事多 点我优惠购买
🛡️ 稳定型:买域名、轻量云服务器、用途:游戏 网站等 《西部数码》 特点:比上两家略贵但是稳定性超好事也少 点我优惠购买
python打包exe,解决打包后体积过大的痛点,基于坛友分享的知识和源码,表示感谢! 实现方法: 1、临时创建一个新的运行环境; 2、解析py脚本的运行依赖库,自动安装,采用清华源; 3、如果存在不全解析的库,可以手动添加; 4、打包结束后,删除临时运行环境。 通过网盘分享的文件:Python打包exe小工具 链接: 提取码: --来自百度网盘超级会员v5的分享 (提示:电脑需要安装有Python环境) Python打包exe小工具,解决打包后体积过大痛点,基于坛友分享 经实测,打包后的体积,成倍数的缩小,再一次感谢坛友的分享! 欢迎各位修改提高,也请不吝分享。
  1. # !/usr/bin/env python
  2. # Coding=utf-8
  3. import sys
  4. import os
  5. import re
  6. import shutil
  7. import subProcess
  8. import venv
  9. import ast
  10. from PyQt6.QtWidgets import (QAPPlication, QMAInWindow, QPushButton,
  11.                              QFileDialog, QLabel, QVBoxLayout, QWidget,
  12.                              QProgressBar, QCheckBox, QGroupBox, QGridLayout,
  13.                              QLineEdit, QTextEdit, QHBoxLayout, QMessageBox, QListWidget)
  14. from PyQt6.QtCore import Qt, QThread, pyqtSignal
  15. from PyQt6.QtGUI import QFont
  16. PACKAGE_MAPPING = {
  17.     # 科学计算 / 数据处理
  18.     'PIL': 'Pillow',
  19.     'cv2': 'opencv-python',
  20.     'sklearn': 'scikit-learn',
  21.     'yaml': 'PyYAML',
  22.     'bs4': 'beautifulsoup4',
  23.     'attr': 'attrs',
  24.     'gi': 'PyGObject',
  25.     'WX': 'wxPython',
  26.     'fitz': 'PyMuPDF',
  27.     'docx': 'python-docx',
  28.     'PPTx': 'python-pptx',
  29.     'tables': 'pytables',
  30.     'xlsxwriter': 'XlsxWriter',
  31.     # Web / 网络
  32.     'jwt': 'PyJWT',
  33.     'jose': 'python-jose',
  34.     'dotenv': 'python-dotenv',
  35.     'socks': 'PySocks',
  36.     'websocket': 'websocket-client',
  37.     'flask': 'Flask',
  38.     'django': 'Django',
  39.     # GUI / 图形
  40.     'tkinter': None,  # 标准库,标记为None跳过安装
  41.     'kivy': 'Kivy',
  42.     'pyautogui': 'PyAutoGUI',
  43.     'customtkinter': 'customtkinter',
  44.     'ttkbootstrap': 'ttkbootstrap',
  45.     # 数据库
  46.     'Psycopg2': 'psycopg2-binary',
  47.     'pymysql': 'PyMySQL',
  48.     'mysql': 'mysql-connector-python',
  49.     'sqlalchemy': 'SQLAlchemy',
  50.     'sqlite3': None,  # 标准库
  51.     # 加密 / 安全
  52.     'Crypto': 'pycryptodome',
  53.     'Cryptodome': 'pycryptodome',
  54.     'nacl': 'PyNaCl',
  55.     'OpenSSL': 'pyOpenSSL',
  56.     # 工具 / 杂项
  57.     'dateutil': 'python-dateutil',
  58.     'serial': 'pyserial',
  59.     'usb': 'pyusb',
  60.     'magic': 'python-magic',
  61.     'slugify': 'python-slugify',
  62.     'barcode': 'python-barcode',
  63.     'win32api': 'pywin32',
  64.     'win32com': 'pywin32',
  65.     'winreg': None,  # 标准库
  66.     'apscheduler': 'APScheduler',
  67.     'charset_normalizer': 'charset-normalizer',
  68.     'Levenshtein': 'python-Levenshtein',
  69.     'qrcode': 'qrcode',
  70.     'zbar': 'pyzbar',
  71. }
  72. class PackThread(QThread):
  73.     progress = pyqtSignal(str)
  74.     finished = pyqtSignal(bool, str)
  75.     stop_flag = False
  76.     def __init__(self, file_path, options, manual_deps):
  77.         super().__init__()
  78.         self.file_path = file_path
  79.         self.options = options
  80.         self.manual_deps = manual_deps
  81.         self.stop_flag = False
  82.         self.venv_dir = None
  83.     def log(self, message):
  84.         self.progress.emit(message)
  85.     def run_command(self, cmd, cwd=None, env=None):
  86.         """执行命令实时返回输出,统一使用UTF-8编码防止GBK崩溃"""
  87.         try:
  88.             process = subprocess.Popen(
  89.                 cmd,
  90.                 stdout=subprocess.PIPE,
  91.                 stderr=subprocess.STDOUT,
  92.                 text=True,
  93.                 encoding='utf-8',       # ✅ 修复: 显式指定UTF-8
  94.                 errors='replace',       # ✅ 修复: 无法解码的字符用替代而非抛异常
  95.                 cwd=cwd,
  96.                 env=env,
  97.                 bufsize=1,
  98.                 universal_newlines=True
  99.             )
  100.             for line in iter(process.stdout.readline, ''):
  101.                 if self.stop_flag:
  102.                     process.terminate()
  103.                     return False, "用户停止操作"
  104.                 if line:
  105.                     self.log(line.strip())
  106.             process.wait()
  107.             return process.returncode == 0, ""
  108.         except Exception as e:
  109.             return False, str(e)
  110.     def extract_imports(self, file_path):
  111.         """使用AST提取第三方库名称,并应用包名映射"""
  112.         imports = set()
  113.         try:
  114.             with open(file_path, 'r', encoding='utf-8') as f:
  115.                 tree = ast.parse(f.read(), filename=file_path)
  116.             for node in ast.walk(tree):
  117.                 if isinstance(node, ast.Import):
  118.                     for alias in node.names:
  119.                         imports.add(alias.name.split('.')[0])
  120.                 elif isinstance(node, ast.ImportFrom):
  121.                     if node.module:
  122.                         imports.add(node.module.split('.')[0])
  123.         except Exception as e:
  124.             self.log(f"警告: 无法解析导入语句: {str(e)}")
  125.         # 过滤标准库(优先使用Python内置集合,回退到手动列表)
  126.         stdlib_modules = set(sys.stdlib_module_names) if hasattr(sys, 'stdlib_module_names') else {
  127.             'os', 'sys', 're', 'json', 'time', 'datetime', 'math', 'random',
  128.             'collections', 'itertools', 'functools', 'pathlib', 'typing',
  129.             'subprocess', 'shutil', 'glob', 'copy', 'string', 'io', 'abc',
  130.             'contextlib', 'unittest', 'logging', 'argparse', 'configparser',
  131.             'csv', 'xml', 'html', 'http', 'urllib', 'socket', 'ssl', 'threading',
  132.             'multiprocessing', 'concurrent', 'asyncio', 'signal', 'mmap', 'struct',
  133.             'codecs', 'unicodedata', 'textwrap', 'difflib', 'pprint', 'enum',
  134.             'operator', 'decimal', 'fractions', 'statistics', 'array',
  135.             'bisect', 'heapq', 'queue', 'types', 'weakref', 'gc', 'inspect',
  136.             'dis', 'pickle', 'shelve', 'dbm', 'sqlite3', 'zlib', 'gzip', 'bz2',
  137.             'lzma', 'zipfile', 'tarfile', 'tempfile', 'fnmatch', 'linecache',
  138.             'tokenize', 'token', 'keyWord', 'ast', 'symtable', 'compileall',
  139.             'py_compile', 'traceback', 'warnings', 'dataclasses',
  140.             'importlib', 'pkgutil', 'modulefinder', 'runpy', 'site', 'sysconfig',
  141.             'builtins', 'errno', 'ctypes', 'platform', 'locale', 'gettext',
  142.             'curses', 'readline', 'rlcompleter', 'tty', 'termios', 'pty', 'fcntl',
  143.             'pipes', 'posixpath', 'ntpath', 'genericpath', 'posix', 'nt', 'pwd',
  144.             'grp', 'resource', 'select', 'selectors', 'stat', 'fileinput', 'filecmp',
  145.             'netrc', 'xdrlib', 'plistlib', 'hashlib', 'hmac', 'secrets', 'uuid',
  146.             'ipaddress', 'email', 'mailbox', 'mimetypes', 'base64', 'binascii',
  147.             'quopri', 'uu', 'colorsys', 'imghdr', 'sndhdr', 'ossaudiodev', 'aifc',
  148.             'sunau', 'wave', 'chunk', 'cgi', 'cgitb', 'wsgiref', 'xmlrPC', 'ftplib',
  149.             'poplib', 'imaplib', 'nntplib', 'smtplib', 'smtpd', 'telnetlib', 'socketserver',
  150.             'robotparser', 'webbrowser', 'cmd', 'shlex', 'tkinter',
  151.             'turtle', 'turtledemo', 'idlelib', 'test', 'doctest', 'unittest',
  152.             'lib2to3', 'pdb', 'profile', 'cProfile', 'timeit', 'trace', 'tracemalloc',
  153.             'faulthandler', 'atexit'
  154.         }
  155.         mapped_deps = []
  156.         for mod in imports:
  157.             if mod in stdlib_modules or mod.startswith('.'):
  158.                 continue
  159.             mapped = PACKAGE_MAPPING.get(mod)
  160.             if mapped is None and mod in PACKAGE_MAPPING:
  161.                 # 显式标记为None的是标准库,跳过
  162.                 continue
  163.             mapped_deps.append(mapped if mapped else mod)
  164.         return list(set(mapped_deps))
  165.     def run(self):
  166.         if self.stop_flag:
  167.             return
  168.         script_dir = os.path.dirname(os.path.abspath(self.file_path))
  169.         script_name = os.path.basename(self.file_path)
  170.         base_name = self.options['name'] if self.options['name'] else os.path.splitext(script_name)[0]
  171.         # 清理非法字符
  172.         if not re.match(r'^[a-zA-Z0-9_\-.\u4.00-\u9fa5][a-zA-Z0-9_\-.\u4e00-\u9fa5 ]*$', base_name):
  173.             self.finished.emit(False, "程序名称包含非法字符或格式不正确!")
  174.             return
  175.         dist_dir = os.path.join(script_dir, 'dist_clean')
  176.         venv_path = os.path.join(script_dir, '.temp_build_venv')
  177.         self.venv_dir = venv_path
  178.         try:
  179.             # 1. 创建虚拟环境
  180.             self.log(f"正在创建临时虚拟环境: {venv_path}")
  181.             if os.path.exists(venv_path):
  182.                 shutil.rmtree(venv_path)
  183.             venv.create(venv_path, with_pip=True)
  184.             self.log("虚拟环境创建成功")
  185.             # 确定虚拟环境中的 python 和 pip 路径
  186.             if os.name == 'nt':
  187.                 py_exe = os.path.join(venv_path, 'Scripts', 'python.exe')
  188.                 pip_exe = os.path.join(venv_path, 'Scripts', 'pip.exe')
  189.                 pyinstaller_exe = os.path.join(venv_path, 'Scripts', 'pyinstaller.exe')
  190.             else:
  191.                 py_exe = os.path.join(venv_path, 'bin', 'python')
  192.                 pip_exe = os.path.join(venv_path, 'bin', 'pip')
  193.                 pyinstaller_exe = os.path.join(venv_path, 'bin', 'pyinstaller')
  194.             # 2. 安装 PyInstaller
  195.             self.log("正在安装 PyInstaller...")
  196.             success, msg = self.run_command(
  197.                 [pip_exe, 'install', 'pyinstaller', '-i', 'https://pypi.tuna.tsinghua.edu.cn/simple', '--trusted-host',
  198.                  'pypi.tuna.tsinghua.edu.cn'])
  199.             if not success:
  200.                 self.finished.emit(False, f"安装 PyInstaller 失败: {msg}")
  201.                 return
  202.             # 3. 分析并安装项目依赖
  203.             self.log("正在分析源码依赖...")
  204.             deps = self.extract_imports(self.file_path)
  205.             # 合并手动添加的依赖
  206.             if self.manual_deps:
  207.                 for dep in self.manual_deps:
  208.                     dep = dep.strip()
  209.                     if dep and dep not in deps:
  210.                         deps.append(dep)
  211.                 self.log(f"已添加手动指定的依赖: {', '.join(self.manual_deps)}")
  212.             if deps:
  213.                 self.log(f"最终需要安装的依赖: {', '.join(deps)}")
  214.                 self.log("正在安装项目依赖(使用清华源加速)...")
  215.                 install_cmd = [pip_exe, 'install'] + deps + ['-i', 'https://pypi.tuna.tsinghua.edu.cn/simple',
  216.                                                              '--trusted-host', 'pypi.tuna.tsinghua.edu.cn']
  217.                 success, msg = self.run_command(install_cmd)
  218.                 if not success:
  219.                     self.log(f"部分依赖安装可能失败,继续尝试打包: {msg}")
  220.             else:
  221.                 self.log("未检测到明显的第三方依赖")
  222.             # 4. 执行 PyInstaller
  223.             self.log("开始打包...")
  224.             command = [pyinstaller_exe]
  225.             if self.options['onefile']:
  226.                 command.append('--onefile')
  227.             else:
  228.                 command.append('--onedir')
  229.             if self.options['noconsole']:
  230.                 command.append('--noconsole')
  231.                 command.append('--windowed')
  232.             command.Extend(['--clean'])
  233.             command.extend(['--distpath', dist_dir])
  234.             command.extend(['--workpath', os.path.join(script_dir, 'build_temp')])
  235.             # 处理图标
  236.             if self.options['icon'] and os.path.exists(self.options['icon']):
  237.                 command.extend(['--icon', self.options['icon']])
  238.             # 处理附加数据 (images 文件夹)
  239.             images_dir = os.path.join(script_dir, 'images')
  240.             if os.path.exists(images_dir):
  241.                 if os.name == 'nt':
  242.                     command.extend(['--add-data', f'{images_dir};images'])
  243.                 else:
  244.                     command.extend(['--add-data', f'{images_dir}:images'])
  245.             command.extend(['--name', base_name])
  246.             command.append(self.file_path)
  247.             self.log(f"执行命令: {' '.join(command)}")
  248.             success, msg = self.run_command(command, cwd=script_dir)
  249.             if not success:
  250.                 self.finished.emit(False, f"打包失败: {msg}")
  251.                 return
  252.             # 5. 验证输出
  253.             output_ext = '.exe' if os.name == 'nt' else ''
  254.             if self.options['onefile']:
  255.                 final_path = os.path.join(dist_dir, f"{base_name}{output_ext}")
  256.             else:
  257.                 final_path = os.path.join(dist_dir, base_name)
  258.             if os.path.exists(final_path):
  259.                 self.log(f"打包成功!输出路径: {final_path}")
  260.                 self.finished.emit(True, f"转换成功!请查看 {final_path}")
  261.             else:
  262.                 self.finished.emit(False, "打包完成但未找到输出文件,请检查日志")
  263.         except Exception as e:
  264.             self.log(f"发生未知错误: {str(e)}")
  265.             self.finished.emit(False, f"发生未知错误: {str(e)}")
  266.         finally:
  267.             # 6. 清理工作
  268.             self.log("正在清理临时文件...")
  269.             if os.path.exists(venv_path):
  270.                 try:
  271.                     shutil.rmtree(venv_path)
  272.                     self.log("已删除临时虚拟环境")
  273.                 except Exception as e:
  274.                     self.log(f"删除虚拟环境失败: {str(e)}")
  275.             build_temp = os.path.join(script_dir, 'build_temp')
  276.             if os.path.exists(build_temp):
  277.                 try:
  278.                     shutil.rmtree(build_temp)
  279.                 except Exception:
  280.                     pass
  281.             self.log("清理完成")
  282. class Py2ExeConverter(QMainWindow):
  283.     def __init__(self):
  284.         super().__init__()
  285.         self.setWindowTitle("Python打包exe小工具")
  286.         self.setGeometry(100, 100, 800, 650)
  287.         self.setStyleSheet("""
  288.             QMainWindow { background-color: #f0f2f5; }
  289.             QGroupBox { font-weight: bold; color: #333; border: 1px solid #ddd; border-radius: 5px; margin-top: 10px; padding-top: 10px; }
  290.             QPushButton { background-color: #0078d4; color: white; border: none; padding: 8px 16px; border-radius: 4px; font-weight: bold; }
  291.             QPushButton:hover { background-color: #005a9e; }
  292.             QPushButton:disabled { background-color: #ccc; }
  293.             QLineEdit, QTextEdit, QListWidget { border: 1px solid #ccc; border-radius: 4px; padding: 5px; background: white; }
  294.             QProgressBar { height: 10px; border-radius: 5px; }
  295.             QProgressBar::chunk { background-color: #0078d4; }
  296.         """)
  297.         central_widget = QWidget()
  298.         self.setCentralWidget(central_widget)
  299.         main_layout = QVBoxLayout(central_widget)
  300.         # 文件选择区域
  301.         file_group = QGroupBox("1. 选择 Python 脚本")
  302.         file_layout = QHBoxLayout()
  303.         self.file_input = QLineEdit()
  304.         self.file_input.setPlaceholderText("请选择 .py 文件")
  305.         self.file_input.setReadOnly(True)
  306.         self.btn_browse = QPushButton("浏览")
  307.         self.btn_browse.clicked.connect(self.browse_file)
  308.         file_layout.addWidget(self.file_input)
  309.         file_layout.addWidget(self.btn_browse)
  310.         file_group.setLayout(file_layout)
  311.         main_layout.addWidget(file_group)
  312.         # 依赖管理区域
  313.         dep_group = QGroupBox("2. 依赖管理 (解决缺失库问题)")
  314.         dep_layout = QVBoxLayout()
  315.         dep_hint = QLabel("如果打包后报错缺少模块,请在此处手动添加库名(如:pandas, numpy)")
  316.         dep_hint.setStyleSheet("color: #666; font-size: 12px;")
  317.         dep_layout.addWidget(dep_hint)
  318.         dep_input_layout = QHBoxLayout()
  319.         self.dep_input = QLineEdit()
  320.         self.dep_input.setPlaceholderText("输入库名,例如: pandas")
  321.         self.btn_add_dep = QPushButton("添加")
  322.         self.btn_add_dep.clicked.connect(self.add_manual_dep)
  323.         self.btn_clear_dep = QPushButton("清空")
  324.         self.btn_clear_dep.clicked.connect(self.clear_manual_deps)
  325.         dep_input_layout.addWidget(self.dep_input)
  326.         dep_input_layout.addWidget(self.btn_add_dep)
  327.         dep_input_layout.addWidget(self.btn_clear_dep)
  328.         dep_layout.addLayout(dep_input_layout)
  329.         self.dep_list = QListWidget()
  330.         self.dep_list.setMaximumHeight(100)
  331.         dep_layout.addWidget(self.dep_list)
  332.         dep_group.setLayout(dep_layout)
  333.         main_layout.addWidget(dep_group)
  334.         # 打包选项区域
  335.         options_group = QGroupBox("3. 打包选项")
  336.         options_layout = QGridLayout()
  337.         self.check_onefile = QCheckBox("单文件模式 (--onefile)")
  338.         self.check_onefile.setChecked(True)
  339.         options_layout.addWidget(self.check_onefile, 0, 0)
  340.         self.check_noconsole = QCheckBox("无控制台窗口 (--windowed)")
  341.         self.check_noconsole.setChecked(False)
  342.         options_layout.addWidget(self.check_noconsole, 0, 1)
  343.         self.input_name = QLineEdit()
  344.         self.input_name.setPlaceholderText("可选:生成的 exe 名称")
  345.         options_layout.addWidget(QLabel("程序名称:"), 1, 0)
  346.         options_layout.addWidget(self.input_name, 1, 1)
  347.         self.input_icon = QLineEdit()
  348.         self.input_icon.setPlaceholderText("可选:图标文件 (.ico)")
  349.         self.btn_icon = QPushButton("选择图标")
  350.         self.btn_icon.clicked.connect(self.browse_icon)
  351.         icon_layout = QHBoxLayout()
  352.         icon_layout.addWidget(self.input_icon)
  353.         icon_layout.addWidget(self.btn_icon)
  354.         options_layout.addWidget(QLabel("图标:"), 2, 0)
  355.         options_layout.addLayout(icon_layout, 2, 1)
  356.         options_group.setLayout(options_layout)
  357.         main_layout.addWidget(options_group)
  358.         # 控制按钮
  359.         btn_layout = QHBoxLayout()
  360.         self.btn_start = QPushButton("开始打包")
  361.         self.btn_start.clicked.connect(self.start_conversion)
  362.         self.btn_stop = QPushButton("停止")
  363.         self.btn_stop.setEnabled(False)
  364.         self.btn_stop.clicked.connect(self.stop_conversion)
  365.         btn_layout.addWidget(self.btn_start)
  366.         btn_layout.addWidget(self.btn_stop)
  367.         main_layout.addLayout(btn_layout)
  368.         # 日志区域
  369.         log_group = QGroupBox("4. 进度日志")
  370.         log_layout = QVBoxLayout()
  371.         self.log_text = QTextEdit()
  372.         self.log_text.setReadOnly(True)
  373.         self.log_text.setFont(QFont("Consolas", 9))
  374.         self.progress_bar = QProgressBar()
  375.         self.progress_bar.setTextVisible(False)
  376.         log_layout.addWidget(self.log_text)
  377.         log_layout.addWidget(self.progress_bar)
  378.         log_group.setLayout(log_layout)
  379.         main_layout.addWidget(log_group)
  380.         self.thread = None
  381.         self.manual_deps = []
  382.     def browse_file(self):
  383.         file_path, _ = QFileDialog.getOpenFileName(self, "选择 Python 脚本", "", "Python Files (*.py)")
  384.         if file_path:
  385.             self.file_input.setText(file_path)
  386.     def browse_icon(self):
  387.         file_path, _ = QFileDialog.getOpenFileName(self, "选择图标", "", "Icon Files (*.ico);;All Files (*)")
  388.         if file_path:
  389.             self.input_icon.setText(file_path)
  390.     def add_manual_dep(self):
  391.         dep = self.dep_input.text().strip()
  392.         if dep:
  393.             if re.match(r'^[a-zA-Z0-9_\-\.]+$', dep):
  394.                 if dep not in self.manual_deps:
  395.                     self.manual_deps.append(dep)
  396.                     self.dep_list.addItem(dep)
  397.                     self.dep_input.clear()
  398.                 else:
  399.                     QMessageBox.warning(self, "提示", "该依赖已存在")
  400.             else:
  401.                 QMessageBox.warning(self, "错误", "库名格式不正确,只能包含字母、数字、下划线、横线和点")
  402.         else:
  403.             QMessageBox.warning(self, "提示", "请输入库名")
  404.     def clear_manual_deps(self):
  405.         self.manual_deps.clear()
  406.         self.dep_list.clear()
  407.     def log_message(self, msg):
  408.         self.log_text.append(msg)
  409.         scrollbar = self.log_text.verticalScrollBar()
  410.         scrollbar.setValue(scrollbar.maximum())
  411.     def start_conversion(self):
  412.         file_path = self.file_input.text()
  413.         if not file_path or not os.path.exists(file_path):
  414.             QMessageBox.warning(self, "错误", "请先选择一个有效的 Python 脚本文件!")
  415.             return
  416.         options = {
  417.             'onefile': self.check_onefile.isChecked(),
  418.             'noconsole': self.check_noconsole.isChecked(),
  419.             'name': self.input_name.text().strip(),
  420.             'icon': self.input_icon.text().strip()
  421.         }
  422.         self.btn_start.setEnabled(False)
  423.         self.btn_stop.setEnabled(True)
  424.         self.log_text.clear()
  425.         self.progress_bar.setRange(0, 0)
  426.         self.thread = PackThread(file_path, options, self.manual_deps)
  427.         self.thread.progress.connect(self.log_message)
  428.         self.thread.finished.connect(self.on_finished)
  429.         self.thread.start()
  430.     def stop_conversion(self):
  431.         if self.thread:
  432.             self.thread.stop_flag = True
  433.             self.log_message("正在请求停止...")
  434.             self.btn_stop.setEnabled(False)
  435.     def on_finished(self, success, message):
  436.         self.btn_start.setEnabled(True)
  437.         self.btn_stop.setEnabled(False)
  438.         self.progress_bar.setRange(0, 100)
  439.         self.progress_bar.setValue(100 if success else 0)
  440.         if success:
  441.             QMessageBox.information(self, "成功", message)
  442.         else:
  443.             QMessageBox.critical(self, "失败", message)
  444.         self.log_message("--- 任务结束 ---")
  445. if __name__ == '__main__':
  446.     app = QApplication(sys.argv)
  447.     window = Py2ExeConverter()
  448.     window.show()
  449.     sys.exit(app.exec())
 
 

点击下方按钮前往主站获取完整资源:

跳转至 wordpress 主站原文页面

文章版权声明:除非注明,否则均为AI虎哥的工具库原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
AddoilApplauseBadlaughBombCoffeeFabulousFacepalmFecesFrownHeyhaInsidiousKeepFightingNoProbPigHeadShockedSinistersmileSlapSocialSweatTolaughWatermelonWittyWowYeahYellowdog
验证码
评论列表 (暂无评论,4人围观)

还没有评论,来说两句吧...

目录[+]

取消
微信二维码
微信二维码
支付宝二维码