From df8d00d719d801820ccf4455b20485c915b3545a Mon Sep 17 00:00:00 2001 From: zimolab Date: Sat, 19 Oct 2024 21:27:35 +0800 Subject: [PATCH] docs: improve docs --- mk-docs/assets/packaging_10.png | Bin 7159 -> 31393 bytes mk-docs/packaging.md | 418 ++++++++++++++++++++++++++++++++ 2 files changed, 418 insertions(+) diff --git a/mk-docs/assets/packaging_10.png b/mk-docs/assets/packaging_10.png index cf815a01e2505c48e8109e9f106dc5705ceb8150..3823b3156f66d1af7678147f10cc8a00592db7cf 100644 GIT binary patch literal 31393 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 literal 7159 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.8,<4.0) is not compatible with some of the required packages Python requirement: + - pyinstaller requires Python <3.14,>=3.8, so it will not be satisfied for Python >=3.14,<4.0 + +Because no versions of pyinstaller match >6.10.0,<7.0.0 + and pyinstaller (6.10.0) requires Python <3.14,>=3.8, pyinstaller is forbidden. +So, because pyguiadapter depends on pyinstaller (^6.10.0), version solving failed. + + • Check your dependencies Python requirement: The Python requirement can be specified via the `python` or `markers` properties + + For pyinstaller, a possible solution would be to set the `python` property to ">=3.8,<3.14" + + https://python-poetry.org/docs/dependency-specification/#python-restricted-dependencies, + https://python-poetry.org/docs/dependency-specification/#using-environment-markers + https://python-poetry.org/docs/dependency-specification/#using-environment-markers +``` + +此时,需要将`pyproject.toml`文件中`python`依赖项的版本设置到正确的范围,比如: + +```toml +... +[tool.poetry.dependencies] +python = "^3.8, <3.14" +... +``` + +然后再次运行`poetry add pyinstaller --group dev`。 + +## (三)开始打包 + +首先需要进入[examples/packaging/]({{main_branch}}/examples/packaging/)目录,以下命令行均在此目录下完成。 + +```shell +cd example/packaging/ +``` + +### 1、基础命令 + +`pyinstaller`的命令语法如下: + +```bash +pyinstaller [options] script [script …] | specfile +``` + +首先,让我们尝试不加任何选项来打包我们的应用,在命令行下输入: + +```bash +pyinstaller app.py +``` + +不出意外,我们将看到类似下面的输出: + +``` +... +20490 INFO: Fixing EXE headers +20566 INFO: Building EXE from EXE-00.toc completed successfully. +20569 INFO: checking COLLECT +20571 INFO: Building COLLECT COLLECT-00.toc +20786 INFO: Building COLLECT COLLECT-00.toc completed successfully. +``` + +这表明打包已经成功了,此时,`examples/packaging/`目录下会出现两个新的目录`build`和`dist`,以及一个新的文件`app.spec`: + +- `build/`:该目录用于存放打包过程中生成的一些中间文件,一般无需过多关注。 +- `dist/`:该目录用于存放打包的结果产物,我们需要的二进制可执行文件就将被放到这个命令下 +- `app.spec`:`*.spec`文件时打包配置文件,该文件记录了打包所使用的参数,运行打包命令后将自动生成该文件。后续可以直接输入`pyinstaller app.spec`命令打包应用,而无需每次都手动输入参数。 + +
+ +
+ + +现在让我们查看`dist`目录,里面包含一个名为`app`的子目录,最终的可执行文件就在该目录下。默认情况下,`pyinstaller`会生成一个可执行文件及一个`_internal/`目录: + +
+ +
+ +`_internal/`目录下存放着这可执行文件运行时依赖的库和其他文件: + +
+ +
+ +### 2、打包资源(数据)文件 + +#### (1)第三方依赖中的资源文件 + +现在,让我们运行`app.exe`,结果发现只有一个命令行窗口一闪而过,程序没有启动成功。这说明打包还存在一些问题,并没有真正成功。让我们通过命令行再次启动`app.exe`,尝试从其命令行输出中定位问题所在。 + +```python +./dist/app/app.exe +``` + +程序输出了大段的异常回溯信息,让我们截取其中关键的部分: + +``` +Traceback (most recent call last): + ... + File "yapf_third_party\_ylib2to3\pygram.py", line 29, in + File "yapf_third_party\_ylib2to3\pgen2\driver.py", line 237, in load_grammar + File "pkgutil.py", line 637, in get_data + File "PyInstaller\loader\pyimod02_importers.py", line 509, in get_data +FileNotFoundError: [Errno 2] No such file or directory: '...\\dist\\app\\_internal\\yapf_third_party\\_ylib2to3\\Grammar.txt' +``` + +从异常信息中不难发现,一些程序运行所必须的文件没有被打包到`_internal/`目录下,具体而言,该文件被第三方包`yapf_third_party`引用。 + +`PyGUIAdapter`依赖`yapf`实现代码格式化功能,而`yapf`在内部依赖又了`yapf_third_party`。显然,目前`pyinstaller`还无法处理`yapf`这种依赖关系,无法自动收集并打包其所需的文件。为了处理类似的错误,我们可以手动指定需要收集的文件的包或模块,`pyinstanller`提供了以下选项: + +- `--hidden-import MODULENAME`或 `--hiddenimport MODULENAME`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-hidden-import) + + 用于告诉`pyInstaller`在打包过程中包含那些可能没有被自动识别为依赖的额外python包,即那些通过`importlib`等方式隐式导入的包。 + +- `--collect-submodules MODULENAME`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-collect-submodules) + + 用于收集指定包或模块的所有子模块。 + +- `--collect-data MODULENAME`或`--collect-datas MODULENAME`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-collect-data) + + 用于收集指定包或模块下的数据文件。 + +- `--collect-binaries MODULENAME`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-collect-binaries) + + 用于收集指定包或模块下的二进制文件。 + +- `--collect-all MODULENAME`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-collect-all) + + 用于指定包或模块下的所有子模块、数据文件、二进制文件。 + +现在,让我们手动指定需要收集资源文件的第三方依赖。除了`yapf_third_party`,还有`pyqcodeeditor`。 + +> [`pyqcodeeditor`]()是作者移植的一个代码编辑器控件,`PyGUIAdapter`使用了该控件。`pyqcodeeditor`内置了一些用于定义语法和高亮规则的`json`文件,`pyinstaller`还没办法自动收集这些文件,因此也需要手动收集这些文件。 + +```shell + pyinstaller app.py --collect-data yapf_third_party --collect-data pyqcodeeditor -y --clean +``` + +这里使用了两个新的选项`-y`和`--clean`。其含义和用途如下: + +- `-y` + +指定该选项后,如果存在之前生成的文件,直接进行覆盖,不会询问是否要移除它们。 + +- `--clean` + +清除缓存,并移除之前构建时产生的临时文件。 + + + +打包完成后。可以发现,`yapf_third_party`和`pyqcodeeditor`中的资源文件打包到`_internal`目录下了。 + +
+ +
+ +让我们再次运行`app.exe`,已经可以正常显示程序界面了。 + +
+ +
+**小结:**如果程序引用了一些第三方库,而`pyinstaller`目前还无法自动收集这些库或是其资源文件,开发者可以使用`--collect-xxx`选项手动包含需要的文件。 + +#### (1)项目资源文件 + +现在,我们的应用仍然存在一个小问题:当我们单击`Help`菜单下的`About`菜单项,本应弹出一个关于对话框,但是,什么都没有发生。 + +
+ +
+ +让我们从命令行输出中定位问题: + +``` +Traceback (most recent call last): + File "pyguiadapter\window.py", line 361, in _on_triggered + File "app.py", line 40, in on_action_about + File "pyguiadapter\utils\messagebox.py", line 264, in show_text_file + File "pyguiadapter\utils\io.py", line 2, in read_text_file +FileNotFoundError: [Errno 2] No such file or directory: '...\\dist\\app\\_internal\\about.html' +``` + +非常清晰的错误消息:`app.py`的第`40行`用到了一个文件`about.html`,但该文件并不存在。 + +
+ +
+ +`about.html`与`app.py`在同一目录下,`pyinstaller`在打包时却没有将`about.html`打包到`_internal`目录下,这就是问题的根源。因此,我们需要手动指定项目中哪些资源文件需要被打包到最终产物中。对此,`pyinstaller`提供了`--add-data`选项。 + +> `--add-data SOURCE:DEST`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-add-data) +> +> Additional data files or directories containing data files to be added to the application. The argument value should be in form of “source:dest_dir”, where source is the path to file (or directory) to be collected, dest_dir is the destination directory relative to the top-level application directory, and both paths are separated by a colon (:). To put a file in the top-level application directory, use . as a dest_dir. This option can be used multiple times. + +让我们添加需要打包的文件,然后再次进行打包: + +```bash +pyinstaller app.py --collect-data yapf_third_party --collect-data pyqcodeeditor --add-data "./about.html:./" -y --clean +``` + +> `--add-data "./about.html:./"`的含义是:把`当前目录(./)`下的`about.html`文件打包到`目标产物的根目录(./)`下 +> +> 而在新版本的`pyinstaller`中,最终产物的根目录就是`_internal`目录。 + +打包完成后,可以发现`about.html`已经被打包到`_internal`目录下了。 + +
+ +
+ +再次运行`app.exe`,现在终于可以正常弹出对话框了: + +
+ +
+ +#### (3)关于文件路径的一些提示 + +为了在程序打包后仍然可以访问到项目中的资源文件,需要在代码中必须非常小心的除了路径问题,尤其是采用`相对路径`时,源码状态下和打包状态下`相对路径`中**“相对”**的含义是不同的,具体而言,**“相对”的起点**不同。为了解决这个问题,一种可行的方法是,以`__file__`为起点对路径进行定位,本例中就是采用这种做法。 + +
+ +
+ +## (四)修改可执行文件名称、图标 + +### 1、修改可执行文件名 + +默认情况下,`pyinstaller`生成的二进制文件名称将与输入的源码文件名称一致。比如,在`windows`平台下,`app.py`打包后的二进制可执行文件名默认为`app.exe`。当然,`pyinstaller`允许我们指定可执行文件的名称,方法是使用`--name`选项。 + +>`-n NAME`, `--name NAME`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-n) +> +>Name to assign to the bundled app and spec file (default: first script’s basename) + +现在,让我们把程序名称修改为`Equation-Solver`。 + +首先,把之前生成的`dist`、`build`、`app.spec`文件删除。然后运行以下命令: + +```bash +pyinstaller app.py --name "Equation-Solver" --collect-data yapf_third_party --collect-data pyqcodeeditor --add-data "./about.html:./" -y --clean +``` + +打包完成后,重新生成了一个`.spec`文件,名称为`Equation-Solver.spec`。在`dist/Equation-Solver`目录下,生成了一个名为`Equation-Solver.exe`的可执行文件。 + +
+ +
+ +### 2、修改可执行文件的图标 + +`pyinstaller`生成的可执行文件具有一个默认的图标,我们可以将其替换成自己的图标,方法是使用`--icon`选项。 + +>`-i `, `--icon `[](https://pyinstaller.org/en/stable/usage.html#cmdoption-i) +> +>FILE.ico: apply the icon to a Windows executable. FILE.exe,ID: extract the icon with ID from an exe. FILE.icns: apply the icon to the .app bundle on Mac OS. If an image file is entered that isn’t in the platform format (ico on Windows, icns on Mac), PyInstaller tries to use Pillow to translate the icon into the correct format (if Pillow is installed). Use “NONE” to not apply any icon, thereby making the OS show some default (default: apply PyInstaller’s icon). This option can be used multiple times. + +首先,需要准备好图标文件(`.ico`或`.icns`格式),并将其放到`app.py`同目录下。 + +
+ +
+ +执行以下打包命令: + +```bash + pyinstaller app.py --name "Equation-Solver" --icon "./icon.ico" --collect-data yapf_third_party --collect-data pyqcodeeditor --add-data "./about.html:./" -y --clean +``` + +打包完成后,我们就得到了一个带自定义图标的可执行文件了: + +
+ +
+ +## (五)窗口模式 + +`pyinstaller`打包应用程序的时候默认使用`-c`选项,因此打开生成的可执行文件时将同时打开一个控制台窗口作为其标准io。若要隐藏该窗口,可以使用`-w`选项。 + +>`-w`, `--windowed`, `--noconsole`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-w) +> +>Windows and Mac OS X: do not provide a console window for standard i/o. On Mac OS this also triggers building a Mac OS .app bundle. On Windows this option is automatically set if the first script is a ‘.pyw’ file. This option is ignored on *NIX systems. + +```bash +pyinstaller app.py -w --name "Equation-Solver" --icon "./icon.ico" --collect-data yapf_third_party --collect-data pyqcodeeditor --add-data "./about.html:./" -y --clean +``` + +## (六)单文件模式 + +`pyinstaller`在打包时,默认会生成一个可执行文件以及一个用于存放其他文件的目录(默认为`_internal`)。`pyinstaller`也支持将所有文件打包成单个可执行文件,方法是使用`-F`选项。 + +> `-F`, `--onefile`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-F) +> +> Create a one-file bundled executable. + +首先,将`dist/`目录清空。然后执行下列命令。 + +```bash +pyinstaller app.py -F -w --name "Equation-Solver" --icon "./icon.ico" --collect-data yapf_third_party --collect-data pyqcodeeditor --add-data "./about.html:./" -y --clean +``` + +打包完成后,`dist/`目录下只有一个可执行文件,其他所有文件都被打包到这个文件中了。 + +
+ +
+ +现在只需要一个文件就可以完成程序的分发了。 + +
+ +
+ +相比多文件模式,单文件打包的优势主要包括以下两点: + +- 方便部署和分发 +- 总的体积更小 + +不足在于应用启动的速度可能会稍慢一些(运行速度不受影响),但是一般不是很明显。 + +就本示例来说,在启用单文件模式前,所有文件体积加起来有`114MB`左右,而采用单文件模式后,文件体积被压缩到`46.5MB`,可以说效果显著。 + +## (七)进一步压缩体积的方法 + +### 1、使用`--strip`选项 + +按照`pyinstaller`官方的说法,该选项可用于去除可执行文件和共享库中的符号表(在`Windows`下不推荐使用),这可能有助于减少生成文件的体积。 + +> `-s`, `--strip`[](https://pyinstaller.org/en/stable/usage.html#cmdoption-s) +> +> Apply a symbol-table strip to the executable and shared libs (not recommended for Windows) + +### 2、使用`upx` + +笔者没有实际尝试这种方法,具体的方法可以参考`pyinstaller`的官方文档:[Using Upx](https://pyinstaller.org/en/stable/usage.html#using-upx)。 + +## (八)结语 + +`pyinstaller`提供了丰富的打包选项,本文仅仅对常用的几个选项进行一些说明和演示。开发者可以进一步阅读官方文档,了解更多细节了,打包过程中遇到的绝大多数问题都可以在其中找到解决方法,以下是`pyinstaller`官方文档的地址:[PyInstaller Manual — PyInstaller 6.10.0 documentation](https://pyinstaller.org/en/stable/index.html)。 + +这里,也贴出本次演示最终的`spec`文件,开发者可以在这个文件基础上进行修改,从而快速得到符合自己项目实际的打包配置文件。 + +> [`examples/packaging/Equation-Solver.spec`]({{main_branch}}/examples/packaging/Equation-Solver.spec) + +```python +# -*- mode: python ; coding: utf-8 -*- +from PyInstaller.utils.hooks import collect_data_files + +datas = [('./about.html', './')] +datas += collect_data_files('yapf_third_party') +datas += collect_data_files('pyqcodeeditor') + + +a = Analysis( + ['app.py'], + pathex=[], + binaries=[], + datas=datas, + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='Equation-Solver', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['icon.ico'], +) +``` +