- 音视频
- 视频处理
- 图形学
- 桌面应用
【免费下载链接】Natron
Open-source video compositing software. Node-graph based. Similar in functionalities to Adobe After Effects and Nuke by The Foundry.
本文围绕 Natron 官方 Python 教程 getSelectedNodesClass.rst 展开:演示如何编写一个 Python 脚本,遍历当前节点图中所有被选中的节点,读取每个节点的 class(即 plugin ID)并输出到控制台。文章在完整保留原教程脚本与运行方式的基础上,结合 Natron 仓库源码,逐层拆解natron.getGuiInstance()、app.getSelectedNodes()、node.getPluginID()三个 API 背后的 C++ 实现、过滤规则与 pluginID 命名约定,并说明脚本如何通过addMenuCommand注册为菜单命令或在 Script Editor 中直接执行。
一、教程目标:为什么需要读取节点的 Class
在 Natron 的节点图中,每个节点本质上是一个效果实例(Effect Instance),它由某个插件(OpenFX 插件或内置插件)驱动。节点在界面上显示的 Label(如 “Blur”“Grade”)只是用户可改的显示名,而真正标识“这个节点是哪一类效果”的字符串是plugin ID,例如net.sf.openfx.MergePlugin、fr.inria.openfx.ReadPNG。
在实际脚本开发中,读取选中节点的 class 是最基础的“节点类型识别”手段,典型场景包括:
- 批量操作前先确认选中节点的类型(只对 Read/Write 节点执行某些逻辑);
- 按 plugin ID 对选区做统计、重命名或参数批量修改的前置判断;
- 与文档中姊妹教程 getSelectedNodesLabel.rst(获取选中节点的 Label)配合,实现“Label 用于展示、pluginID 用于逻辑判断”的完整取数流程。
两个教程均收录在开发者文档的 Python 节点操作章节 nodes.rst 中。
二、完整脚本解读
原教程给出的完整脚本如下(保持原文不变,可直接复制使用):
import os import NatronEngine from NatronGui import * def getSelectedNodesClass(): # get current Natron instance running in memory app = natron.getGuiInstance(0) # get selected nodes selectedNodes = app.getSelectedNodes() # cycle through every selected node for currentNode in selectedNodes: # get current node class currentID = currentNode.getPluginID() # write node class in console os.write(1,'\n' + str(currentID) + '\n')逐行说明:
| 代码 | 作用 |
|---|---|
import NatronEngine/from NatronGui import * | 引入引擎与 GUI 的 Python 绑定模块。NatronGui提供natron、app等全局对象 |
app = natron.getGuiInstance(0) | 获取内存中正在运行的第 0 号 Natron 应用实例(一个实例对应一个打开的工程/项目) |
selectedNodes = app.getSelectedNodes() | 获取当前节点图中被选中的所有节点,返回一个Effect对象列表 |
currentNode.getPluginID() | 读取该节点所属插件的 ID 字符串,也就是教程中所说的 node class |
os.write(1, ...) | 向文件描述符 1(标准输出)写一行,结果显示在 Natron 的 Console |
需要注意两点:
getGuiInstance(0)中的0是应用实例索引。Natron 可以同时打开多个工程,每个工程是一个独立的 App Instance,索引从 0 开始;- 脚本在 Natron 内置 Python 环境中运行时,
app、app1等预定义变量通常已指向具体实例,但教程选择显式通过natron.getGuiInstance(0)获取,可避免对隐式变量的依赖。
三、源码级解析:三个核心 API 的底层实现
3.1natron.getGuiInstance(idx):定位 GUI 应用实例
该 API 对应 C++ 侧的PyGuiApplication::getGuiInstance,实现在 PyGlobalGui.h 中:
GuiApp* getGuiInstance(int idx) const { AppInstancePtr app = appPTR->getAppInstance(idx); if (!app) { return 0; } GuiAppInstancePtr guiApp = std::dynamic_pointer_cast<GuiAppInstance>(app); if (!guiApp) { return 0; } return new GuiApp(guiApp); }从源码结构看,它先用索引向 AppManager 查询AppInstance,再将其动态转型为GuiAppInstance(仅 GUI 模式下的实例才具备 GUI 能力),最后包装成返回给 Python 的GuiApp对象。因此可以推断:在纯命令行渲染(无 GUI)环境下,此调用会因转型失败而返回空。
3.2app.getSelectedNodes():选择集从何而来,以及隐含的过滤规则
Python 端的GuiApp::getSelectedNodes实现在 PyGuiApp.cpp 中:
std::list<Effect*> GuiApp::getSelectedNodes(Group* group) const { std::list<Effect*> ret; NodeGraph* graph = 0; if (group) { // 若传入 Group 参数,则取该 Group 内部节点图的选择集 ... } else { graph = getInternalGuiApp()->getGui()->getLastSelectedGraph(); } if (!graph) { graph = getInternalGuiApp()->getGui()->getNodeGraph(); } ... const NodesGuiList& nodes = graph->getSelectedNodes(); for (NodesGuiList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { NodePtr node = (*it)->getNode(); if ( node->isActivated() && !node->getParentMultiInstance() ) { ret.push_back( new Effect(node) ); } } return ret; }这段实现揭示了教程脚本之外的三个关键事实:
- 目标图的确定:不传参数时,API 优先取
getLastSelectedGraph()(用户最近交互的节点图),取不到时回退到主节点图getNodeGraph(); - 支持可选的 group 参数:
getSelectedNodes(group)可以获取某个 Group 内部节点图的选择集,教程示例为简化起见未使用该参数; - 结果经过过滤:只有满足
node->isActivated()(节点处于激活状态)且!node->getParentMultiInstance()(不是 MultiInstance 的子实例)的节点才会进入返回列表。这意味着用该 API 拿到的选择集天然剔除了“被停用”的节点,也避免了对多实例展开后的子节点重复计数。
而真正的选择集维护在 GUI 层的节点图对象中,见 NodeGraph.cpp:
const std::list<NodeGuiPtr> & NodeGraph::getSelectedNodes() const { return _imp->_selection; }即选择集是NodeGraph私有实现中的一份NodeGui列表,随用户点选、框选实时更新。Gui::getSelectedNodes()(Gui.cpp)则只是转发给当前的节点图控件。
3.3node.getPluginID():class 字符串的真正来源
教程中currentNode是Effect包装对象,其getPluginID()定义在 PyNode.cpp:
QString Effect::getPluginID() const { return QString::fromUtf8( getInternalNode()->getPluginID().c_str() ); }它只是把内部EffectInstance::getPluginID()(在 EffectInstance.h 中声明为纯虚函数,由各节点类型分别实现)返回的std::string转成QString交给 Python。也就是说,class 字符串的最终出处是每个插件注册时携带的标识。
3.4 plugin ID 的命名约定
EffectInstance.h 顶部集中定义了一组常用插件 ID 宏,展示了两类命名风格:
#define PLUGINID_OFX_MERGE "net.sf.openfx.MergePlugin" #define PLUGINID_OFX_TRACKERPM "net.sf.openfx.TrackerPM" #define PLUGINID_OFX_READOIIO "fr.inria.openfx.ReadOIIO" ... #define PLUGINID_NATRON_VIEWER (NATRON_ORGANIZATION_DOMAIN_TOPLEVEL "." NATRON_ORGANIZATION_DOMAIN_SUB ".built-in.Viewer") #define PLUGINID_NATRON_DISKCACHE (NATRON_ORGANIZATION_DOMAIN_TOPLEVEL "." NATRON_ORGANIZATION_DOMAIN_SUB ".built-in.DiskCache") #define PLUGINID_NATRON_GROUP (NATRON_ORGANIZATION_DOMAIN_TOPLEVEL "." NATRON_ORGANIZATION_DOMAIN_SUB ".built-in.Group")- 第三方/社区 OpenFX 插件沿用反向域名风格,如
net.sf.openfx.*、fr.inria.openfx.*、net.fxarena.openfx.*; - Natron 内置节点则使用
org.natron.built-in.*前缀拼接生成。
因此在脚本中做类型判断时,推荐直接比较 pluginID 字符串(例如currentID == "net.sf.openfx.TransformPlugin"),而不是解析 Label 文本,因为 Label 可被用户随意修改,pluginID 才是稳定的类型标识。
四、脚本的两种运行方式
原教程给出了两种落地方式,下面分别结合仓库源码补充细节。
4.1 方式一:保存为 .py 文件,并用 addMenuCommand 注册为菜单命令
把getSelectedNodesClass函数保存到一个.py文件(该文件位于 Natron 的脚本搜索路径中会被自动导入),然后在启动脚本initGui.py中调用natron.addMenuCommand注册菜单项。参考仓库内完整的 initGui.py 示例 initGui.py,其最后一行展示了带快捷键的注册写法:
natron.addMenuCommand("Inria/Scripts/IconViewer","createIconViewer",QtCore.Qt.Key.Key_L,QtCore.Qt.KeyboardModifier.ShiftModifier)对应本教程脚本的注册方式为(第二个参数是启动脚本中可调用到的函数名):
natron.addMenuCommand("Scripts/PrintSelectedNodesClass", "getSelectedNodesClass")addMenuCommand在 C++ 侧有两个重载,实现在 PyGlobalGui.h:
// 仅菜单项,无快捷键 void addMenuCommand(const QString& grouping, const QString& pythonFunctionName) { appPTR->addCommand(grouping, pythonFunctionName.toStdString(), (Qt::Key)0, Qt::NoModifier); } // 菜单项 + 快捷键(快捷键可通过 Natron 的快捷键编辑器二次修改) void addMenuCommand(const QString& grouping, const QString& pythonFunctionName, Qt::Key key, const Qt::KeyboardModifiers& modifiers) { appPTR->addCommand(grouping, pythonFunctionName.toStdString(), key, modifiers); }关于grouping参数的取值,文档 userCommands.rst 说明:grouping 是斜杠分隔的分组路径(如"Inria/Scripts/IconViewer"),最终映射为菜单栏上的层级路径;且该函数只能在启动脚本(init.py / initGui.py)中调用,运行期调用不产生任何菜单项——“This is not a dynamic function and will not create menu entries on the fly.”
这里还有一个重要的时序约束:启动脚本在任何 App Instance(工程)创建之前执行(见 startupscripts.rst),因此不能在 initGui.py 里直接执行依赖app变量的语句;正确做法是像本教程一样只定义函数,等用户点菜单时再执行,届时app已由运行环境提供。initGui.py 示例中的createIconViewer函数正是这一模式的范例:函数体内使用全局app变量,并通过onProjectCreatedCallback回调在新工程创建时被触发。
说明:原教程文档中“initGuy.py”为笔误,仓库中的实际文件名是
initGui.py,二者对应同一份启动脚本机制。
4.2 方式二:在 Natron 的 Script Editor 中直接执行
更轻量的调试方式是:把整个脚本粘贴到 Natron 界面的 Script Editor(Python 脚本面板)中,并在末尾追加一行调用:
getSelectedNodesClass()执行后,Console 中会按行打印每个选中节点的 plugin ID,例如:
net.sf.openfx.TransformPlugin net.sf.openfx.GradePlugin fr.inria.openfx.ReadPNG这种方式适合开发阶段快速验证:在节点图中框选若干节点 → 运行脚本 → 查看 Console 输出。确认无误后再走方式一注册为正式菜单命令。
五、扩展建议与常见坑
- 输出通道:
os.write(1, ...)直接写标准输出,等价于在 Console 面板打印。若想让信息以醒目方式呈现,可改用natron.informationDialog(title, message)等对话框 API(见 PyGlobalGui.h 中的informationDialog/warningDialog/errorDialog),但批量打印时 Console 方式更合适。 - 多工程场景:
getGuiInstance(0)固定取第 0 个实例。若操作对象不是第 0 个工程,应改用 Natron 提供的app、app1… 变量或对应索引,避免读错工程的选择集。 - 被过滤的节点:由于
GuiApp::getSelectedNodes会跳过未激活节点(见 PyGuiApp.cpp),如果“选区里有节点但脚本没打印它”,先检查该节点是否处于被停用(inactive)状态。 - Group 内部节点:需要读取 Group 内部节点图的选择时,传入对应的 Group 对象:
app.getSelectedNodes(group),底层会定位到该 Group 的内部NodeGraph。 - 与 Label 的取舍:
getLabel()(姊妹教程 getSelectedNodesLabel.rst)适合人类可读的输出;getPluginID()适合程序判断。两者结合可输出形如Label -> pluginID的映射,方便排查复杂节点图。
六、小结
本教程虽然只有二十多行代码,却覆盖了 Natron Python 绑定的三条主链路:natron.getGuiInstance(idx)从 AppManager 取出 GUI 实例 →app.getSelectedNodes()从NodeGraph的选择集构建并过滤Effect列表 →Effect.getPluginID()读取插件类型标识。掌握这套“实例 → 选择集 → 节点属性”的取数模式后,替换最后的属性调用(Label、参数、连接等)即可衍生出绝大多数节点图自动化脚本。
相关源码与文档索引:
- 教程原文:getSelectedNodesClass.rst
- 姊妹教程:getSelectedNodesLabel.rst
- 菜单命令机制:userCommands.rst、startupscripts.rst
- 启动脚本完整示例:initGui.py
- 关键实现:PyGlobalGui.h、PyGuiApp.cpp、NodeGraph.cpp、PyNode.cpp、EffectInstance.h
- 音视频
- 视频处理
- 图形学
- 桌面应用
【免费下载链接】Natron
Open-source video compositing software. Node-graph based. Similar in functionalities to Adobe After Effects and Nuke by The Foundry.
相关推荐
Natron 节点图脚本实战:用 Python 批量读取 Roto 节点条目标签
Natron 节点图脚本实战:用 Python 批量读取 Roto 节点条目标签 本篇技术指南围绕 Natron 官方开发文档中的 getRotoItemsLa
音视频视频处理图形学桌面应用Natron 中 G'MIC Distort Lens 畸变镜头节点完整指南:参数详解与节点图实战
Natron 中 G'MIC Distort Lens 畸变镜头节点完整指南:参数详解与节点图实战 导读 G'MIC Distort Lens(插件标识符 eu
音视频视频处理图形学桌面应用Natron G'MIC 节点详解:Equation Plot Y=f(X) 方程绘图节点实战指南
Natron G'MIC 节点详解:Equation Plot Y=f X 方程绘图节点实战指南 本文聚焦 Natron 中 G'MIC 分组下的 Equati
音视频视频处理图形学桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考