matplotlib - Matplotlib“无法确定主目录”问题在 MSYS2/MinGW 中使用 PyInstaller
问题描述
这比帖子的标题可能暗示的要复杂一些。问题源于我有一个用 C 语言编写的大型 GTK 应用程序,它生成了一个 Python matplotlib 进程,该进程使用 Python GTK 库嵌入到 GTK 窗口中。matplotlib 脚本使用 PyInstaller 编译成可执行文件。在 Linux 环境(Ubuntu 16.04/18.04)中一切正常,但在尝试使用 MSYS2/MinGW 为 Windows 编译应用程序时情况并非如此。当我在 Windows 中编译应用程序时,绘图失败并出现以下错误:
Traceback (most recent call last):
File "plotter.py", line 6, in <module>
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
File "PyInstaller/loader/pyimod03_importers.py", line 531, in exec_module
File "matplotlib/__init__.py", line 921, in <module>
File "matplotlib/__init__.py", line 602, in matplotlib_fname
File "matplotlib/__init__.py", line 599, in gen_candidates
File "matplotlib/__init__.py", line 239, in wrapper
File "matplotlib/__init__.py", line 502, in get_configdir
File "matplotlib/__init__.py", line 444, in _get_xdg_config_dir
File "pathlib.py", line 1104, in home
File "pathlib.py", line 267, in gethomedir
RuntimeError: Can't determine home directory
它来自脚本中matplotlib的导入。我不确定这是否是一个简单的 PATH 问题,或者是否还有其他一些微妙之处源于混合使用 MSYS 和 MinGW 来编译所有内容。
我创建了一个最小的工作示例,它从我的大型 GTK 应用程序中复制了问题。由于这需要一些 DLL 和 makefile 才能运行,而不是简单地在此处发布代码,我创建了一个带有 README 的 GitHub 存储库,其中介绍了需要做什么来显示问题以及我做了什么来解决问题。您可以使用以下方法克隆它:
git clone https://github.com/LeighKorbel/pyinstaller_msys2_issue.git
问题在于我不确定究竟是什么解决了这个问题,无论发生了什么,我都无法在我的实际 GTK 程序中重现相同的解决方案。自述文件中概述了我的问题,因此我将在此处发布以更加清楚:
(1) Download and install MSYS2 from "https://www.msys2.org/".
(2) This will install both MSYS2 and MinGW to the msys2 directory on your local disk. Go into the msys2 folder and
open a MinGW64 shell as administrator.
(3) In the MinGW shell, type in the following commands to install the required packages using the Pacman package manager:
pacman -Syu (NOTE: You will have to perform this call and close the shell several times for it to complete!)
pacman -S mingw-w64-x86_64-python-pip
pacman -S mingw-w64-x86_64-python-matplotlib
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-gtk3 mingw-w64-x86_64-python3 mingw-w64-x86_64-python3-gobject
pacman -S git make nano pkg-config gcc
(4) Clone the GitHub repository with the example showing the error in question:
mkdir testing
cd testing
git clone https://github.com/LeighKorbel/pyinstaller_msys2_issue.git
cd pyinstaller_msys2_issue
(5) Download PyInstaller using Python's pip package manager:
pip install pyinstaller
(6) Now we are ready to start compiling. The first thing we need to do is run PyInstaller on the python script. This
script is just a simple matplotlib plot that is embedded within a GTK window. Run the following commands:
cd gtk_simple_plot
pyinstaller plot.py
(7) After PyInstaller finishes, an executable file will now be located in "/dist/plot". This can be run from the
command line by:
./dist/plot/plot.exe
(8) Now that we have verified that this works, we want to compile a very basic GTK application in C that only has a
button that will spawn the script using GLib's g_spawn() function. However, this needs to be done in a MSYS2 shell
because MinGW does not have the system headers needed in it. So you need to open an MSYS2 shell now as an administrator.
After this is done, we will need to set an environment variable for pkg_config, which is used by GTK. I would
recommend adding the following line to the .bashrc file and then closing and reopening the MSYS2 shell, but it is
not required (you will just need to remember to export again in the case that you close and reopen the shell). Once
the MSYS2 shell is open type in:
nano .bashrc
(9) Scroll to the bottom of the file and type in the following:
export PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:$PKG_CONFIG_PATH"
(10) Now exit and save by pressing "Ctrl-x", then "y", then "enter". Close and reopen the shell. Now enter the following commands:
cd testing/call_gtk_plot_from_C
make
(11) The GTK program will now be compiled and can be executed by running:
./launch.exe
(12) Press the button on the UI to launch the PyInstaller compiled matplotlib script. The following error should
appear in the MSYS2 shell:
Traceback (most recent call last):
File "plotter.py", line 6, in <module>
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
File "PyInstaller/loader/pyimod03_importers.py", line 531, in exec_module
File "matplotlib/__init__.py", line 921, in <module>
File "matplotlib/__init__.py", line 602, in matplotlib_fname
File "matplotlib/__init__.py", line 599, in gen_candidates
File "matplotlib/__init__.py", line 239, in wrapper
File "matplotlib/__init__.py", line 502, in get_configdir
File "matplotlib/__init__.py", line 444, in _get_xdg_config_dir
File "pathlib.py", line 1104, in home
File "pathlib.py", line 267, in gethomedir
RuntimeError: Can't determine home directory
********************************************************************************************************************
QUESTION 1: WHAT IS THE CAUSE OF THIS ERROR? IS THIS FROM THE DUAL USE OF MinGW FOR PYINSTALLER AND MSYS2 FOR MAKE?
IS IT A PATH ISSUE?
********************************************************************************************************************
(13) This error can be eliminated by performing the following: In the "call_gtk_plot_from_C" folder, edit both the
makefile and the launch.c code (in src/) to remove all instances of "../gtk_simple_plot/" and then copy and move
the plot.py script from the gtk_simple_plot directory into the call_gtk_plot_from_C directory. From a MinGW shell
run make.
NOW IT WILL MAKE EVERYTHING, EVEN THE C CODE.
(14) Run the program:
./launch.exe
When you run this time it errors out with a gdk_pixbuf error. For some reason, in /dist/plot/lib/gdk-pixbuf there
is no longer the folder "loaders" which does exist on the previous run of PyInstaller. Copy it from the dist/
folder in gtk_simple_plot over to the dist/ folder in call_gtk_plot_from_C. Run the program again.
NOW IT WORKS WITHOUT THE PREVIOUS HOME DIRECTORY ERROR.
********************************************************************************************************************
QUESTION 2: WHAT CAUSED IT TO WORK NOW? WAS IT THE FULL COMPILE IN MinGW? IS THERE A PATH THAT IS DIFFERENT NOW WITH
THE PYTHON SCRIPT BEING MOVED INTO THE NEW DIRECTORY? WHY WAS THE PIXBUF MISSING?
********************************************************************************************************************
********************************************************************************************************************
QUESTION 3: WHY DOES THE C CODE COMPILE IN MinGW NOW? IT DOES NOT CONTAIN THE SYSTEM HEADERS REQUIRED FOR IT COMPILE?
********************************************************************************************************************
您可以看到,我概述的三个问题对我来说仍然很神秘。如果有人可以帮助我解决其中任何一个问题,将不胜感激。
解决方案
问题是在不同的环境中使用 gcc。这个问题有点复杂,因为我有一个用 C 编写的 GTK 程序(依赖于 POSIX 系统头文件,所以需要 MSYS2),但是这个程序正在使用 PyInstaller 生成一个冻结 Python 脚本的子进程(它也使用 GTK将 matplotlib 图嵌入 GTK 窗口)。PyGobject 库(包含 GLib 和 GTK)需要使用 MinGW shell 才能编译它。所以一方面我有一些需要 MSYS2 的东西,另一方面我有一些需要 MinGW 的东西。
所以问题1的答案是:
它是由同时使用 MSYS2 和 MinGW 引起的,但真正的原因是使用 MSYS2 gcc 编译 C 代码和 MinGW gcc 在 Python 脚本上运行 PyInstaller。这个问题确实是一个 PATH 问题。
这是因为 Python 脚本继承了 MinGW 的环境变量,而 C GTK 代码继承了 MSYS2 的环境变量。当 Python 脚本无法确定主目录时,这是因为它们根本不存在,因此当 C 代码中的 g_spawn() 发生时,python 脚本会出现 HOMEPATH 错误。
问题2的答案是:
它现在可以工作,因为一切都是在 MinGW 环境中编译的一个简单事实,所以现在 C 代码缺少它以前没有的环境变量。
问题 3 的答案是:
C 代码在 MinGW 中正常编译。这最终成为一个糟糕的最小示例,因为它不依赖于任何系统头文件。在我的实际代码中,情况并非如此。我必须确保一切都在 MinGW 中编译,这需要使用 MSYS2 gcc 进行编译,以便获得所需的系统头文件并能够使用 PyGobject 编译 python 脚本。为了让它工作,我需要使用 g_setenv() 和 g_get_home_dir() 在 C 代码中设置环境变量。这然后允许 g_spawn() 拥有它以前缺少的环境变量。
推荐阅读
- spring-boot - Feign Retry:没有找到能够从 [java.lang.String] 类型转换为 [java.lang.Class] 类型的转换器
] - c# - 在 ASP.NET MVC 方法中创建一个具有以下功能的应用程序:
- html - 如何在正确的内容页面上打开图像
- ubuntu - ErrorException 为 foreach() 提供的参数无效 - LAMP 堆栈
- python - 将每一行乘以其他行并求和
- c# - 如何设置一个while循环?
- csv - 我想在 JMeter 中保存带有汇总报告的 CSV。我需要将此文件保存在 JMX 文件所在的本地存储库中
- cypress - 赛普拉斯检查控制台中记录的内容
- java - 如何触发 KeygaurdManager 以编程方式获取指纹
- spring - 使用 Spring Boot 纱线连接到带有后缀的资源管理器