pyinstaller将.py文件转成可执行文件

对于.py代码里引用的功能模块,pyinstaller一般能帮忙分析出来,并打包进可执行文件夹,但对于代码中引用的数据文件则需要特别处理一下。

  • 数据文件运行时找不到

weatherPM.py里需要访问city.json文件,获取城市ID。在程序中对city.json引用的语句如下:

……
city_list_location = '/Users/PythonABC/Documents/python/city.json'
……

def get_city_ID(city_name): 

    with open(city_list_location, encoding='utf-8') as city_file:
……

打包时得把city.json打包进来,将数据文件打包进来pyinstaller用参数—-add-data。假设city.json已经提前拷贝到代码所在文件夹下,那么参数—-add-data的作用就是把city.json拷贝一份到指定目标文件夹下。命令如下(后面会详细解释):

   pyinstaller --add-data './city.json:.' weatherPM.py

打包成功,运行也没问题!就是……没有通用性不能发给其他用户用。如果发给其他用户使用,其他用户自己的电脑上必须有一个/Users/PythonABC/Documents/python/city.json,否则运行发过来的可执行文件weatherPM(Mac)/weatherPM.exe(Windows)就会出现错误:

FileNotFoundError: [Errno 2] No such file or directory: '/Users/PythonABC/Documents/python/city.json'

这是因为在weatherPM.py里打开city.json文件读取数据时用的是绝对路径,显然必须在另一台机器上存在这个路径这个文件的条件很苛刻。

那么如果把程序中的绝对路径改成相对路径呢?

……
city_list_location = './city.json'
……

也不可以!原因在于用pyinstaller打包时’.’解释成代码所在的当前路径,city.json就在这个目录下。打包成功后city.json虽然被拷贝到可执行文件weatherPM(Mac)或weatherPM.exe(Windows)所在的文件夹,但运行weatherPM/weatherPM.exe时程序却不认为当前路径是自己所在的文件夹。用相对路径这种写法pyinstaller打包完,直接在本机上运行可执行文件都会出现找不到city.json文件的错误:FileNotFoundError: [Errno 2] No such file or directory: './city.json'。

  • 代码中用到的数据文件的路径改成不用’.’的相对路径

在代码里把city.json的路径改成不用’.’的相对路径可以解决这个问题:

  1. 引进sys模块,argv[0]是正在运行的代码文件的绝对路径+文件名。
  2. 引进第三方模块pathlib,用argv[0]生成路径对象Path(sys.argv[0])
  3. 用路径对象的属性parent,得到正在运行的代码文件所在的绝对路径Path(sys.argv[0]).parent。为什么要用.parent?是因为argv[0]得到的是可执行文件的绝对路径,city.json此时跟可执行文件在同一个文件夹下,Path(sys.argv[0]).parent指向的就是这个文件夹。
  4. 使路径对象指向json,Path(sys.argv[0]).parent.joinpath(‘city.json’)

修改weatherPM.py里关于city.json位置有关的语句(city_list_location = ‘./city.json’):

……
import sys
from pathlib import Path
……

city_list_location = Path(sys.argv[0]).parent.joinpath('city.json')
# 这句原来是city_list_location = ‘./city.json’
  • 打包数据文件时指明当前和打包后的存放位置

pyinstaller用参数—-add-data打包weatherPM.py时不仅要指定city.json当前的存放位置,还要指定打包后city.json的存放位置,源存放位置和目标存放位置用冒号分隔。city.json打包前跟weatherPM.py放在一起,路径就是当前文件夹下的city.json。在weatherPM的打包命令里,指明city.json的源存放位置可以用city.json的绝对路径;也可以先进入到weatherPM.py和city.json所在路径,然后用./city.json。打包后的文件夹是可执行文件夹,即dist下的weatherPM文件夹,带--add-data参数的pyinstaller命令把city.json拷贝到weatherPM文件夹下。在打包命令中目标文件夹的位置用’.’来指代weatherPM文件夹。

打开命令窗口上先进入weatherPM.py所在的文件夹,而后执行:

   pyinstaller --add-data './city.json:.' weatherPM.py

参数--add-data后跟的字符串'./city.json:.'用‘:’分开,‘:’前放带路径的数据文件city.json。因为之前已经进入到weatherPM.py文件夹下了,所以用’./city.json’。‘:’右边的目标文件夹的位置用‘.’,这个’.’指的是打包时生成的dist下的文件夹weatherPM。文件夹weatherPM里存放可执行文件weatherPM(Mac)或weatherPM.exe(Windows)以及运行时所需的辅助文件。打包完成后,city.json也会被自动拷贝一份到这个weatherPM文件夹下的。

运行weatherPM文件夹下的可执行文件weatherPM(Mac)或weatherPM.exe(Windows)时,代码里的修改部分Path(sys.argv[0]).parent指的就是可执行文件所在的路径,此时city.json就在这个路径里。所以从city_list_location = Path(sys.argv[0]).parent.joinpath('city.json')的这个city_list_location可以找得到city.json文件,不会再出现找不到文件的错误。

  • 打包成一个可执行文件要手工拷贝数据文件

如果不是打包成一个可执行文件夹,而是要打包成一个可执行文件:

            pyinstaller -F weatherPM.py

这种情况下参数--add-data是没有办法把city.json打包进可执行文件的,只能手动拷贝。在dist文件夹下生成可执行文件weatherPM(Mac)或weatherPM.exe(Windows)后,再手动拷贝一份city.json到dist文件夹下。