导语 :在混合编程的世界里,让沉稳厚重的 C++ 拥抱灵活便捷的 Python,是许多开发者的梦想。PythonQt 就是那座连接两者的桥梁。本篇作为系列的第一阶段,将手把手带你跨越最艰难的"第一道坎"------环境搭建与初试啼声。
一、前置认知:PythonQt 是如何工作的?
在动手之前,我们需要理清一个核心概念:**PythonQt 采用的是"嵌入"模式,而不是"扩展"模式。
- 扩展 :用 C++ 写库,编译成
.pyd/.so,然后 Python 去调用。代表:PyQt5, pybind11。 - 嵌入 :C++ 程序是主体,C++ 启动后,在内部开辟一个 Python 解释器,然后执行 Python 代码。代表:PythonQt 。
你可以把 PythonQt 想象成在 C++ 的大厦里建了一个 Python 的"玻璃房"。C++ 可以看到里面,Python 也可以看到外面,而 PythonQt 就是连接两者的对讲机。
二、环境搭建:跨越编译的鸿沟
PythonQt 没有提供预编译的二进制包,这意味着你必须从源码编译它。这是新手最容易放弃的一步,但只要理清逻辑,其实并不难。
2.1 准备依赖
你需要确保系统中有以下三个组件:
- C++ 编译器(MSVC on Windows, GCC/Clang on Linux/Mac)
- Qt 环境 (建议 Qt 5.15 或 Qt 6.x,确保
qmake或cmake在环境变量中) - Python 开发环境 (必须是安装了头文件和静态库的版本,Linux 下通常是
python3-dev,Windows 下安装时勾选 "Download debugging symbols" 和 "Download debug binaries")
2.2 获取源码
bash
git clone https://github.com/MeVisLab/pythonqt
cd pythonqt
2.3 核心步骤:告诉构建系统去哪找 Qt 和 Python
PythonQt 的构建系统依赖两个关键的环境变量。如果这一步没做,编译必报错。
QTDIR:指向你的 Qt 安装根目录(里面应该有lib,include,mkspecs等文件夹)。PYTHON_DIR:指向你的 Python 安装根目录(里面应该有include,libs或lib文件夹)。
Linux/Mac 示例:
bash
export QTDIR=/opt/Qt/5.15.2/gcc_64
export PYTHON_DIR=/usr/local/python3.10
Windows 示例 (CMD):
cmd
set QTDIR=C:\Qt\5.15.2\msvc2019_64
set PYTHON_DIR=C:\Python310
2.4 编译
目前 PythonQt 推荐使用 CMake 构建:
bash
mkdir build && cd build
cmake ..
cmake --build . --config Release -j 8
⚠️ 避坑指南 :在 Windows 上,如果你的 C++ 程序是 Release 模式,Python 也必须是 Release 版本(即正常的 python3.dll)。如果你用 Debug 模式编译 C++,它需要 Python 的 debug 版本(python3_d.dll),这通常需要你自行编译 Python 源码,非常麻烦。新手强烈建议统一使用 Release 模式!
编译完成后,你会在
lib目录下找到libPythonQt.so或PythonQt.dll,这就是我们后续链接的核心库。
三、项目集成:在 C++ 中点亮 Python 的火种
假设你现在用 Qt Creator 或 VS Code 创建了一个空的 Qt Widgets 项目。你需要做以下配置:
3.1 CMake 配置
cmake
cmake_minimum_required(VERSION 3.16)
project(PythonQtStep1)
set(CMAKE_CXX_STANDARD 17)
# 1. 寻找 Qt 和 Python
find_package(Qt6 COMPONENTS Core Gui Widgets REQUIRED)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
# 2. 假设你将编译出的 PythonQt 放在了第三方库目录
set(PYTHONQT_DIR ${CMAKE_SOURCE_DIR}/3rdparty/PythonQt)
add_library(PythonQt SHARED IMPORTED)
set_target_properties(PythonQt PROPERTIES
IMPORTED_LOCATION ${PYTHONQT_DIR}/lib/libPythonQt.so # Windows下改为.dll
INTERFACE_INCLUDE_DIRECTORIES ${PYTHONQT_DIR}/include
)
add_executable(Step1 main.cpp)
# 3. 链接库
target_link_libraries(Step1 PRIVATE
Qt6::Core Qt6::Gui Qt6::Widgets
PythonQt
Python3::Python # 必须链接 Python 的 C API 库
)
四、代码实战:Hello, Python from C++!
一切就绪,让我们编写第一行代码。我们的目标是:在 C++ 中启动 Python 解释器,并让它打印一句话。
4.1 完整代码
cpp
#include <QApplication>
#include <QDebug>
#include <PythonQt.h> // PythonQt 核心头文件
#include <PythonQt_QtAll.h> // 可选:注册所有 Qt 类到 Python
int main(int argc, char** argv)
{
QApplication app(argc, argv);
// ★ 第 1 步:初始化 PythonQt 引擎
// PythonQt::RedirectStdOut 表示将 Python 的 print 输出重定向到 C++
PythonQt::init(PythonQt::RedirectStdOut);
// ★ 第 2 步:获取 Python 的 __main__ 模块上下文
// 这相当于你打开终端输入 python 后进入的那个环境
PythonQtObjectPtr mainModule = PythonQt::self()->getMainModule();
// ★ 第 3 步:执行 Python 代码!
qDebug() << "--- C++ 开始调用 Python ---";
// 方式 A:执行单行/多行字符串
mainModule.evalScript("print('Hello from Python inside C++!')");
// 方式 B:执行简单的数学计算
mainModule.evalScript("result = 2024 * 2");
// ★ 第 4 步:从 Python 拿回数据
QVariant value = mainModule.getVariable("result");
if (value.isValid()) {
qDebug() << "C++ 读取到的 Python 计算结果:" << value.toInt(); // 输出 4048
}
// ★ 第 5 步:清理(可选,程序退出时自动清理)
// PythonQt::cleanup();
return app.exec(); // 进入 Qt 事件循环
}
4.2 代码深度解析
让我们拆解上面代码中最重要的几步:
PythonQt::init()
这行代码在 C++ 进程中启动了 CPython 解释器。如果你不调用它,任何 PythonQt 的操作都会崩溃。参数RedirectStdOut非常实用,默认情况下 Python 的print会输出到控制台,加上这个参数后,Python 的输出会被 PythonQt 拦截,我们就可以在 C++ 端处理它了(下一篇会讲怎么把输出显示到 UI 上)。PythonQtObjectPtr mainModule
Python 中万物皆对象,模块也是。getMainModule()返回的是一个智能指针,它指向 Python 的__main__空间。你在 C++ 里定义的所有变量、函数,都要放在这个空间里才能互相交互。evalScript()
这是执行 Python 代码最直接的方法。你可以把它理解成 C++ 在向 Python 终端逐行敲击键盘。getVariable()
这就是 C++ 伸出触角,从 Python 环境中"提取"数据的魔法。它返回一个QVariant,因为 Python 是动态类型,C++ 在拿到数据时并不知道它是什么类型,所以用QVariant包一层,然后再用toInt()或toString()转换。
五、阶段验收与排错
运行程序,如果你在控制台看到了:
text
--- C++ 开始调用 Python ---
Hello from Python inside C++!
C++ 读取到的 Python 计算结果: 4048
恭喜你!你已经成功在 C++ 的心脏中点亮了 Python 的火种,第一阶段圆满完成!
🚨 新手常见报错排查
如果没跑通,请对照以下常见问题:
-
报错:****
ImportError: No module named xxx或找不到site-packages-
原因:C++ 启动 Python 时,找不到你系统安装的第三方库路径。
-
解决 :在
init之后,手动把 Python 的路径加进去:cppmainModule.evalScript("import sys; sys.path.append('C:/Python310/Lib/site-packages')");
-
-
崩溃:程序启动直接闪退,报
PYTHONPATH或 DLL 找不到- 原因:C++ 编译时链接的 Python 版本,与运行时系统环境变量指向的版本不一致。
- 解决 :确保 CMake 中
find_package(Python3)找到的版本,和你在环境变量中配置的版本一致。
-
乱码:Python 打印中文变乱码
- 解决 :在
evalScript的第一行加上# -*- coding: utf-8 -*-,或者在 C++ 端使用QString::fromUtf8()处理返回值。
- 解决 :在
下一步预告
现在,你已经能让 C++ 执行一段固定的 Python 代码了。但在真实业务中,我们不可能把代码硬编码在 C++ 里,我们需要从外部加载 .py 文件,并且需要 C++ 和 Python 之间传递复杂的业务数据。
在 第二阶段:数据与模块的交互 中,我们将探讨:
- 如何用
evalFile()执行外部 Python 脚本? - 如何在 C++ 和 Python 之间自由穿梭
int,string甚至List和Dict? - 如何在 C++ 中调用 Python 的函数并获取返回值?