文件打包

概要

  • 脚本打包exe:win/mac【终端】
  • qt5,开发桌面应用
  • 打包qt5程序【桌面应用】
  • 1.exe 打包

    pip install pyinstaller

    注意事项:

  • 支持mac、win(Windows建议使用python3.6.8)

  • 配合虚拟环境打包

    Mac系统下:
    
    1、在mac系统上先开发(为这个程序创建一个虚拟环境) dream
    2、开发........(安装各自用到的包)
    3、开发完毕后
    	pip freeze > reqirements.txt(将不同的包写入到程序中)
    4、打开Windows虚拟机(专门对程序进行打包)
    5、创建一个虚拟环境(专门用来写项目用的)
    6、安装项目依赖
     	pip install -r reqirements.txt
    7、安装pyinstaller
    	pyinstaller -F xxxxx
    	
    产出:dream.exe
    Windows系统下:
    
    1、创建虚拟环境+项目  dream
    2、开发
        开发完毕后
            pip freeze > reqirements.txt(将不同的包写入到程序中)(可有可无)
    3、安装pyinstaller
    	pyinstaller -F xxxxx
    
    产出: dream.exe
    
    #可以不用写pip freeze > reqirements.txt(这步)
    #原因:利用自己的Windows系统已经存在这个环境文件了。
  • 过程详解:

  • 1、pyinstaller包:帮我们快速打包应用程序!
  • 2、Windows建议使用python3.6.8:对程序打包会发生更少的bug!
  • 3、建议配合虚拟环境打包程序:每一个程序用到的包都是不同的,在不同的虚拟环境下会安装各自的不同的包!
  • 4、pip freeze > reqirements.txt:就是将虚拟环境中安装的所有的第三方包写入到程序中!
  • 5、创建一个虚拟环境:专门用来写项目用的!
  • 6、pip install -r reqirements.txt:把文件中所有写上的包都安装上了!
  • 7、pyinstaller -F xxxxx:产出exe程序命令!
  • 2、开发流程

    2.1 创建虚拟环境

    2.2 创建成功后

    2.3 创建新项目(app.py)

    2.4 开发程序文件及安装一些自己想依赖的包

    2.5 利用 pip freeze > reqirements.txt 语句查看当前依赖的包有哪些

  • 可以查看到当前安装的包以及包的版本
  • 3、多文件打包

    利弊:

  • 缺点:文件夹文件冗杂
  • 优点:运行速度较快
  • 3.1 首先 pip install pyinstaller 安装这个包

  • 安装完成后关闭终端再重启终端,输入 pyinstaller 即可看到关于 pyinstaller 的各种命令
  • 3.2 打包方法

    pyinstaller -D 项目名
    
    pyinstaller -D app.py
  • 打包成功后即可看到目录结果已经发生了明显变化
  • 文件解读

  • build:中间编译过程中产出的代码(基本没用)

  • dist:打完包后的结果。可以运行的文件(将app文件夹压缩打给其他人解压—–>点击app.exe即可运行程序)

  • app.spec:打包过程中生成的配置文件。

  • 3.3 双击运行效果:(输入信息并打印信息)

    3.4 注意事项:报错详解

  • 在写代码过程中可能会写错代码导致错误,如果是正常的点击exe文件运行程序,则会进入,运行到错误的时候后马上闪退,并且无法知道报错的类型及原因!

  • 解决办法:

  • 将exe文件拖拽到cmd(命令提示符窗口)运行
  • 明显细节可能出错:输入文本类型会报错

    text = int(input("请输入信息:"))

  • 重新打包程序(将之前打包产生的文件都删除,即build文件夹、dist文件夹、app.spec)

  • 打包完成后按照上述方法

  • 将exe文件拖拽到cmd(命令提示符窗口)运行(或输入文件路径,本质上二者均是输入路径运行程序)
  • 在终端命令行运行代码即可查看到报错原因
  • 4、单文件打包

    利弊:

  • 优点:只有一个文件即exe文件
  • 缺点:运行起来需要缓存,可能回慢一些
  • 打包方法

    pyinstaller -F 项目名
    
    pyinstaller -F app.py

  • 可以很明显的看到打包后的dist文件夹内只有app.exe一个文件(即可执行exe程序文件)

  • 运行效果如刚才所示

  • 5、文件打包之对于exe文件的命名

    5.1 打包命名方法

    pyinstaller -F 项目名 -n 文件名
    
    pyinstaller -F app.py -n 哔哩哔哩
    
    pyinstaller -D 项目名 -n 文件名
    
    pyinstaller -D app.py -n 哔哩哔哩

    5.2 打包完成后即可看到明显变化

  • 运行程序名如我们所命名的变成了哔哩哔哩
  • 6、其他方法参数详解

    6.1 参数参考

    [-h] [-v] [-D] [-F] 
    [--specpath DIR] 
    [-n NAME] 
    [--add-data <SRC;DEST or SRC:DEST>]      
    [--no-embed-manifest] 
    [-r RESOURCE] 
    [--uac-admin] 
    [--uac-uiaccess]
    [--win-private-assemblies] 
    [--win-no-prefer-redirects] 
    [--argv-emulation]
    [--osx-bundle-identifier BUNDLE_IDENTIFIER] 
    [--target-architecture ARCH]
    [--codesign-identity IDENTITY] 
    [--osx-entitlements-file FILENAME] [--runtime-tmpdir PATH]     
    [--bootloader-ignore-signals] [--distpath DIR] 
    [--workpath WORKPATH] 
    [-y]
    [--upx-dir UPX_DIR] 
    [-a] 
    [--clean] 
    [--log-level LEVEL]
    scriptname [scriptname ...]

    6.2 参数详解

    6.2.1 打包参数详解
    参数 说明
    -F 产生单个的可执行文件
    -D 产生一个目录(包含多个文件)作为可执行程序
    -a 不包含 Unicode 字符集支持
    -d debug 版本的可执行文件
    -w 指定程序运行时不显示命令行窗口(仅对 Windows 有效)
    -c 指定使用命令行窗口运行程序(仅对 Windows 有效)
    -o 指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
    -p 设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径
    -n 指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字
    6.2.1 打包带自定义icon(图标)的exe可执行文件
  • 1、下载icon(图标文件)

    https://www.iconfont.cn/

  • 2、也可以将图片转化为icon(图标文件)

    https://www.bitbug.net/

  • 3、用以下命令自定义exe图标

    pyinstaller -F -i icon文件名 程序名
    
    pyinstaller -F -i bit.ico app.py

  • 很明显可以看到我们的程序图标已经发生变化

  • 7、运行文件读取其他文件操作详解

    7.1. 读取文件路径

    import os
    
    p1 = os.path.abspath(__file__)  #os模块读取当前文件所在绝对目录
    print(p1)# E:\Luffycity\exe\bilibili\app.py
    
    p1 = os.path.dirname(os.path.abspath(__file__))  # os模块读取当前文件所在绝对目录的上一级目录
    print(p1) #E:\Luffycity\exe\bilibili

    7.2 添加文件路径

    import os
    
    #声明根目录
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # os模块读取当前运行文件所在绝对路径的上一级目录
    
    print("------欢迎使用dream系统!------")
    
    #os.path.join(BASE_DIR,"acount.txt") : 拼接路径 即 把txt文件路径拼接到BASE_DIR路径下面
    with open(os.path.join(BASE_DIR,"acount.txt"), mode='r', encoding='utf8') as f:
        data = f.read().strip()

    7.3 特别注意

    7.3.1 多文件打包:不会报错

  • 原因是文件都在文件夹内
  • 7.3.2 单文件打包:报错

    为什么会报错?
    
    #只生成了一个dist文件
    
    //多文件打包:在运行程序exe文件时,是会解压所有的文件的,多文件打包时可以读取到每个文件夹的路径及内容
    
    //单文件打包:在运行exe程序时,会解压所有的文件到虚拟文件目录,无法读取到写死的文件夹目录

    7.3.4 如何解决之分析思路(sys.argv)

  • 创建demo.py文件,将sys.argv函数写进去。

    import sys
    
    #sys.argv : 列表内存储的是列表(列表内存的就是当前程序所在的目录)
    print(sys.argv) # ['E:\\Luffycity\\exe\\bilibili\\demo.py']
    print(sys.argv[0]) # E:\Luffycity\exe\bilibili\demo.py
  • 将demo.py封装成exe程序

    pyinstaller -F demo.py

  • 在命令行终端运行即可看到文件路径

  • 在exe程序代表可执行程序的文件所在路径

  • ['E:\\Luffycity\\exe\\bilibili\\demo.py']
  • 找到文件所在的绝对路径

    #os.path.relpath : 找到所在文件的绝对路径
    BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
  • 7.3.5 如何解决之修改代码
  • 修改BASE_DIR的路径

    import time
    import os
    import sys
    
    # 声明根目录
    # BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # os模块读取当前运行文件所在绝对路径的上一级目录
    #拿到程序所在的根目录
    BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
    print("------欢迎使用dream系统!------")
    
    # os.path.join(BASE_DIR,"acount.txt") : 拼接路径 即 把txt文件路径拼接到BASE_DIR路径下面
    with open(os.path.join(BASE_DIR, "acount.txt"), mode='r', encoding='utf8') as f:
        data = f.read().strip()
    
    print(data)
    
    time.sleep(5)
  • 7.3.6 运行效果及注意事项
  • 1、重新打包exe文件

    pyinstaller -F app.py -n 哔哩哔哩
  • 2、注意将想要读取的文件copy到程序所在目录,否则会报错

  • 3、运行效果 — 正常读取文件内容

  • 7.3.5 如何解决之分析思路(frozen)

    import sys
    if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
        print('running in a PyInstaller bundle')
    else:
        print('running in a normal Python process')
  • 条件选择

  • 如果是程序运行文件

  • 如果是py文件

    if getattr(sys, 'frozen', False):
        #executable : 当前执行文件的文件目录
        BASE_DIR = os.path.dirname(sys.executable)
    else:
        #文件夹所在目录
        BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  • app.py文件

    import time
    import os
    import sys
    
    if getattr(sys, 'frozen', False):
        #executable : 当前执行文件的文件目录
        BASE_DIR = os.path.dirname(sys.executable)
    else:
        #文件夹所在目录
        BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    
    print("------欢迎使用dream系统!------")
    
    # os.path.join(BASE_DIR,"acount.txt") : 拼接路径 即 把txt文件路径拼接到BASE_DIR路径下面
    with open(os.path.join(BASE_DIR, "acount.txt"), mode='r', encoding='utf8') as f:
        data = f.read().strip()
    
    print(data)
    
    time.sleep(5)
  • 8、关于模块

    8.1 先创建一个文件夹(里面包含两个包)

  • 定义两个函数文件 db.py和encrypt.py

  • db.py

  • def get_data_base():
        return "mysql"
  • encrypt.py

  • def md5():
        return "加密"
  • 在主文件(app.py)中导入这个包并调用这两个函数

  • 打包exe:会发现这两个函数(包)默认帮我打到我依赖的包里面

  • 可以正常运行且会输出内容
  • 8.2 包里面又嵌套了另一个包

  • 定义三个函数文件 db.py、encrypt.py、tree.py

  • db.py

    from . import tree
    
    
    def get_data_base():
        print(tree.get_name())
        return "mysql"
  • encrypt.py

    def md5():
        return "加密"
  • tree.py

    def tree():
        return tree
  • 重新打包运行exe程序

  • 正常运行且可以看到从另一个依赖的包导入的函数

  • 总结

  • 当写的内容较少时,导入的模块会默认被添加到默认依赖里面

  • 8.3 如果遇到动态导入模块的代码时,则无法找到关联的包

    8.3.1 方法一:常规方法
  • import time
    
    print("------欢迎使用dream系统!------")
    
    # #正常常规写法
    from utils import card
    card.get_number()
    
    time.sleep(5)
  • 8.3.2 方法二:动态导入模块方法
  • import importlib  #动态模块导入依赖包
  • import time
    
    print("------欢迎使用dream系统!------")
    
    #借助模块动态导入方法
    import importlib
    card = importlib.import_module('utils.card')
    v1 = card.get_number()
    print(v1)
    
    time.sleep(5)
  • 问题:

  • 虽然动态模块导入可以 运行 import importlib 将此依赖包加入到默认依赖中

  • 但是无法 通过 card = importlib.import_module('utils.card') 这行代码,将其中的card函数(包)加入到默认依赖中

  • 8.4 对于动态导入模块中无法找到依赖包的解决办法

  • 程序名.spec(哔哩哔哩.spec):pyinstaller打包时读的配置文件

  • 在 hiddenimports 手动加入需要动态导入的包

    hiddenimports=[
            "utils.card",
        ],

  • 注意:再次重新打包时

  • 不能使用

    pyinstaller -F app.py -n 哔哩哔哩
  • 原因:利用该语句打包时,会重新创建 哔哩哔哩.spec 配置文件,将改好的配置文件替换成默认的
  • 应该使用

    pyinstaller -F  哔哩哔哩.spec
    
    #此语句可能会报这个错
    #makespec options not valid when a .spec file is given
    
    #解决办法:不加 -F
    pyinstaller 哔哩哔哩.spec
  • 效果如下:正常启动运行
  • 物联沃分享整理
    物联沃-IOTWORD物联网 » Python文件打包exe程序

    发表评论