SAM 命令桥、Python 与 PYD 扩展 API / SAM Command Bridge, Python, and PYD Extension APIs
本篇解释 GUI 如何将操作交给 SAM Kernel,以及 PYD 如何把 C++ 能力暴露给内嵌 Python。
1. 三层调用模型
GUI DLL
`-- SAMGuiMode/SAMForm 生成 Python 命令字符串
`-- cmdGCommandDeliveryRole
`-- SAM Python/Kernel
+-- mdb 用户级 API
`-- import 业务 PYD
`-- C++ SDK、模型和网格算法GUI DLL 和 PYD 不会因为放在同一目录就自动通信。二者之间通常以 Python 命令、方法参数、返回值和模型更新作为显式协议。
2. 命令投递类
2.1 cmdGCommandDeliveryRole
GUI 到 Kernel/Python 的核心命令桥,采用单例角色设计。
void SendCommand(const QString& command,
cmdGCommandDoneCB* callback = nullptr,
bool writeToReplay = true,
bool writeToJournal = false,
bool waitForReply = true);
cmdCReply* EvalCommand(const QString&);
void SendCliCommand(const QString&,
cmdGCommandDoneCB* = nullptr);
void SyncSendCommand(const QString&,
bool replay = true,
bool journal = false);
void SoftInterrupt();
int OkToSend();
QString CurrentModel() const;
sesGCurrentContext CurrentModelContext() const;
bool HasReply() const;
const ipcReply& GetCmdReply() const;
omuPrimitive* getMethodValue(const omuPrimPath*,
const QString& methodName,
omuArguments& args);
Q_INVOKABLE void onCommand(const QString&,
bool replay,
bool journal);SendCommand():普通命令发送,可附带完成回调。EvalCommand():计算表达式并取得返回对象。SyncSendCommand():同步等待结果,使用不当会阻塞 GUI。writeToReplay:是否记录到 Replay。writeToJournal:是否记录到 Journal。
2.2 cmdGCommandDoneCB
命令完成回调基类:
cmdGCommandDoneCB(cmdGCommandDoneCB* decorator = 0);
virtual void cmdContinue();
void SendCommand(const QString&,
bool replay = true,
bool journal = false);
bool HasReply() const;
ipcReplyCOW GetReply();派生类可重写 cmdContinue(),读取上一条 Reply 后继续业务流程。
2.3 cmdGCommandDeliveryQueue
维护 CLI 命令顺序:
cmdGCommandDeliveryQueue();
void QueueCommand(const QString&,
cmdGCommandDoneCB* = nullptr);3. Python 用户层的责任
SAM 内嵌 Python 2.7。Python 层适合:
- 使用
mdb.models[...]、parts[...]等对象; - 编排可回放、可批处理的业务流程;
- 调用 PYD 提供的 C++ 算法;
- 处理适合脚本表达的参数和循环;
- 形成 CLI、宏和自动化入口。
典型命令:
import YourModule
result = YourModule.calculate(...)在 SAM 命令窗口输入时,不要复制 >>> 提示符,也不要在顶层命令前添加空格。
4. PYD 是什么
PYD 是 Windows 上的 CPython 原生扩展模块。它在文件格式上类似 DLL,但由 Python import 加载,而不是由 Qt Toolset 插件扫描器加载。
PYD 适合:
- 访问 SAM C++ 模型和网格对象;
- 执行大量节点、单元和几何计算;
- 包装已有 C++ 库;
- 将算法注册为 Python 函数、类型或 Fragment;
- 返回 Python 可识别的数值、字符串、元组和接口对象。
本机常用部署位置:
D:\Program Files\SAM\Release\YourModule.pyd5. Python 绑定值体系
5.1 omuPrimitive
所有绑定值的抽象根类型:
virtual QString TypeString() const;
virtual QString AsString(int depth = 0) const = 0;
virtual QString AsRepr() const;
virtual void Accept(omuPrimVisitor*) = 0;
virtual omuPrimitive* Copy() const = 0;
bool IsType(char) const;
bool IsA(typTypeTag) const;5.2 omuInterfaceObj
把具有方法和成员的 C++ 对象暴露给 Python。
typedef omuPrimitive*
(omuInterfaceObj::*methodFunc)(omuArguments&) const;
void DescribeType(const char*,
const methodTable[] = 0,
const memberTable[] = 0);
virtual bool IsMethod(const char*) const;
virtual omuPrimitive* CallMethod(const char* path,
const char* method,
omuArguments& args) const;
virtual omuPrimitive* GetMember(const char*) const;
virtual omuPrimitive* SetMember(const char*,
omuArguments&) const;
virtual cowListString MemberList() const;
virtual cowListString MethodList() const;
bool IsA(const char* type) const;
virtual bool IsStale() const;
virtual void SetStale(bool);5.3 omuArguments
继承 omuPrimTuple,解析位置参数和关键字参数。
void Begin();
void End(QString nesting = "");
void Get(int&);
void Get(double&);
void Get(QString&);
void Get(int&, const QString& keyword);
void Get(double&, const QString& keyword);
void Get(QString&, const QString& keyword);
void Put(int);
void Put(double);
void Put(const QString&);
void Put(omuPrimitive*); // 接管 Primitive 指针
void Put(omuInterfaceObj*); // 借用接口对象指针
bool Error() const;
bool Alternative();所有权差异十分重要。跨 DLL/PYD 错误释放对象可能直接导致崩溃。
5.4 omuMethodCall
构造 Python 方法调用表达式:
omuMethodCall(const QString& object,
const QString& method,
const omuArguments& args);
operator QString() const;5.5 omuPrimNumber
数值 Primitive 基类,提供字符串表示、复制、Visitor 和数据库序列化接口。
5.6 omuPrimString
omuPrimString(const QString&);
omuPrimString(int);
omuPrimString(uint);
omuPrimString(double);
const QString& AsCharPtr() const;
omuPrimString* Slice(int i, int j) const;
void Concat(const omuPrimString&);
int Length() const;5.7 omuPrimTuple
序列/元组包装:
omuPrimTuple(int size, char type = '(');
const omuPrimitive* Index(int i) const;
omuPrimSequence* Slice(int i, int j) const;
void Put(int/double/QString/...);
void Get(int/double/QString/...);
void Begin();
void End(QString nesting = "");
void Optional();
cowListInt AsListInt();
cowListDouble AsListDouble();
cowListString AsListString();6. PYD 模块注册
6.1 pyoModule
表示一个 C++ 实现的 Python 模块。
pyoModule(const char* name,
omuInterfaceObj::methodTable* methods,
ImportEnm autoImport = NO_IMPORT);
void DefineVariable(const char*, int/double/const char*/...);
void DefineConstant(const omuPrimEnumBase&);
virtual void DefineConstants() = 0;
void DefineType(const char* name);
void Import(const char* module);
void ImportFrom(const char* module, const char* variable);
void ImportAs(const char* module, const char* variable);
void DeleteVar(const char* variable);6.2 iniPythonModuleRegistrar
注册模块初始化和结束函数:
void Register(inifunction initialize,
inifunction finalize);6.3 ptsKModelFragment
在现有 Python Model 对象上增加 Part 构造方法。
omuPrimitive* PartConstructor(omuArguments&);
omuPrimitive* PartFromGeometryFile(omuArguments&);
omuPrimitive* PartFromAcis(omuArguments&);
omuPrimitive* PartFromODB(omuArguments&);
omuPrimitive* PartFromExtrude2DMesh(omuArguments&);
omuPrimitive* PartFromMeshMirror(omuArguments&);
omuPrimitive* PartMeshToGeometry(omuArguments&);
omuPrimitive* OrphanMeshPart(omuArguments&);
omuPrimitive* PartFromIges(omuArguments&);
omuPrimitive* PartFromStep(omuArguments&);
omuPrimitive* PartFromSTL(omuArguments&);6.4 ptsKPartFragment
在现有 Python Part 对象上增加方法:
omuPrimitive* Copy() const;
omuPrimitive* CallMethod(const char* path,
const char* method,
omuArguments& args) const;Model Fragment 和 Part Fragment 的挂载层级不同。前者用于 model.method(),后者用于 part.method()。
7. 一个完整业务调用的推荐形式
GUI 槽函数不直接遍历网格,而是发送稳定的 Python 命令:
用户点击
-> Toolset 槽函数
-> 激活 Form/Dialog
-> 收集 Model、Part 和参数
-> 生成 YourModule.operation(...)
-> cmdGCommandDeliveryRole::SendCommand
-> Python import YourModule
-> PYD 访问 Part/Mesh
-> 返回结果或更新模型
-> 回调/Message Area/视口刷新8. 常见问题
import 失败
检查 PYD 文件名、导出模块名、Python 2.7、x64、MSVC Runtime、依赖 DLL 和 sys.path。
import 成功但没有方法
重点检查 methodTable、DefineType()、Fragment 注册和初始化顺序。
GUI 能找到 PYD,但调用崩溃
重点检查 ABI、对象所有权、C++ 异常是否越过模块边界,以及 SAM 对象是否已经失效。
Python 修改了数据但 GUI 没刷新
模型操作和显示更新是两个阶段。需要触发模型通知、Part 场景更新或当前 Scene 刷新。