编程环境 | PyCharm |
---|---|
用到的第三方库 | PyInstaller |
用到的软件 | NSIS |
PyInstaller部分
-
首先确保自己编写好的Python程序无中文路径(我的程序使用了PyQt5和PyQtGraph制作了界面);
-
通过
pip install PyInstaller
或直接通过PyCharm界面的File->Settings->Project->Python Interpreter
,点击‘+’进行第三方库安装; -
在PyCharm界面的Terminal上(一般在界面的最下方,如下图所示)
导航到需要生成exe的.py文件路径下
-
执行命令
PyInstaller -D -w example.py(这里换成你自己程序的名字)
,等待3min左右,就会在example.py
所在的路径生成两个文件夹:bulid
和dist
,以及一个配置文件spec
。其中我们程序生成的.exe文件就在dist
文件夹下。
讲解一下上面命令中参数的含义,方便大家根据需要使用:
- -D:表示打包后生成一个文件夹,文件夹中包含我们的.exe,这样做的好处是,生成的可执行文件运行速度更快;它可以替换为-F,表示打包后生成一个纯.exe文件,其体积较大,运行速度较慢,但是比前面的方法更整洁。
- -w: 表示生成的.exe不显示控制台(就是类似cmd出来的那个窗口),只显示我们自己做的界面,但是如果你的python程序没有界面,那就什么都不会显示了;它可以替换为-c,表示生成的.exe显示控制台界面和自己的ui,我个人建议,在第一次运行程序的时候,使用
PyInstaller -D -c example.py(这里换成你自己程序的名字)
,这样可以看到界面运行时出现错误的原因,等全部排查后,再重新生成一个用户使用的界面时,使用PyInstaller -D -w example.py(这里换成你自己程序的名字)
到这里,PyInstaller部分完成,如果只是自己用的话,看到这里就OK了。接下来使用NSIS对这个exe进行二次打包,效果就是,打包后把一个文件夹变成一个纯.exe,双击后就是常见的安装向导了。
NSIS部分
- 下载NSIS:NSIS官网下载链接
- 将刚才使用PyInstaller打包后生成的dist目录下的目录(就是你的python程序名)进行压缩,生成ZIP文件;
- 点击下载的NSIS.exe,通过安装向导来安装;安装成功后出现一个名为NSIS的文件夹,进入后点击NSIS.exe
选择下面的命令
然后将你的ZIP文件导入,其他选项都不变
等待Extracted开始统计文件夹中文件数目;结束后Generate按键变亮,点击后,等待5min左右,就可以在dist文件夹下得到打包后的.exe向导文件了。
到这里,NSIS部分完成。
排雷
在我使用这两个工具打包的时候,没有想象中一帆风顺,顺利的就完成了毕业设计最后一步,由于自己毕设的特殊性,遇到了下面的问题,重复打包了N次后才成功。
- Python程序中涉及到文件路径导航时:由于我设计的界面需要显示本地文件夹下其他Python程序的名称,我通过PyInstaller打包后,把这些程序所在的文件夹也移到了这个打包后的文件夹中,但是运行.exe时,界面却没有显示这些名称。
解决方法: 1.首先排查了源代码,发现使用了绝对路径
os.path.abspath
,把所有的绝对路径都删掉了,并将路径冻结,整体修改方法如下:
首先在if __name__ == '__main__':
下面添加
if getattr(sys, 'frozen', False):
path = os.path.dirname(sys.executable)
elif __file__:
path = os.path.dirname(__file__)
对路径进行冻结,此时path获得的就是.exe所在的路径;
其次,把程序中的
file_path = os.path.abspath(os.path.join(path1, question_file))
os.path.abspath
都删掉,使用相对路径;此时重新进行打包后,生成的.exe就可以显示程序名称了;
- 虽然GUI上显示了Python程序的名称,但是我希望它可以在GUI上运行,之前在PyCharm中实现时,使用
subprocess
模块,具体的代码如下:
subprocess.Popen([sys.executable, fn])
其中fn
是Python程序的路径(上面修改为了相对路径导航);但是在.exe中点击运行按钮后,没有弹出这个程序运行的界面,而是又弹出了一个相同的GUI。
仔细分析后,我认为问题一定出现在sys.executable
这个参数上。果不其然,在PyCharm中打印了这个参数的地址,发现它指向了本地的python.exe文件;因此猜想,在.exe中,这个参数指向的是我们生成的.exe,所以又运行了一次这个GUI。
但是我没找到特别好的办法,只是把本地的Python.exe
文件所在文件夹整个移到了我们生成的.exe的文件夹下,然后把源代码中sys.executable
参数修改为这个Python.exe
的路径(相对路径)。再次运行PyInstaller -D -c example.py(这里换成你自己程序的名字)
命令,把本地的Python.exe
文件所在文件夹整个再移过去,运行,结果!闪现了一个黑乎乎的窗口,失败/(ㄒoㄒ)/~~
我又尝试运行了一下这个移动过来的Python.exe
,结果发现,它本身就是闪现的窗口(我也不知道为什么),但是意外发现,文件夹下的ipython.exe
文件可以正常运行。
反正功能相同,我就试着把源代码中sys.executable
参数再次指向ipython.exe
,**再次,再次打包!**结果,又一次闪现黑窗口/(ㄒoㄒ)/~~
再次排查问题,找到了一个大佬的帖子帖子链接,发现这个subprocess.Popen
中的参数,可以进行调整。
shell=True 可以防止执行subprocess.Popen()时闪现一个黑糊糊的dos窗口。
打包是关闭了命令行窗口,stdin, stdout 无处安放,应该把它们用subprocess.PIPE 管道代替。
最后修改代码如下:
subprocess.Popen([path_python, fn], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
其中path_python
就是指向移到生成文件夹的ipython.exe
的相对路径,fn是要执行的python程序的相对路径。
再次PyInstaller后,运行.exe,成功通过子线程运行了另一个Python程序!
- 使用NSIS打包后,我尝试换了一个盘运行这个软件,结果发现,GUI只是出现不到2s,又退出了/(ㄒoㄒ)/~~
还好之前生成.exe时,设置了控制台可以显示,在GUI闪现的时候,立马按下PrintScreen
按键,看到了出错的原因:
这个问题,我也百度到了答案,这里放一下链接。
除此之外,大家应该可以看到,我的路径包含了中文,这也预示着我又得多打包一次/(ㄒoㄒ)/~~。排查了这个问题之后(不要让路径包含中文),再走一遍流程,终于成功做完毕设!