为什么将python程序编译成pyd?
因为.pyd文件是将.py文件编译为.c,再把.c编译为.pyd,反编译只能编译为.c文件,几乎没有可读性,能较好的保证应用代码的安全性及私密性。针对需要将python应用发布为客户端或在非安全环境下使用。
使用cython编译pyd
cython安装
pip install Cython
# 或
python -m pip install Cython
cython使用
在需要编译的.py平级目录创建build.py ,配置需要导出的python模块及文件路径。
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension('main', ['main.py'],), # 模块,对应模块文件。位置为当前文件的相对位置
Extension('all.auth_util', ['all/auth_util.py'],),
Extension('all.cfg', ['all/cfg.py'],),
Extension('all.config_utils', ['all/config_utils.py'],),
Extension('all.exe_main', ['all/exe_main.py'],),
Extension('all.loading_mask_dlg', ['all/loading_mask_dlg.py'],),
Extension('all.main_dlg', ['all/main_dlg.py'],),
Extension('all.welcome_dlg', ['all/welcome_dlg.py'],),
Extension('all.process_work_runner', ['all/process_work_runner.py'],),
Extension('all.register_dlg', ['all/register_dlg.py'],),
Extension('all.res_utils', ['all/res_utils.py'])
]
setup(
name="runExe",
version='1.0',
cmdclass={'build_ext': build_ext},
packages=['all'],
package_dir={'': 'build_cython'}, # 这里指定编译后结果存放的路径
ext_modules=ext_modules,
)
编译
build.py配置完成后,命令行进入到build.py存放的路劲下,执行
python build.py build_ext --inplace
# --inplace 表示当前运行目录在 build.py处
如上所示,编译结果将存储在/build_cython 下
*python程序打包成pyd之后,只能以python模块的方式调用,所以,我在这里添加了一个run.py
# coding=utf-8
from main import runMain # 这个模块是打包成.pyd后的主程序入口
if __name__ == '__main__':
runMain()
到这里,已经完成了安全的python程序发布,很好的保护了我们的源码。美中不足是用户只能以命令行的方式启动程序。
python run.py
如果你的用户是专业人员,那么到这一步就可以了。以下可以忽略。然而,如果你的应用可能是客户端程序,或者其他面向普通用户的程序,那么,你就需要让用户以更习惯的方式打开程序。
比如说:exe
EXE
python应用程序可以使用pyinstaller直接打包成exe.但它会将所有依赖和引用打入exe,导致exe启动缓慢,直观感受是双击exe之后有可能要去泡个茶,不清楚状况的用户会任务是程序未响应,从而反复双击exe。
我的应用因为引用了第三方模块,且第三方模块不能在独立python环境下运行。所以我选择使用c++制作exe,通过命令行调用python主程序运行。因为python主程序本身有可能加载较慢,为了避免这个问题,我们可以使用 (Splash Screen)启动画面。在双击之后做快速视觉响应。我这里界面框架使用了qt5
#include <QtWidgets/QApplication>
#include <QSplashScreen>
#include <QPixmap>
#include <QProcess>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
QPixmap pixmap("res/l.png");
QSplashScreen screen(pixmap);
//screen.showMessage(QStringLiteral("正在努力加载资源,请稍等..."),Qt::AlignBottom, QColor("#ffffff"));
//screen.setStyleSheet("font-family:'微软雅黑';font-size:14px;");
screen.show();
QProcess p(0);
p.start("cmd.exe /C python run.py"); //也可使用startDetached()使python主程序与当前exe进程分离
p.waitForStarted();
……
return a.exec();
}
启动画面有了,也能成功通过命令行调起python主程序了。
这里省略了exe进程和主程序之间通讯的代码。就是当python主程序成功运行后,关闭当前exe创建的进程,及启动画面等。可以考虑使用共享文件锁的方式通讯。