将python代码打包成一个app/exe

前言

  打包的代码通常都需要写一个简单的界面,一般用 PyQt 来写。用 PyQt 写界面的方法请戳这里:PyQt5的安装及基本配置    PyQt5教程

  python提供了几个用来打包的模块,主要有 py2app、py2exe、pyinstaller,其中第一个是用来打包来给 mac 用的,后两者是针对于 windows 系统。

关于 py2exe 和 pyinstaller 两者的比较:

  对于 pyinstaller 和 py2exe 两种把 Python 文件打包成 exe 可执行文件的方法,都有各自的优缺点。但是最终目的都是为了在没有 Python 环境下的普通  Windows 系统的电脑中可直接运行。py2exe 的使用方法基本和 py2app 一样,但是本人操作时发现在 mac 中无法用 py2exe 打包成 exe,但是可以用 pyinstaller 打包成 exe,没有尝试过是否可以在 windows 环境下用 py2app 打包成 app。pyinstaller (-F指令下)生成的 exe 文件,集成了所需要的所有资源(所以 exe 文件 相对较大),可直接拷贝到其他电脑中使用。对于 py2exe 来说,限制就比较多了,它所需要用到的外部资源都在 dist 目录下,想要在其他电脑中使用就必须把整个 dist 文件夹都拷贝过去。而且经测试在 64 位机器生成的 exe 无法在 32 位机器上打开使用。

  

py2app打包

注:py2app 方法已在 Mac 环境下测试无误,windows 环境操作时如果遇到问题请自行Google 

一、安装py2app

sudo pip install py2app

二、进入要打包的文件所在的文件夹

cd 。。。。。。。。

三、生成setup.py文件,该文件用于写打包所需要的依赖

py2applet   --make-setup  xxx.py  # xxx.py为项目的启动文件,之后生成的xxx文件就是双击执行的app文件

执行以后目录中会生成 setup.py 文件,用于写入依赖的库。

四、在 setup.py 文件中手动输入需要的依赖

  如果项目很简单,没有导入第三方库和自建模块,可以忽略此步骤。

  下面是setup.py文件的一个例子,手动输入的部分就是在 DATA_FILES 空列表里加自建模块的名字,在 OPTIONS 字典的 includes 对应的空列表中加第三方模块的名字

# python自带的库无需输入,第三方库和自己引入的自写模块需要输入
"""
This is a setup.py script generated by py2applet

Usage:
    python setup.py py2app
"""

from setuptools import setup

APP = ['start.py']
#自写模块放在DATA_FILES列表中
DATA_FILES = ['xxx1.py','xxx2.py','xxx3.py']
# 第三方库放在OPTIONS下的includes对应的列表中
OPTIONS = { 'includes': ['sip', 'PyQt5.QtCore', 'PyQt5.QtWidgets'],}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)
示例setup.py

五、生成app

# 自己开发,打包速度快。(因为本机安装了依赖库,所以可以直接运行)
python setup.py py2app -A

# 给其他没有 sdk 的电脑使用,包括 lib 库。(没有安装 sdk 的电脑使用,需要去掉 -A,将把所有的依赖全部打包。)
python setup.py py2app 

  之后会生成 build 和 dist 两个文件夹,启动文件在 dist 下,双击就可以执行。

注:如果发现有问题,在重新进行上述步骤前最好先删除 build 和 dist 两个文件夹

rm -rf build dist

py2exe

  首先声明,py2exe 在高版本的 python 环境下可能会出现不支持的情况,我在打包的时候只支持到 python3.4,不清楚目前支持到哪个 python 版本。

注:本人在 win10 下用 py2exe 打包的含有 PyQt5 写界面的程序无法正常运行,遇到的问题很多,如果程序中用到了 PyQt5,推荐选用 pyinstaller 打包

一、安装py2exe

pip install py2exe

二、进入项目目录

cd xxxxxxxxx

三、在项目根目录中自行创建setup.py文件

  该文件的作用与 py2app 的 setup.py 文件一样,只不过需要自己手动创建,区别在于你可以任意命名该文件(如 woshinibaba.py)

四、在 setup.py(woshinibaba.py) 文件中写入需要的依赖

文件中基本格式为

# -*- coding: utf-8 -*-

from distutils.core import setup
import py2exe

setup(
    # console和windows分别代表控制台和图形界面,按需求选择
    #console = [{"script" : 'comtrade.py'}], 
    windows = [{"script":"comtrade.py", "icon_resources": [(1, "logo.ico")]} ],
    name = 'comtrade',# 生成的exe文件名
    version = '1.0', 
    options={}, # 括号内填入的为项目所需的依赖库和会造成报错的文件
    data_files={})# 括号内输入的为项目所需的依赖文件
# version ,description,name不是必须要写的。

其他参数:

  • dist_dir           打包生成的文件放在 dist 下,可设置存放目录(一般没有特殊要求,可以不需修改。可使用相对路径)
  • Compressed    默认为 0,1 为指定压缩文件(library.zip)的行为;0 为不压缩。
  • Zipfiles           配置共享压缩文件的生成目录和文件名,默认是在目录 dist 下生成一个  “library.zip” 文件,打包了 .exe 文件运行需要的 .pyd  和 .dll 文件(不包含配置文件等)。
  • Optimize         打包优化,合法值是字符串('','O','OO')或者整型数字 (0, 1, or 2)。
    • 为 0 时:不进行优化,压缩包大小较大,打包的编译文件为 .pyc;
    • 为 1 时:进行少量优化,压缩包大小略小,打包的编译文件为 .pyo;
    • 为 2 时:优化级别最高,压缩包大小也明显变小,打包的编译文件为 .pyo。
  • Bundle_files     打包绑定,64位不支持此属性。
    • 为 0 时:pyd 和 dll 文件不会被打包到 exe 文件中;
    • 为 1 时:pyd 和 dll 文件会被打包到 exe 文件中,且不能从文件系统中加载 python 模块;
    • 为 2 时:pyd 和 dll 文件会被打包到 exe 文件中,但是可以从文件系统中加载 python 模块。
    • 注:
      • .py:编写的源文件。
      • .pyc:编译过的二进制代码文件。如果导入一个模块,python 将创建一个 *.pyc 文件,文件内为二进制码,这样可以在再次导入时更容易(更快)。
      • .pyo:当优化等级 (-O) 开启时生成的 *.pyc 文件。
      • .pyd:相当于一个 windows dll 文件。实际上 .pyd 文件就是 dll 文件,只是略有不同。
  • Date_files       文件可执行文件所需数据。在 python27 中,需要的 MSVCP90.dll 不能单独发布,必须确保 py2exe 复制所有的三个 dll 文件和 manifest 文件到工程目录 dist 下,并且放在一个名为 'Microsoft.VC90.CRT' 的子目录下。
    • 参考做法为:
      from glob import glob  
      
      data_files  = [
          ("Microsoft.VC90.CRT", 
          glob(r'C:Program FilesMicrosoft Visual Studio 
          freeze_support9.0VC
      edistx86Microsoft.VC90.CRT*.*'))
      ]
  • ascii               
    • 为 0 时:不包含编码和解码器;
    • 为 1 时则反之。
    • 假设出现 QPixmap::scaled: Pixmap is a null pixmap 问题,这是由于 PyQt 和 qt 都是默认的 png 格式的图片,打包后,会找不到 jpg 格式的图片,所以在打包过程中需要把 PyQt4 文件中的imageformats 文件夹下的 dll 文件导入。这是 jpg 格式的图片需要的插件。
  • 类标识符无属性,产生的CLSID无属性。

typelibs

列表:需要包含的gen_py产生的typelibs

  • 多进程打包遇到的程序不正常执行问题,需要在多进程之前调用 freeze_support() 函数。经试验,最好在函数开始执行的时候,首先调用此函数。

具体例子:

# -*- coding: utf-8 -*-

# 必须写的倒入模块
from distutils.core import setup
import py2exe

# 可以不用写的部分
"""
#We need to import the glob module to search for all files.
import glob
import sys
#this allows to run it with a simple double click.
sys.argv.append('py2exe')
"""

# 项目中需要用到的第三方库写入includes所对应的列表中
# 项目中用不到的会造成报错的文件放在excludes所对应的列表中,若报错的是dll文件,放入dll_excludes所对应的列表中
# 下面是示例:
opts= {
'py2exe':{ "includes" : [ "sip", "matplotlib.backends", "matplotlib.backends.backend_tkagg",
                          "matplotlib.figure","numpy", "matplotlib.pyplot", "pylab", "six",
                          "matplotlib.backend_bases", 'scipy.special._ufuncs_cxx',
                          "scipy.integrate","scipy.integrate.quadpack","scipy.sparse.csgraph._validation"],
           "excludes" : ['_gtkagg', '_tkagg', '_agg2', '_cairo', '_cocoaagg',
                         '_fltkagg','_gtk', '_gtkcairo'],

          "dll_excludes":['libgdk-win32-2.0-0.dll','libgobject-2.0-0.dll',"MSVCP90.dll",]
         }
      }

#项目中需要用到的外部文件依赖放入列表中,格式为[(),(),()]
# 元祖中第一个元素是打包时要创建的文件夹名,如果要放在exe文件的同目录下就用"",第二个元素是该依赖文件的路径
data_files= [(r'mpl-data',glob.glob(r'C:Anaconda3Libsite-packagesmatplotlibmpl-data*.*')),
            #Because matplotlibrc does not have an extension, glob does not findit (at least I think that's why)
            #So add it manually here:
            (r'mpl-data',[r'C:Anaconda3Libsite-packagesmatplotlibmpl-datamatplotlibrc']),
            (r'mpl-dataimages',glob.glob(r'C:Anaconda3Libsite-packagesmatplotlibmpl-dataimages*.*')),
            (r'mpl-datastylelib',glob.glob(r'C:Anaconda3Libsite-packagesmatplotlibmpl-datastylelib*.*')),
            (r'mpl-datafonts',glob.glob(r'C:Anaconda3Libsite-packagesmatplotlibmpl-datafonts*.*')),
            ("",[r"C:Anaconda3Libsite-packagesPyQt5libEGL.dll"]),
            ("platforms",[r"C:Anaconda3Libsite-packagesPyQt5pluginsplatformsqwindows.dll"])]

# 将上述参数传入setup中,console和windows分别代表控制台和图形界面,按需求选择
setup(
    #console = [{"script" : 'comtrade.py'}], 
    windows = [{"script":"comtrade.py", "icon_resources": [(1, "logo.ico")]} ],
    name = 'comtrade',
    version = '1.0', 
    options=opts, data_files=data_files)
setup.py(woshinibaba.py)

五、生成exe 

python setup.py app2exe

执行完毕后会生成build和dist文件夹,启动文件在dist文件夹下

py2exe报错解决

1. 执行打包命令时报错 Missing run-py3.5-win-amd64.exe

  • 原因:py2exe 最高只支持到 python3.4,如果你用的 3.5 或更高的版本就会出现这个问题
  • 解决方法:创建个虚拟环境安装 python3.4,然后 pip install 所有项目需要的第三方库后重新进行一边打包操作

2. 执行打包命令时报错 indexError: tuple index out of range

  • 原因:py2exe 最高只支持到 python3.4,如果用更高的版本就会出现这个问题
  • 解决方法:创建个虚拟环境安装 python3.4,然后 pip install 所有项目需要的第三方库后重新进行一边打包操作

3. 执行打包操作时报错 (忘了具体报错信息,意思时递归深度超过最大限制)

  • 原因:py2ex e最高只支持到 python3.4,如果你用的更高的版本就会出现这个问题
  • 解决方法:创建个虚拟环境安装 python3.4,然后 pip install 所有项目需要的第三方库后重新进行一边打包操作

4. 打包 ok,但双击可执行文件时报错 Failed to execute script xxx

  • 原因:去 log 文件中查看,会发现报错信息为 no module named xxx,意思为项目中缺少 xxx 模块
  • 解决方法:pip install ,如果你确定你已经安装了该模块,那就在 setup.py(woshinibaba.py)文件最上面 import 该模块

5. 打包 ok,但双击可执行文件时弹窗报错 This application failed to start because it could not find or load the Qt platform plugin "windows".

注:这是我遇到的一个最大的问题,问题原因和 PyQt5 有关目前尚未找到解决方案,然后选用了 pyinstaller

  • 疑似原因一:python3.4 不支持 PyQt5
  • 本人理解:python3.4 环境下用 pip install PyQt5,报错说找不到该模块,但是可以运行 pip install pyqt,而 pyqt 指得是 PyQt4,两者是不一样的。在 pycharm 中将其升级为 PyQt5,惊奇的发现我的python 环境变成 python3.7了?!在升级 pyqt 的同时将我的 python 都升级了?但是 py2exe 不支持 python3.7 啊,WTF?!最后本人不了了之选择了pyinstaller
  • 疑似原因二:没有将 PyQt5 写入环境变量
  • 网上提供的解决方法一:将 QT 的 bin 目录下的 platformsqwindows.dll 拷贝至 exe 所在目录,注意保留 platforms 子目录
  • 网上提供的解决方法二:在 data_files 参数中加入两个元祖,元祖中写入(该方法与上面的方法一个作用,他会在你执行打包命令时自动将将 QT 的 bin 目录下的 platformsqwindows.dll 拷贝至 exe 所在目录)
# 注:路径为你的python的PyQt5的路径
data_files=[("",
            [r"F:Pythonpython3Libsite-packagesPyQt5libEGL.dll"]),
            ("platforms",
            [r"F:Pythonpython3Libsite-packagesPyQt5pluginsplatformsqwindows.dll"])]
  • 网上提供的解决方法三:改变系统变量(变量值为你的 python 所在的目录下的 PyQt5 的目录)

                    

  

pyinstaller

  首先要声明,pyinstaller 在高版本的 python 环境下可能会出现不支持的情况,我在打包的时候只支持到 python3.5,不清楚目前支持到哪个 python 版本。如果你的 python 已是3.5以上的版本,建议创建一个虚拟环境后安装 python3.5,再自行安装上程序所依赖的库比如 requests 等等,在新环境中进行打包。

注:pyinstaller 方法已在 win10、win8 和 Mac环境下测试无误,但打包程序本身会因为你的程序的不同而需要有些许改动,文章末尾会有一些我遇到过的报错的解决方法,出现问题可自行Google

一、安装pyinstaller

pip install pyinstaller

二、切换到工作目录

cd xxxxxxxxxxx

三、打包命令

  与上面两个不同的是,pyinstaller 不需要自己写 setup.py 文件,只需要在工作目录中输入打包命令即可。最后会生成 build 和 dist 文件夹,启动文件在 dist 文件夹下。

命令格式:

pyinstaller [项目启动文件]

其他参数(按需求选择):

  • -F   表示在 dist 文件夹下只生成单个可执行文件(内部包含所有依赖),不加默认会在 dist 生成一大堆依赖文件+可执行文件。
  • -D   与 -F 相反用法
  • -W  表示去掉控制台窗口,如果你的程序是有界面的,可以不写这个参数。但是测试情况下建议先加上这个参数,因为如果打包不成功,运行时报错信息会在控制台上输出,没有控制台就看不到报错信息。
  • -c   表示去掉窗框,使用控制台
  • -p    表示自己定义需要加载的类路径,项目中包含多个自建模块的时候需要加上 -p aaa.py -p bbb.py -p ccc.py
  • -i     表示可执行文件的图标,后面跟图标的路径
  • --hidden-import  后面跟模块名如 queue,用于告诉打包程序某个模块我用不着你不用打包进去

  打包完毕后在 dist 文件夹下双击项目启动文件就可以了

pyinstaller报错解决

1.执行打包命令时报错  IndexError: tuple index out of range

  • 原因:官网目前的版本是 3.2.1 只支持到 python3.5 ,高版本的 python 尚不支持,
  • 解决方法:网上有大神提供了完善版的代码——官网源码里有 https://github.com/pyinstaller/pyinstaller 替换你 python 目录下的 Libsite-packagesPyInstaller 即可 这样就支持python3.6了 不过是开发版,可能还不完善。

  作者建议最好还是用虚拟环境下的 python3.5 进行打包。


2.执行打包命令时报错 ImportError: No module named 'queue'

  • 原因:尚不清楚
  • 解决方法:如果该模块你用不到,可以在执行打包命令时用 --hidden-import 不打包进去。如果程序中需要该模块,在主文件最上面写上 improt queue

3.打包命令执行成功,但双击可执行程序弹出报错窗口failed to excute script xxx

  • 原因:打包时内部缺少了某个依赖,这时需要看看控制台打印了什么报错信息,打包时加了 -w 参数的请再打包一次记得去掉 -w
  • 现象:基本都是在控制台上发现报错 No module named 'xxxxx',如 No module named 'queue' 或者 ModuleNotFoundError: No module named 'PyQt5.sip'
  • 解决方法:同2,如果该模块你用不到,可以在执行打包命令时用 --hidden-import 不打包进去。如果程序中需要该模块,在主文件最上面写上 improt xxxxx。如 import queue 或 import PyQt5.sip

                

原文地址:https://www.cnblogs.com/zhuminghui/p/9483627.html