目录
前言
Python源码以及VS2017的准备 Python源码 VS2017 Windows下编译Python源码 编译命令 编译步骤 编译后的配置 添加注册表 Win10中编译Python源码小结 Windows中C++调用Python代码绘制等高线图 CMakeLists.txt文件如下: Python代码如下: CPP文件: 运行结果 Linux中编译Python源码
前言
距离上次推送已经快一个月了,这段时间一直在准备开题。说起来我自己都不太相信,我竟然一个多学期没看过文献…….今年5月底才开始看文献,中间课题方向也被换了好几次。今天下午刚开完组会,又得改一堆。
前阵子我老师要我用C++做个软件(还没做完),其中需要有一项功能是显示一个等高线图,但是没找着可以画等高线的C++库,自己开发太麻烦了(主要自己不会)。画等高线用MATLAB很容易就能画出来,Python的matplotlib库画等高线图也很简单。所以就考虑C++调用MATLAB,或者C++调用Python。
如果用C++调用MATLAB的话,打包后还需要MATLAB的运行时库才行,而且这个还很大,好像安装好后有几个G,而且美国又在对中国限制matlab,所以打算放弃C++调用MATLAB。
C++调用Python的话,坑会比较多,一般低级语言调用高级语言的坑都比较多。需要编译Python的源代码(Python的源码基本都是C文件),如果不编译的话可能会无法调用和Python有关的.dll文件或者.pyd文件以及相关的.lib库文件。这些都是经过编译器编译出来的,必须要保证编译器是一致的才能使用,还有需要明确是32位的还是64位的。
Note:本次推送中有部分内容是取自于我学习 夏曹俊
老师课程时所记的笔记,夏曹俊老师的课程质量还是很好的,有兴趣的可以上51cto或者网易云课堂搜索一下看看。
Python源码以及VS2017的准备
Python源码
可以直接去官网:www.python.org 上下载
解压后打开其中的目录结构如下:
VS2017
需要用VS2017来编译Python源码,VS2015得单独安装Win10开发工具,更高版本的就不清楚了,反正我还是建议使用VS2017,我这有一个企业版的2017:
具体的安装可以看看我之前写的VS2017编译Qt源码的那篇推送:搭建VS2017_QT_MATLAB开发环境。
实际上我主要是需要VS2017的nmake这个编译器,这个VS2017的编译器可以直接被我的CLion检测到,但是我的CLion找不到VS2015编译器在哪,自己手动加了也不行。
Windows下编译Python源码
编译命令
build -c Release -p x64orbuild -c Release -p win32orbuild -c Debug -p x64orbuild -c Debug -p win32
前两个都是用来编译Release版本的,一个是x64(amd64)平台的,一个是win32平台的。我编译的都是x64的。
后两个都是用来编译Debug版本的,一个是x64(amd64)平台的,一个是win32平台的。我编译的都是x64的。
-c 表示编译模式,-p是指平台(platform)
下面仅以build -c Release -p x64
为例。
编译步骤
先把下面的这个目录添加到环境变量中,因为编译的过程中可能会用到Win10的开发工具,为了能让编译器找到它们,所以需要添加到路径中:
C:\Program Files (x86)\Windows Kits\10\bin\10.0.17134.0\x64
上面的这个10.0.17134.0
只是win10开发工具的一个版本,不一定要和我的一致,如果有多个版本,选择最新的就行。
进入PCbuild文件夹中,这是Windows环境的源码目录,如下:
可以看到这里面都是VS的项目文件
在当前文件夹中打开WindowsPowerShell,或者打开CMD用cd命令切换到PCbuild文件夹下,在其中输入下面这行编译指令来编译64位的Python:
./build -c Release -p x64
编译过程的第一步是去下载编译依赖的库和文件,一般情况下下载都会出点错吧,毕竟是从国外的服务器上下载的。这个下载下来的东西都放在源码路径/externals
文件夹下:
这个externals文件夹开始时是不存在的,执行编译指令后才会被创建
这个externals文件,我已经上传到云盘里了,如果有同学上面没下载下来的话可以下载我上传的这个,解压后放到下载的源码路径下,记得不要改externals这个名字:
链接:https://pan.baidu.com/s/1EgPjkcfFtTf1jCC\_geYF6g
提取码:depz
拷贝好后再重新编译就能成功了,如下图:
Note:build -c Release -p x64
编译输出在./PCbulid/amd64
文件夹中,这个文件夹中就有一个python.exe,如下图
编译后的配置
- 添加一个
PYTHONHOME
系统变量,值为源码的家目录,如下图(我把源码拷贝到C盘了)
在系统环境变量中把编译出来的python.exe的路径添加进去
还有pip的路径也添加进去,你的pip可以用externals/pythonx86/tools/Scripts中的,所以还应该把这个路径添加到环境变量中
Note:如果有多个版本的Python的话,在用到Python.exe时,使用的是PATH环境变量中最上面的那个版本的Python。
添加注册表
在用Python做拓展库时,它拓展库可以打成一个安装包,这个安装包会通过注册表来判断你的系统有没有装这个版本的Python。
下面给出一段Python代码来添加注册表:
import sysfrom winreg import *version = sys.version[:3]installpath = sys.prefixregpath = "SOFTWARE\\Python\\Pythoncore\\%s-64\\" % (version)installkey = "InstallPath"pythonkey = "PythonPath"pythonpath = "%s;%s\\Lib\\;%s\\DLLs" % (installpath, installpath, installpath)print(version)def RegisterPy(): try: reg = OpenKey(HKEY_CURRENT_USER, regpath) except EnvironmentError as e: try: reg = CreateKey(HKEY_CURRENT_USER, regpath) SetValue(reg, installkey, REG_SZ, installpath) SetValue(reg, pythonkey, REG_SZ, pythonpath) CloseKey(reg) except: print("---Python ", version, " is not registered!") return if (QueryValue(reg, installkey) == installpath) and (QueryValue(reg, pythonkey) == pythonpath): CloseKey(reg) print("---Python ", version, " is already registered!") return CloseKey(reg)if __name__ == "__main__": RegisterPy()
Note: 一定要用你编译出来的那个版本的Python来运行这个代码!
Win10中编译Python源码小结
到此,在Win中的编译就算完事了,上面说了这老大一堆,实际总结下来也没多少
python官网下载源码,下载VS2017
把Windows Kits中的那个x64目录添加到路径(具体路径往回翻一下看看)
进入源码目录下的PCbuild目录中,在当前目录中打开Power Shell或者打开cmd然后cd到PCbuild目录中
执行
./build -c Release -p x64
编译出64位Release版本的Python,python.exe在PCbuild目录中的amd64下面。(可以先下载我上传的那个externals文件夹,解压后放到源码根目录下)编译完成后添加PYTHONHOME系统变量,并把PCbuild/amd64这个目录添加到环境变量中
添加注册表
如果想编译32位的只要把x64改成win32就行。过程都一样,那个Python代码中的64也改成32。
Windows中C++调用Python代码绘制等高线图
这部分不涉及C++与Python之间的参数传递,仅仅是C++调用py文件,py文件也没有编译。有关C++与Python之间的数据传递,后期会做一些推送来记录。
把Python源码目录中的Include拷贝到程序目录中
把Python源码目录中的PC/pyconfig.h拷贝到程序目录中(放到上面的Include里面)
把PCbuild/amd64/python37.lib,还有相应的python37.dll也拷贝到程序目录中。除此之外还需要从这个amd64下面拷贝出:_bz2.lib、_contextvars.lib、_socket.lib、_tkinter.lib、select.lib、unicodedata.lib,以及对应的.pyd文件(pyd文件是Python编译的动态库也要和.exe放一起)。实际上你可以把amd64下的整个都拷贝走,这样更省事,就是打包后的文件大一点,事先我们也不知道需要哪些库,但是如果缺少库的话,运行结果会提示缺少那个,此时你再去amd64中找**(下同)**。
把Python源码目录中的Lib目录也整个拷贝走,如果不怕麻烦可以挨个删掉用不上的库。
采用CLion创建的项目,项目文件结构如下:
我把python相关的lib库文件和头文件都放在Python子文件夹中,把pyd以及dll文件以及py源代码都和.exe文件放在一起。
CMakeLists.txt文件如下:
cmake_minimum_required(VERSION 3.15)project(demo8_2)set(CMAKE_CXX_STANDARD 14)set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)# CLion这无法用相对路径# 把Armadillo的头文件的目录包括进来(固定的)include_directories("D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/Include")# 把Python lib 目录路径 链接进来link_directories("D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python")# 链接 Python liblink_libraries( "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/_bz2.lib" "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/_contextvars.lib" "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/_socket.lib" "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/_tkinter.lib" "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/python37.lib" "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/select.lib" "D:/PersonalAllAboutStudy/Programming/CLion_Python_Demo/demo8_2/Python/unicodedata.lib")add_executable(main main.cpp)
不可照搬,起码得改个路径啥的!
Python代码如下:
import numpy as npimport matplotlib.pyplot as pltimport matplotlib as mpl# 隐藏matplotlib的工具栏mpl.rcParams['toolbar'] = 'None'# 修改matplotlib窗口的图标thismanager = plt.get_current_fig_manager()# thismanager.window.wm_iconbitmap("could.png") # 试了,看不到图标thismanager.window.wm_iconbitmap("") # 换成Tkinter的羽毛把# 计算x,y坐标对应的高度值def f(x, y): return (1-x/2+x**5+y**3) * np.exp(-x**2-y**2)def main(): # 生成x,y的数据 n = 256 x = np.linspace(-3, 3, n) y = np.linspace(-3, 3, n) # 把x,y数据生成mesh网格状的数据,因为等高线的显示是在网格的基础上添加上高度值 X, Y = np.meshgrid(x, y) # 填充等高线 plt.contourf(X, Y, f(X, Y), 20, cmap=plt.cm.hot) # 添加等高线 C = plt.contour(X, Y, f(X, Y), 20) plt.clabel(C, inline=True, fontsize=12) # 显示图表 plt.show()if __name__ == '__main__': main()
CPP文件:
#include <iostream>#include <Python.h>#include <exception>using namespace std;int main(int argc, char *argv[]){ cout << "C++ call Python" << endl; //设置Python的Home路径 Py_SetPythonHome(L"./"); //python解释器初始化 Py_Initialize(); try { int re = 0; //////////////////////////////////////////////////// // 执行python文件 char *filename = "a.py"; FILE *fp = fopen(filename,"r"); if (!fp) { throw exception("open file failed!"); } re = PyRun_AnyFile(fp, filename); if (re != 0) { PyErr_Print(); throw exception("PyRun_AnyFile failed!"); } //清理python Py_Finalize(); } catch (exception &ex) { cout << ex.what() << endl; Py_Finalize(); } getchar(); getchar(); return 0;}
运行结果
Linux中编译Python源码
这块我是直接用我的云服务器来演示的,就不写程序来演示了,直接给出编译步骤,也不截图了。
安装依赖项
命令行中分别输入以下几行
sudo apt-get install g++ make gitsudo apt-get install libbz2-dev libncurses5-dev libgdbm-dev liblzma-dev libsqlite3-dev libssl-dev libreadline6-devsudo apt-get install libffi-dev zlib*
下载Python源码并解压
这部分同上面一样,略,下面只给出解压命令
tar -xvf xxxx.tar.xz
编译python
进入源码目录,分别执行下面几行指令
./configure -h./configure --enable-sharedmake -j2make install
其中,make -j2
是采用2个线程编译
覆盖原来的Python
还是在Python源码目录中,
cp libpython3.7m.* /usr/libcp /usr/local/bin/python3.7 ./usr/bin/python
验证下Python的版本
命令行中输入:
python -V
本文分享自微信公众号 - MatlabGUI QtCPP等学习记录(ASparkleSubscription)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。