打包工具选择
我自己用PyQT5写了一个应用程序,基本上都写好了,到了打包发布这个环节,我知道的有两个工具可以帮我打包分别是
Pyinstaller Cythonpyinstaller会给我打包成一个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,它们就不用打包了。然后我把我的五个源码文件夹
以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文件如下:
运行完这些源码文件夹把得到pyd文件分别拷贝到另外一个目录下对应文件夹中,最终我的打包好的目录结构如下:
其中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
运行截图如下:
遇到的坑
最后说一下,我用cython打包遇到的一个大坑:
我有个超类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里面去就好啦!
全部0条评论
快来发表一下你的评论吧 !