当前位置:   article > 正文

pyinstaller打包exe(单文件 + 多文件)_pyinstaller打包多文件

pyinstaller打包多文件


看到几篇大佬整理的的文章(收费),仅作为个人收藏。
(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

3.1、通用选项

序号选项说明参数
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 WORKPATHWORKPATH:指定临时文件的保存路径(默认:./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 )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

3.2、生成选项(exe)

3.2.1、pyinstaller -F my_script.py(常用) —— 打包单个 / 多个文件

方式:	-F 或 --onefile

作用:	将(单个)源文件打包为单个可执行文件。
命令:	pyinstaller -F my_script.py
参数:	my_script.py	待打包的 Python 脚本文件

"""
缺点:	由于包含了所有必需文件和模块,所以可执行文件占用的内存更大。
优点:	可执行文件更加独立,且无需担心缺少依赖项和配置环境。
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上下两个方法(效果一致):都是将所有py源文件和依赖的库文件全部打包到一个独立的可执行文件中。

方式:	-F 或 --onefile

作用:	将(多个)源文件打包为单个可执行文件。
命令:	pyinstaller -F my_script1.py my_script2.py
参数:	
		my_script1.py	待打包的 Python 脚本文件(1.py)
		my_script2.py	待打包的 Python 脚本文件(2.py)


"""
缺点:	由于包含了所有必需文件和模块,所以可执行文件占用的内存更大。
优点:	可执行文件更加独立,且无需担心缺少依赖项和配置环境。
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.2.2、pyinstaller -D file_path my_script.py —— 打包多个文件(不能独立运行)

降低可执行文件占用的内存,但不能独立运行,可移植性差。

方式:	-D 或 --onedir

作用:	设置可执行文件的数据文件搜索路径。
命令:	pyinstaller -D /path/to/data/files my_script.py
参数:	/path/to/data/files		数据文件所在的目录 
		my_script.py 			待打包的 Python 脚本文件

"""
详解:	(1)将可执行文件所需要的数据文件全部保存在指定路径下,且不打包到可执行文件中。
详解:	(2)在可执行文件运行时,系统将自动加载指定路径下的文件。

优点:	降低可执行文件占用的内存,并使数据文件可以更容易地进行更新或替换。
缺点:	不能独立运行,可移植性差。
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3.2.3、pyinstaller -F --add-data < SRC;DEST or SRC:DEST > my_script.py —— 打包多个文件或目录(不能独立运行)

实践结果:(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 目录中。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.2.4、pyinstaller -d your_script.py —— 生成一个调试版本的可执行文件

方式:	-d 或 --debug

作用:	在生成可执行文件时启用调试信息。
命令:	pyinstaller -d your_script.py
参数:	my_script.py	待打包的 Python 脚本文件

"""
优点:	调试版本的可执行文件,包含更多信息,以便在出现问题时进行故障排除。
缺点:	内存可能会更大,且可能会影响可执行文件的性能。

应用:	(1)在开发过程中,可以使用 -d 参数。
		(2)在应用程序时,不建议使用 -d 参数。
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.2.5、pyinstaller -F -i my_icon.ico your_script.py —— 指定可执行文件的图标

方式:	-i 或 --icon

作用:	指定一个图标文件作为可执行文件的图标。
命令:	pyinstaller -F -i my_icon.ico your_script.py
参数:	my_icon.ico	 	图标文件(支持格式:ICO、PNG、BMP、JPEG、GIF、SVG)
		my_script.py	待打包的 Python 脚本文件

"""
详解:	图标用于显示在可执行文件的文件夹和窗口上,以提供一个自定义的外观。
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

四、必读信息

【Python虚拟环境】创建 + 激活 + 查看 + 退出 + 复制 + 删除

在cmd命令窗口中(在pycharm-Terminal中):

  • (1)待打包的 .py 文件必须满足其所需要的配置环境(如:在虚拟环境py39下,执行pyinstaller),如此打包后的.exe才可以正常运行,否则闪退。
  • (2)无论是否满足配置环境,都会打包成功。

详见下图:.py 文件所需的配置环境安装在虚拟环境 py39 中。
在这里插入图片描述

五、项目实战

5.1、单个py文件打包exe:-F

5.1.1、详细步骤

(1)cmd切换到.py路径下

在这里插入图片描述

(2)开始打包

在这里插入图片描述

(3)查看结果

在这里插入图片描述

5.1.2、测试源码

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_())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

5.2、多个py文件打包exe:-F

5.2.1、打包多个文件:py(需保存在相同路径下) —— exe独立运行

5.2.1.1、详细步骤

若有多个py文件:

  • 方式一:只需要打包py主文件即可;
  • 方式二:若同时打包多个py文件,也可以完成;

方式一:打包主文件
在这里插入图片描述

方式二:打包主文件 + 子文件
在这里插入图片描述

5.2.1.2、测试源码
"""
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_())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
"""
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_()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

5.2.2、打包多个文件:py + txt + xls等(需保存在相同路径下) —— exe运行依赖非py文件

5.2.2.1、详细步骤

在这里插入图片描述

在这里插入图片描述

5.2.2.2、测试源码
"""
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_())


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
"""
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_()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

参考博客

  1. PyInstaller下载、安装、使用
  2. Pyinstaller详解命令行参数1
  3. Pyinstaller详解命令行参数2
  4. pyinstaller参数介绍以及总结
  5. pyinstaller打包exe(详细教程):单个文件 + 多个文件
  6. 用python打包exe应用程序-PyInstaller
  7. pyinstaller打包exe(详细教程)
  8. 如何将写好的Python代码打包成exe文件?
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/647311
推荐阅读
相关标签
  

闽ICP备14008679号