赞
踩
看到几篇大佬整理的的文章(收费),仅作为个人收藏。
(1)《 PyInstaller 打包实战指南》
(2)《 Nuitka 打包实战指南》
(3)比较 PyInstaller 和 Nuitka
官网手册:PyInstaller Manual
PyInstaller
:将一个或多个 Python 脚本(.py)打包成独立可执行文件(.exe)的工具,用户不需要安装 Python 解释器和脚本所需的所有依赖项,即可运行。
生成的可执行文件是自包含的:包括Python解释器和所有依赖项,用户不需要安装额外的软件和环境。
支持多种Python版本:支持Python 2.7、Python 3.x
支持多种打包选项:
- (1)生成单个可执行文件
- (2)生成文件夹分发(打包成一个包含多个文件和文件夹的目录,而不是一个单独的可执行文件)等。
支持跨平台:支持Windows、Linux和macOS等多个操作系统,可以在不同平台上生成可执行文件。
依赖管理:可以自动识别和处理Python脚本中的依赖项,包括标准库和第三方库。
插件支持:支持插件,可以扩展其功能。例如:添加自定义构建步骤或处理特定类型的文件。
控制选项:可以通过命令行选项或配置文件来自定义打包过程,以满足特定需求。
python包安装指南
:在 pypi官网 搜索需要的安装包,然后在终端 cd 到保存文件的路径下,输入命令pip install
安装。
- 在线安装(推荐):
pip install pyinstaller
- 离线安装:
- 下载方法
(1):PYPI - pyinstaller 下载
(2):Links for pyinstaller 下载- 安装方法
(1)轮子安装(.whl):DOS命令行窗口下 cd 到 .whl 文件路径下:pip install .whl
(2)离线包安装(tar.gz):
- (1)解压
- (2)DOS命令行窗口下 cd 到 setup.py 文件路径下
- (3)编译:
python setup.py build
确保python命令已经在搜索目录中。- (4)安装:
python setup.py install
【更多请看官网】https://pyinstaller.org/en/stable/man/pyinstaller.html
序号 | 选项 | 说明 | 参数 |
---|---|---|---|
1 | -h 或 --help | 显示帮助消息并退出 | |
2 | -v 或 --version | 显示程序版本信息并退出 | |
3 | -n NAME 或 --name NAME | 指定 .exe 和 .spec 文件的名称(默认:与.py等同) | NAME:表示自定义名称 |
4 | --specpath DIR | 指定 .spec 文件的保存路径(默认:当前目录) | DIR:表示自定义路径 |
5 | --distpath DIR | 指定 .exe 文件的保存路径(默认:./dist) | DIR:表示自定义路径 |
6 | --workpath WORKPATH | WORKPATH:指定临时文件的保存路径(默认:./build) | WORKPATH:表示自定义路径 |
.spec文件
:用于详细描述构建可执行文件过程中的各种选项和配置。由 PyInstaller 自动生成的配置文件
spec文件结构: ############################################################ # -*- mode: python ; coding: utf-8 -*- block_cipher = None a = Analysis(['my_script.py'], pathex=['/path/to/your/script'], binaries=[], datas=[('data.txt', '.')], # 添加数据文件 hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='my_script', debug=False, bootloader_ignore_signals=False, bootloader_ignore_ssl=True, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True )
方式: -F 或 --onefile
作用: 将(单个)源文件打包为单个可执行文件。
命令: pyinstaller -F my_script.py
参数: my_script.py 待打包的 Python 脚本文件
"""
缺点: 由于包含了所有必需文件和模块,所以可执行文件占用的内存更大。
优点: 可执行文件更加独立,且无需担心缺少依赖项和配置环境。
"""
上下两个方法(效果一致):都是将所有py源文件和依赖的库文件全部打包到一个独立的可执行文件中。
方式: -F 或 --onefile
作用: 将(多个)源文件打包为单个可执行文件。
命令: pyinstaller -F my_script1.py my_script2.py
参数:
my_script1.py 待打包的 Python 脚本文件(1.py)
my_script2.py 待打包的 Python 脚本文件(2.py)
"""
缺点: 由于包含了所有必需文件和模块,所以可执行文件占用的内存更大。
优点: 可执行文件更加独立,且无需担心缺少依赖项和配置环境。
"""
降低可执行文件占用的内存,但不能独立运行,可移植性差。
方式: -D 或 --onedir
作用: 设置可执行文件的数据文件搜索路径。
命令: pyinstaller -D /path/to/data/files my_script.py
参数: /path/to/data/files 数据文件所在的目录
my_script.py 待打包的 Python 脚本文件
"""
详解: (1)将可执行文件所需要的数据文件全部保存在指定路径下,且不打包到可执行文件中。
详解: (2)在可执行文件运行时,系统将自动加载指定路径下的文件。
优点: 降低可执行文件占用的内存,并使数据文件可以更容易地进行更新或替换。
缺点: 不能独立运行,可移植性差。
"""
实践结果:(1)打包后的.exe不是独立可执行文件(2)在运行时,需要将非.py文件保存到.exe相同路径下。
方式: -D 或 --onedir 作用: 将文件或目录添加到生成的可执行文件中。 命令: pyinstaller -F --add-data <SRC;DEST or SRC:DEST> my_script.py 参数: SRC 源文件或目录的路径 DEST 目标路径,文件或目录将被复制到生成的可执行文件中的目录 my_script.py 待打包的 Python 脚本文件 """ 详解: (1)将指定的文件或目录包含在可执行文件中,这样可执行文件就可以在没有外部文件依赖的情况下运行。 详解: (2)对于创建独立的可执行文件非常有用。 """ (用法一)将(单个或多个文件)添加到可执行文件中 举例:pyinstaller --onefile --add-data "data.txt;." --add-data "image.jpg;." main.py 参数:将 data.txt + image.jpg 复制到生成的可执行文件的根目录中。 (用法二)将(一个目录)添加到可执行文件中 举例:pyinstaller --onefile --add-data "mydatafolder;data" main.py 参数:将 mydatafolder 目录中的所有文件和子目录都复制到生成的可执行文件的 data 目录中。
方式: -d 或 --debug
作用: 在生成可执行文件时启用调试信息。
命令: pyinstaller -d your_script.py
参数: my_script.py 待打包的 Python 脚本文件
"""
优点: 调试版本的可执行文件,包含更多信息,以便在出现问题时进行故障排除。
缺点: 内存可能会更大,且可能会影响可执行文件的性能。
应用: (1)在开发过程中,可以使用 -d 参数。
(2)在应用程序时,不建议使用 -d 参数。
"""
方式: -i 或 --icon
作用: 指定一个图标文件作为可执行文件的图标。
命令: pyinstaller -F -i my_icon.ico your_script.py
参数: my_icon.ico 图标文件(支持格式:ICO、PNG、BMP、JPEG、GIF、SVG)
my_script.py 待打包的 Python 脚本文件
"""
详解: 图标用于显示在可执行文件的文件夹和窗口上,以提供一个自定义的外观。
"""
【Python虚拟环境】创建 + 激活 + 查看 + 退出 + 复制 + 删除
在cmd命令窗口中(在pycharm-Terminal中):
- (1)待打包的 .py 文件必须满足其所需要的配置环境(如:在虚拟环境py39下,执行pyinstaller),如此打包后的.exe才可以正常运行,否则闪退。
- (2)无论是否满足配置环境,都会打包成功。
详见下图:.py 文件所需的配置环境安装在虚拟环境 py39 中。
import sys from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QFileDialog, QLabel, QVBoxLayout, QWidget from PyQt5.QtGui import QPixmap class SubWindow(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle('子界面') self.setGeometry(100, 100, 400, 300) self.image_label = QLabel(self) self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout = QVBoxLayout() layout.addWidget(self.image_label) self.setLayout(layout) def show_image(self, image_path): pixmap = QPixmap(image_path) self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio)) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle('主界面') self.setGeometry(100, 100, 400, 200) self.button = QPushButton('打开图像', self) self.button.clicked.connect(self.show_sub_window) self.sub_window = SubWindow() self.setCentralWidget(self.button) def show_sub_window(self): options = QFileDialog.Options() options |= QFileDialog.ReadOnly file_path, _ = QFileDialog.getOpenFileName(self, '选择图像文件', '', 'Images (*.png *.jpg *.bmp);;All Files (*)', options=options) if file_path: self.sub_window.show_image(file_path) self.sub_window.show() if __name__ == '__main__': app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())
若有多个py文件:
- 方式一:只需要打包py主文件即可;
- 方式二:若同时打包多个py文件,也可以完成;
方式一:打包主文件
方式二:打包主文件 + 子文件
""" main.py """ import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget from sub_window import SubWindow class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Main Window") self.setGeometry(100, 100, 400, 300) central_widget = QWidget() layout = QVBoxLayout() open_sub_window_button = QPushButton("Open Sub Window") open_sub_window_button.clicked.connect(self.open_sub_window) layout.addWidget(open_sub_window_button) central_widget.setLayout(layout) self.setCentralWidget(central_widget) def open_sub_window(self): sub_window = SubWindow() sub_window.exec_() if __name__ == "__main__": app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())
""" sub_window.py """ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QFileDialog from PyQt5.QtGui import QPixmap class SubWindow(QDialog): def __init__(self): super().__init__() self.setWindowTitle("Sub Window") self.setGeometry(200, 200, 600, 400) layout = QVBoxLayout() self.image_label = QLabel(self) layout.addWidget(self.image_label) self.load_image_button = QPushButton("Load Image") self.load_image_button.clicked.connect(self.load_image) layout.addWidget(self.load_image_button) self.setLayout(layout) def load_image(self): file_dialog = QFileDialog() file_path, _ = file_dialog.getOpenFileName(self, "Open Image File", "", "Images (*.png *.jpg *.bmp *.tif)") if file_path: pixmap = QPixmap(file_path) self.image_label.setPixmap(pixmap) self.image_label.setScaledContents(True) self.image_label.adjustSize() if __name__ == "__main__": import sys from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) sub_window = SubWindow() sub_window.exec_()
""" main.py """ import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget from sub_window import SubWindow class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Main Window") self.setGeometry(100, 100, 400, 300) central_widget = QWidget() layout = QVBoxLayout() open_sub_window_button = QPushButton("Open Sub Window") open_sub_window_button.clicked.connect(self.open_sub_window) layout.addWidget(open_sub_window_button) central_widget.setLayout(layout) self.setCentralWidget(central_widget) def open_sub_window(self): sub_window = SubWindow() sub_window.exec_() if __name__ == "__main__": app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())
""" sub_window.py """ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QFileDialog, QTextBrowser from PyQt5.QtGui import QPixmap class SubWindow(QDialog): def __init__(self): super().__init__() self.setWindowTitle("Sub Window") self.setGeometry(200, 200, 600, 400) layout = QVBoxLayout() self.image_label = QLabel(self) layout.addWidget(self.image_label) self.load_image_button = QPushButton("Load Image") self.load_image_button.clicked.connect(self.load_image) layout.addWidget(self.load_image_button) # Add a button to load "image.jpg" and "data.txt" self.load_data_button = QPushButton("Load Data") self.load_data_button.clicked.connect(self.load_data) layout.addWidget(self.load_data_button) self.text_browser = QTextBrowser(self) layout.addWidget(self.text_browser) self.setLayout(layout) def load_image(self): file_dialog = QFileDialog() file_path, _ = file_dialog.getOpenFileName(self, "Open Image File", "", "Images (*.png *.jpg *.bmp *.tif)") if file_path: pixmap = QPixmap(file_path) self.image_label.setPixmap(pixmap) self.image_label.setScaledContents(True) self.image_label.adjustSize() def load_data(self): # Load and display "image.jpg" pixmap = QPixmap("image.jpg") self.image_label.setPixmap(pixmap) self.image_label.setScaledContents(True) self.image_label.adjustSize() # Load and display the content of "data.txt" try: with open("data.txt", "r") as file: data = file.read() self.text_browser.setPlainText(data) except FileNotFoundError: self.text_browser.setPlainText("data.txt not found") if __name__ == "__main__": import sys from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) sub_window = SubWindow() sub_window.exec_()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。