• py打包工具


    库地址:

    auto-py-to-exe

    https://pypi.org/project/auto-py-to-exe/

    Gooey

    https://pypi.org/project/Gooey/

    为什么要介绍这俩库?

    1. 直接丢代码给别人用:各种配置环境要有才能执行
    2. 命令行执行:丑
    3. 打包复杂
    4. 可视化界面编写复杂

    auto-py-to-exe

    auto-py-to-exe 是一个用于将Python程序打包成可执行文件的图形化工具。以往打包会使用pyinstaller库,需要掌握各种参数的作用,很难记。而auto-py-to-exe 基于 pyinstaller ,和相比pyinstaller ,多了 GUI 界面,用起来更为简单方便。

    安装

    # py版本 3.6-3.10
    pip install auto-py-to-exe
    

    启动

    auto-py-to-exe
    

    image-20211227171132476

    常用选项介绍

    • 单文件

      • 单文件:打包的可执行文件就一个,把所有的依赖都打包到一个文件中

        image-20211227171439035

      • 单目录:打包的可执行文件在输出目录中,包含了依赖文件、库

      image-20211227171641097

    • 控制台窗口

      • 控制台:所有的交互在控制台显示
      • 窗口:编写了窗体界面才能用此选项,隐藏控制台,仅展示编写的窗体
    • 图标

      可执行文件的图标,需要是ico格式

    • 附加文件

      • 添加文件:脚本依赖外部文件,即添加单个文件作为依赖
      • 添加目录:脚本依赖外部库(或多个文件),添加对应目录作为依赖
    • 高级选项

      • 常规选项
        • name:执行文件的名称
        • clean:打包前是否清除缓存
      • 捆绑选项
        • add-binary:如果用到可执行文件,可以添加进去
        • paths:搜搜依赖库的目录(一般不用)
        • key:编译的临时文件是否需要加密,如果是核心代码,建议加密。
      • windows特定选项
        • version-file:版本文件,一般不用
    • 设置

      • 输出路径:用于生成打包文件存放的地方
    • 当前命令

      用于展示pyinstaller打包的命令

    • 输出

      展示打包的过程

    实践

    功能

    编写一个简单的交互式脚本,实现输入一个数,输出这个数加3的值。

    编写脚本

    • 脚本结构

      image-20211227174420079

    • 文件内容

    # Add3.py
    
    def add3(x):
        return x + 3
    
    # run_it.py
    
    import time
    from Fun.Add3 import add3
    
    if __name__ == '__main__':
        n = input('请输入一个数:')
        print(add3(int(n)))
        time.sleep(10)  # 避免执行完直接退出了,啥都看不到
    

    看一下正常的控制台交互效果:

    image-20211227175201972

    文件打包

    image-20211227175618821

    image-20211227175641300

    生成文件

    image-20211227175800961

    如果不想生成一个目录,可以选择生成一个文件,【单文件】选择单文件即可。

    image-20211227180439666

    Gooey

    一个快速构建可视化页面的工具库,自带封装好了的各种组件,只需一行命令就能生成一个带界面的工具。

    安装

    # py 2.7 3.x
    pip install Gooey
    

    改写脚本

    # run_it.py
    
    from Fun.Add3 import add3
    from gooey import Gooey, GooeyParser
    
    
    @Gooey(program_name="工具的名字啊")
    def main():
        parser = GooeyParser(description="第一个示例!")
        parser.add_argument(
            "x",
            metavar=u'输入的数:',
            help="请输入一个整数"
        )
        args = parser.parse_args()
        try:
            res = add3(int(args.x))
            print(res)
        except TypeError as e:
            print('哎呀,报错了')
    
    
    if __name__ == '__main__':
        main()
    

    image-20211227182942334

    运行

    image-20211227183032397

    组件介绍

    组件 -- a --
    FileChooser 文件选择器
    MultiFileChooser 文件多选器
    DirChooser 目录选择器
    MultiDirChooser 目录多择器
    DateChooser 日期选择器
    TextField 文本输入框
    Dropdown 单选框
    RadioGroup 复选框

    全局配置

    参数 介绍
    advanced 切换显示全部设置还是仅仅是简化版本
    show_config 跳过所有配置并立即运行程序
    language 指定从 gooey/languages 目录读取哪个语言包
    program_name GUI 窗口显示的程序名。默认会显 sys.argv[0]。
    program_description Settings 窗口顶栏显示的描述性文字。默认值从 ArgumentParser 中获取。
    default_size 窗口默认大小,(600,400)
    required_cols 设置必选参数行数。
    optional_cols 设置可选参数行数。
    dump_build_config 将设置以 JSON 格式保存在硬盘中以供编辑/重用。
    richtext_controls 打开/关闭控制台对终端控制序列的支持(对字体粗细和颜色的有限支持)

    支持多种结构布局

    image-20211227184947121

    image-20211227185015640

    image-20211227185029980

    image-20211227185041311

    实践

    图片角度修正可视化工具

    目录结构

    image-20211227185203744

    文件

    # main.py
    import os
    from fun import *
    
    from gooey import Gooey, GooeyParser
    
    
    @Gooey(
        richtext_controls=True,  # 打开终端对颜色支持
        program_name="爱标xx工具",  # 程序名称
        encoding="utf-8",  # 设置编码格式,打包的时候遇到问题
        progress_regex=r"^progress: (\d+)%$",  # 正则,用于模式化运行时进度信息
        menu=[{
            'name': '文件',
            'items': [{
                'type': 'AboutDialog',
                'menuTitle': '关于',
                'name': '图片旋转角度处理工具',
                'description': '用于处理标注页面图片看起来正常,但是切图后发现图片和画框的位置不一致,例:倒置、旋转等',
                'developer': 'wjlv4@iflytek.com',
            }, {
                'type': 'Link',
                'menuTitle': '访问主页',
                'url': 'https://ainxx.iflyxxx.com/'
            }]
        }, {
            'name': '帮助',
            'items': [{
                'type': 'AboutDialog',
                'menuTitle': '帮助文档',
                'name': '图片旋转角度处理帮助',
                'description': '标注页面画框后预览图片出现倒置、旋转等问题自助处理步骤:\n1. 打开浏览器调试窗口(F12)\n2. 点击Network(网络)\n3. 点击标注页面框的√号\n4. 在调试窗口的“网络”中找到ocr?url=的请求后,鼠标点击此请求\n5. 复制右侧地址中的/test/xxxx/xxxx.jpg到工具输入栏',
            }]
        }]
    )
    def main():
        parser = GooeyParser(description="平台辅助工具:图片旋转角度修正、入库试题可视化 ...")
    
        subs = parser.add_subparsers(help='commands', dest='command')
    
        draw_pic = subs.add_parser('入库图片可视化')
        pic_fix = subs.add_parser('图片旋转角度修正')
        pic_fix.add_argument('source_page_url',
                             metavar='图片路径',
                             help='请输入ocr图片的路径以/test开头.jpg结尾',
                             widget='TextField')
        pic_fix.add_argument(
            "rotate",
            metavar=u'旋转角度:',
            help="图片需要旋转的角度(逆时针)",
            # 选项
            choices=['90', '180', '270', '0'],
            # 默认值
            default='180',
            # 下拉菜单
            widget='Dropdown',
            # 数据校验
            gooey_options={
                'validator': {
                    'test': "user_input in ['0','90', '180', '270']",
                    'message': "仅能输入90、180、270、0]"
                }}
        )
    
    
        draw_pic.add_argument(
            "page_path",
            metavar=u'请输入待处理目录',
            help="page.json和topic.json所在的目录",
            # 下拉菜单
            widget='DirChooser'
        )
    
        args = parser.parse_args()
        if getattr(args, 'page_path', None):
            run(os.path.join(args.page_path, 'page.json'))
        elif getattr(args, 'source_page_url', None):
            deal_pic(args.source_page_url, int(args.rotate))
    
    
    # 将界面收集的参数进行处理
    # ......
    
    if __name__ == '__main__':
        main()
    
    
    # zzj_rotate_pic.py
    
    # coding:utf8
    import sys
    
    import requests
    from PIL import Image
    from fun.Log import *
    
    
    def resource_path(relative_path):
        """ Get absolute path to resource, works for dev and for PyInstaller """
        try:
            # PyInstaller creates a temp folder and stores path in _MEIPASS
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")
    
        return os.path.join(base_path, relative_path)
    
    
    def download_src(source_page_url, out_file_path='before.jpg'):
        """
        下载原图
        :param out_file_path:保存路径
        :param source_page_url: 原图url
        :return:
        """
    
        logger.info(f' download {source_page_url}')
        print('progress: 15%')
        if not source_page_url.startswith('http'):
            url = r"http://aixxxe-mxxxxe.ifxxdxxxb.com/api/ds/api/daxxxet/file"
            source_page_url = url + '?path=' + source_page_url
        # 下载并存储原图
        res = requests.get(source_page_url, stream=True)
        with open(out_file_path, 'wb') as f_w:
            f_w.write(res.content)
        print('progress: 30%')
    
    
    def rotate_pic(rotate, pic_path='before.jpg'):
        logger.info('rotate pic ...')
        print('progress: 45%')
        img = Image.open(pic_path)
        img1 = img.rotate(rotate, expand=True)
        logger.info('save rotated pic ...')
        img1.save('after.jpg')
        print('progress: 60%')
    
    
    def upload_file(file_path, local_file='after.jpg'):
        url = r"http://axxte-mxxxxe.iflxxxxub.com/api/ds/api/daxxxt/file"
        if not file_path.startswith('http'):
            file_path = url + '?path=' + file_path
        file = open(local_file, 'rb')
        logger.info('upload file ...')
        print('progress: 80%')
        querystring = {"path": file_path.split('=')[-1]}
        response = requests.post(url=url, data=file.read(), headers={'Content-Type': 'text/xml'}, params=querystring)
        if response.status_code == 200 and response.json().get('retcode', None) == '000000':
            logger.info('upload success!')
            print('progress: 100%')
        else:
            logger.error(f'upload failed!')
            print('progress: 0%')
    
    
    def deal_pic(source_page_url, rotate, save_tmp_file=False):
        """
        处理角度异常的图片
        :param source_page_url: 原图地址
        :param rotate: 旋转角度,逆时针旋转
        :param save_tmp_file: 是否保存临时文件
        :return:
        """
        try:
            download_src(source_page_url)
            rotate_pic(rotate)
            upload_file(source_page_url)
            if not save_tmp_file:
                os.remove('before.jpg')
                os.remove('after.jpg')
        except Exception as e:
            logger.error(e)
    
    
    
    
    if __name__ == '__main__':
        deal_pic(r'/test/5db9bca31775476cbxxxxx11f018ea9/cf0907bf6e9adxxxxx90f35a278c9e41.jpg',180)
    
    

    运行

    image-20211227185433590

    打包成执行文件

    image-20211227185603112

    image-20211227185614786

    image-20211228094944965

    打包文件太大?

    • 原因:打包用的解释器是conda的环境,里面包含了非当前脚本用到的第三方库

    • 解决办法:使用新的环境来打包文件,新的环境只安装脚本需要的库

      • 创建新环境

        我这里采用的是virtualenv管理环境,直接创建一个新环境

        D:\ProgramData\virtualenvs>virtualenv -p "C:\Users\wjlv4\AppData\Local\Programs\Python\Python39-32\python.exe" tmp_env
        created virtual environment CPython3.9.5.final.0-32 in 2517ms
          creator CPython3Windows(dest=D:\ProgramData\virtualenvs\tmp_env, clear=False, no_vcs_ignore=False, global=False)
          seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\wjlv4\AppData\Local\pypa\virtualenv)
            added seed packages: pip==21.3.1, setuptools==59.4.0, wheel==0.37.0
          activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
        
        D:\ProgramData\virtualenvs>
        
        

        切换项目的解释器到新虚拟环境,可以看到新的虚拟环境没有任何外部库。

        image-20211228095851791

      • 本地执行脚本,查看缺少哪些依赖包,分别安装上

        image-20211228095957625

        image-20211228100053630

        pip install Pillow requests pandas loguru gooey
        
      • 在当前的虚拟环境使用pyinstaller打包(命令可以直接用刚才打包的命令)

        image-20211228100634035

        image-20211228112405670

        依赖安装完毕后,在虚拟环境安装pyinstaller之后,直接复制命令执行即可:

        pyinstaller --noconfirm --onedir --windowed --icon "D:/abk.ico" --name "脚本的标题" --clean --key "1231a" --add-data "D:/t1/fun;fun/"  "D:/t1/main.py"
        
      • 查看打包后的文件

        image-20211228113048374

      明显减少了很多。

  • 相关阅读:
    零零碎碎
    MFC入门--显示静态图片及调用本地软件
    Python版本OpenCV安装配置及简单实例
    用星星画菱形--Java
    pycharm IDE在导入自定义模块时提示有错,但实际没错
    Cmd使用方式--命令行运行程序
    cv2 & PIL(pillow)显示图像
    C++命令行多文件编译(g++)
    MNIST多图显示--Python练习
    visual studio 2017--括号自动补全
  • 原文地址:https://www.cnblogs.com/wjlv/p/15740013.html
Copyright © 2020-2023  润新知