PyQT5+OpenCV开发的应用如何打包发布?

描述

 

打包工具选择

我自己用PyQT5写了一个应用程序,基本上都写好了,到了打包发布这个环节,我知道的有两个工具可以帮我打包分别是  

Pyinstaller
Cython
pyinstaller会给我打包成一个exe文件,把python解释器、所有依赖包、各种DLL都打包在里面,然后最终出来一个exe文件,听上去是很好。   Cython会给打包成pyd文件,然后依靠包导入的方式完成启动,不会生成exe文件,听上去没有pyinstaller好   两个工具的安装都十分方便快捷:  
pip install pyinstaller
pip install Cython
这两个我都测试了,最终我选择用Cython完成了打包发布。为什么不用pyinstaller,打包出来的exe文件比较大,然后各种命令行操作我也不太熟悉,已经学废了。然后打包完成之后各种DLL无法load跟找不到,时间不等人啊!最重要的,pyinstaller打包完成exe其实就是一个压缩包,特别容易被反编译跟逆向工程取得源码!谁让我已经用过这招,所以我猜别人也会用。   选择Cython是转C然后编译生成的,想要逆向是十分困难,源码保护会好点,另外Cython打包只要一个脚本运行一下就好啦,这个脚本还是我从网上的来的,感谢这个脚本作者!脚本随便改了改,就可以直接使用了。

 

打包流程与目录结构

在开始打包之前,先把一些非源码的文件整理好,放在项目的资源文件夹下面,我有两个资源文件夹分别是images跟models,它们就不用打包了。然后我把我的五个源码文件夹

pyqt5

以dlcore为例,修改脚本,运行package_installer,脚本我也贴出来,有需求的自取,放到跟项目同层目录下面,会自动创建build文件夹的。

import sys, os, shutil, time
from distutils.core import setup
from Cython.Build import cythonize

start_time = time.time()
curr_dir = os.path.abspath('.')
parent_path = sys.argv[1] if len(sys.argv) > 1 else ""
setup_file = __file__.replace('/', '\')
build_dir = "build"
build_tmp_dir = build_dir + "/temp"

s = "# cython: language_level=3"


def get_py(base_path=os.path.abspath('.'), parent_path='', name = 'ui', excepts=(), copyOther=False, delC = False):
    """
    获取py文件的路径
    :param base_path: 根路径
    :param parent_path: 父路径
    :param excepts: 排除文件
     py文件的迭代器
    """
    full_path = os.path.join(base_path, parent_path, name)
    for filename in os.listdir(full_path):
        full_filename = os.path.join(full_path, filename)
        if os.path.isdir(full_filename) and filename != build_dir and not filename.startswith('.'):
            for f in get_py(base_path, os.path.join(parent_path, name), filename, excepts, copyOther, delC):
                yield f
        elif os.path.isfile(full_filename):
            ext = os.path.splitext(filename)[1]
            if ext == ".c":
                if delC and os.stat(full_filename).st_mtime > start_time:
                    os.remove(full_filename)
            elif full_filename not in excepts and os.path.splitext(filename)[1] not in ('.pyc', '.pyx'):
                if os.path.splitext(filename)[1] in ('.py', '.pyx') and not filename.startswith('__'):
                    path = os.path.join(parent_path, name, filename)
                    yield path
        else:
            pass


def pack_pyd():
    # 获取py列表
    module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,)))
    try:
        setup(
            ext_modules=cythonize(module_list, compiler_directives={'language_level': "3"}),
            script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir],
        )
    except Exception as ex:
        print("error! ", str(ex))
    else:
        module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,), copyOther=True))

    module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,), delC=True))
    if os.path.exists(build_tmp_dir):
        shutil.rmtree(build_tmp_dir)

    print("complate! time:", time.time() - start_time, 's')


def delete_c(path='.', excepts=(setup_file,)):
    '''
    删除编译过程中生成的.c文件
    :param path:
    :param excepts:
    
    '''
    dirs = os.listdir(path)
    for dir in dirs:
        new_dir = os.path.join(path, dir)
        if os.path.isfile(new_dir):
            ext = os.path.splitext(new_dir)[1]
            if ext == '.c':
                os.remove(new_dir)
        elif os.path.isdir(new_dir):
            delete_c(new_dir)


if __name__ == '__main__':
    try:
        pack_pyd()
    except Exception as e:
        print(str(e))
    finally:
        delete_c()
  最终build文件夹中生成对应的pyd文件如下:

 

pyqt5

运行完这些源码文件夹把得到pyd文件分别拷贝到另外一个目录下对应文件夹中,最终我的打包好的目录结构如下:

pyqt5

其中requirements.txt里面是需要安装第三方依赖库,在该目录运行命令行即可完成安装:

pip install -r requirements.txt
  参考readme文件,可以完成OpenVINO2022、TensorRT8.x配置支持。   我手写了一个简单startup.bat文件,双击startup.bat即可运行打包好的应用!
@echo "try to run openvm"
SET PYTHONPATH=.%PYTHONPATH%
cd ui
python application_ui.py
 

 

运行截图如下:

pyqt5

遇到的坑

最后说一下,我用cython打包遇到的一个大坑:

pyqt5

我有个超类ImageTask,然后里面定义几个抽象方法,其中一个方法名是exec,谁知道这个是cython无法解析的字符串,我猜可能因为代码安全检测无法通过,所以一直给报截图的错误,后面我把这个方法名称从exec改成t_exec就可以把对应的py文件转pyd文件了。

使用pyinstaller打包时候会遇到

 

Error: geos_c.dll not found, required by hook-shapely.py

 

去下载geos_c.dll  https://www.dll-files.com/geos_c.dll.html

然后扔到windows/system32里面去就好啦!

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分