最近的NDK开发涉及到了动态input及动态knobs的问题。

开发需求如下:建立一个节点,该节点能获取每一个input上游的inputframerange信息。

具体下来就是:需要Node的input可以不断增加,而不是固定的几个;而knobs的数量也与input数量同步。

查了nuke提供的开发工具,动态input数量的问题已经解决了,本文主要谈DynamicKnobs的机制。

DynamicKnobs.cpp的机制相对较复杂,涉及到三个方面:

一:

void DynamicKnobs::knobs(Knob_Callback f)
{
Bool_knob(f, &_showDynamic, "show_dynamic", "Show Dynamic"); //add a bool knob
SetFlags(f, Knob::KNOB_CHANGED_ALWAYS); //give it a description //If you were creating knobs by default that would be replaced, this would need to
//be called to create those.
//_numNewKnobs = add_knobs(addDynamicKnobs, this->firstOp(), f); //Call the callback manually this once to ensure script loads have the appropriate knobs to load into.
if(!f.makeKnobs())
DynamicKnobs::addDynamicKnobs(this->firstOp(), f);
}

首先需要理清什么是Knob,在ndk文档中有明确解释,原文如下:

Knobs in NUKE manage the UI elements seen in the param panel and viewer opengl handles, along with the data storage responsible for reading and writing to the NUKE script file。

译文如下:

Nuke中的Knobs管理参数面板中的UI元素和viewer中的opengl操作手柄,跟数据存储机制一起负责读写Nuke工程文件。

理清knobs的功能之后,我们再来研究这段代码。

在这段代码中我们看到先建立了一个名为show_dynamic的Bool_knob属性,下一行利用了SetFlags方法设置了一个很重要的标记,该标记表示该行之后建立的knob都会被视作动态knob,后来添加的knob都会被添加到该Flag之后。
在往下看,add_knobs会在该节点初始化的时候就调用addDynamicKnobs这个callback来建立knob,这个knob会被建立在firstOp()上,即第一个Op实例上,当然Knob_Callback依然还是f。add_knobs方法的返回值是通过addDynamicKnobs所建立起knob的数量。该返回值会存储在_newNewKnobs上。

if(!f.makeKnobs())的意思是判断当前是否有建立knob的行为,如果没有这个建立knob的行为,就会自动调用addDynamicKnobs方法。

通过以上代码就会建立一个自动创建knob并区分哪些是新knob那些是必须保留的机制。

实际上我们也发现了,Knobs是通过callbacks来与Nuke中其他组件进行交互访问的。理解这一点至关重要。

二:

void DynamicKnobs::addDynamicKnobs(void* p, Knob_Callback f)    //click the bool knob,show the slide knob.
{
if(((DynamicKnobs*)p)->getShowDynamic()) {
Float_knob(f, ((DynamicKnobs*)p)->getDynamicKnobStore(), "dynamic_knob", "Dynamic Knob");
}
}

该方法非常简单,getShowDynamic可以获得show_dynamic这个knob的当前值,如果为真,就会创建一个新的knob。该方法会在下一个方法中被调用。

三:

int DynamicKnobs::knob_changed(Knob* k)
{
if(k==&Knob::showPanel || k->is("show_dynamic")) {
_numNewKnobs = replace_knobs(knob("show_dynamic"), _numNewKnobs, addDynamicKnobs, this->firstOp());
return 1;
}
return NoIop::knob_changed(k);
}

关于knob_changed(Knob* k)方法ndk开发文档中也有解释,原文如下:

int Op::knob_changed(Knob* )[virtual]

Whenever the user moves a Knob this is called with a pointer to the Knob, on one of the Ops controlled by that Knob. The purpose is to automatically enable/disable or set the values of other knobs.

Do not assume this is called on all instances of your Op! It will not be in cases of clones or multiple frame numbers in Nuke. So storing results in this call is wrong! To get values out of knobs you must either rely on them being stored and validate() being called, or you must ask for them using knob(name)->value().

You must return non-zero if you do anything. Returning zero indicates that there is no need to call this on the same knob again. Base class returns zero always.

Reimplemented in DD::Image::DrawIop, DD::Image::GeoOp, DD::Image::NukeWrapper, and DD::Image::ReadGeo.

Referenced by DD::Image::NukeWrapper::knob_changed().

译文如下:

用户调整某knob参数的时候,指向该knob的指针会在一个被该knob控制的op上调用knob_changed方法。这个机制的目的是通过该knob来控制其他knob的值。

不要假定认为这个方法在所有op实例中被调用,在克隆和多帧动画的情况下是不会被调用的,即该方法只对当前帧当前op中的参数调整生效。所以也不要在这个方法中存储任何值,在没有knobs的情况你要获得值你确保knobs的值被存储还有validata()方法被调用。或者你也可以通过knob(name)->value()方法来获得knob的值。

无论做什么事都不要返回零值。返回零值就表示在这个knob上之后都不需要再调用knob_changed方法了。需要注意,这个方法的基类只返回零值。

通过这段解释,我们很容易了解knob_changed方法的机制,首先该方法需要一个Knob类的参数k,该方法会监听所有knob值改变的信息。所以需要通过if语句来确定一个条件,即当show_dynamic这个knob参数发生变化,或者knob在面板上显示的时候。当以上两个条件任意成立一个的时候就会触发内部代码块。首先调用replace_knobs()方法来调用addDynamicKnobs()方法。

在这里详细解释下replace_knobs()方法的机制,原文如下:

int Op::replace_knobs ( Knob afterthis,
    int  n,
    void(*)(void *, Knob_Callback)  f,
    void *  v,
    const char *  fileExt = 0 
  )    

Change the set of knobs this node has, by deleting n knobs after the afterthis knob, then inserting new knobs produced by calling the function f. The return value is the number of knobs created by f, which you probably want to save and pass to the next call.

If n is zero or negative then this only creates knobs. If f is null then this only destroys knobs.

Currently the old knobs are completly destroyed. Future versions may try to match up the new knobs with the old ones and preserve the values and widgets.

Your knobs() function should also call f by using add_knobs(). You also need to pick a knob that controls the rest of the knobs and set the Knob::KNOB_CHANGED_ALWAYS flag on it so that you can change the set of knobs on any changes.

译文如下:

通过删除afterthis之后的n个节点,再插入回调函数f创建的新knobs来改变当前Node的knobs集合。该函数会返回新创建knobs的数量。

如果n是零值或者负值,replace_knobs方法就只会创建knobs,如果回调函数f为空,那么replace_knobs就只会删除指定knobs。

一般来说旧的knobs被删除之后,新版本的的node会尝试将新knobs与旧knobs匹配起来,确保值与窗口属性等保持一致。

你的knobs方法的定义中也应该用add_knobs()方法去调用f,你也需要选择一个knob来控制其他knobs并且设置一个KNOB_CHANGED_ALWAYS flag来确保你能改变knobs集合。

通过这段解释就可以清楚的了解到replace_knobs的机制,当然show_dynamic这个knob值发生变化的时候,就会触发replace_knobs这个方法来删除show_dynamic这个knob之后_numNewKnobs数量的的knobs,并且调用addDynamicKnobs来添加新的节点,同时返回新添加的knobs数量,更新到_numNewKnobs中。然后返回1,表示当前执行成功。
如果if语句内部代码并没有被触发,就会返回knob_changed(k)方法,重复这个循环。

分析完上面三个关键的方法之后不难发现,DynamicKnobs的机制很简单,即:knob_changed()调用了replace_knobs(),replace_knobs()调用了addDynamicKnobs(),通过这种方式去添加新功能。

首先需要建立一个knobs()方法来定义基本的knob,并且保证定义至少中有KNOB_CHANGED_ALWAYS这样一个flag。

第二:定义一个添加knobs的方法即addDynamicKnobs(),内部可以包含一个条件语句,一旦满足条件就添加knob。

第三,需要定义一个监听方法即knob_changed()来获得knobs值的状态,是否这些knobs记录的值发生了变化,一旦指定的knob的值发生了变化,就立刻调用replace_knobs()方法来更新具体位置之后的knobs集合。replace_knobs会先删除一部分knobs,再调用addDynamicKnobs()创建新的knobs。

DynamicKnobs是一个非常结构性的node,通过对这个源码的学习也逐渐理解了C++的一些思想,即基础的去构建一个功能。

NDK学习笔记(三):DynamicKnobs的机制的更多相关文章

  1. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  2. VSTO学习笔记(三) 开发Office 2010 64位COM加载项

    原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...

  3. 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记

    回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...

  4. ZooKeeper学习笔记三:使用ZooKeeper实现一个简单的配置中心

    作者:Grey 原文地址:ZooKeeper学习笔记三:使用ZooKeeper实现一个简单的配置中心 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 很多程序往 ...

  5. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  6. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  7. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

  8. Java IO学习笔记三

    Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...

  9. NumPy学习笔记 三 股票价格

    NumPy学习笔记 三 股票价格 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.&l ...

  10. Learning ROS for Robotics Programming Second Edition学习笔记(三) 补充 hector_slam

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

随机推荐

  1. 数据结构中的列表、元组、字典、集合 ,深浅copy

    数据结构:数据结构是计算机存储数据和组织数据的方式.数据结构是指相互之间存在一种或多种特定关系的数据元素的集合.在python中主要的数据类型统称为容器. 而序列(如列表.元组).映射(如字典).集合 ...

  2. 【集成学习】sklearn中xgboost模块的XGBClassifier函数

    # 常规参数 booster gbtree 树模型做为基分类器(默认) gbliner 线性模型做为基分类器 silent silent=0时,不输出中间过程(默认) silent=1时,输出中间过程 ...

  3. 洛谷P2568 GCD(线性筛法)

    题目链接:传送门 题目: 题目描述 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对. 输入输出格式 输入格式: 一个整数N 输出格式: 答案 输入输出样例 ...

  4. C++学习(二十三)(C语言部分)之 指针4

    指针 指针 存放地址 只能存放地址 使用 &取地址运算符 *取值 解引用运算符 malloc 申请堆内存 free释放堆内存 1.1 指针 存放的地址(变量地址 常量区的地址 堆区内存首地址 ...

  5. functional program language

    1.什么是函数式编程语言 函数式语言(functional language)一类程序设计语言,是一种非冯·诺伊曼式的程序设计语言.函数式语言主要成分是原始函数.定义函数和函数型.这种语言具有较强的组 ...

  6. Echarts全解注释

    coordinate-geo.js文件为地理坐标系的配置参数 mytextStyle={ color:"#333",//文字颜色 fontStyle:"normal&qu ...

  7. 【HAOI2016】放旗子

    终于自己推出来一道题了quq然而时间有点久,考场上并不大丈夫……原题: 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍 ...

  8. 【SpringBoot】SpringBoot热部署和配置文件自动注入实战

    ========================3.SpringBoot热部署devtool和配置文件自动注入实战 ============================ 1.SpringBoot2 ...

  9. openresty 集成 sentry 异常系统

    sentry 是一个方便的错误异常追踪系统,同时社区也提供了openresty 的lua 包,使用docker-compose 进行测试 备注: sentry 部分的配置来自官方文档 环境准备 doc ...

  10. 普林斯顿数学指南(第三卷) (Timothy Gowers 著)

    第V部分 定理与问题 V.1 ABC猜想 V.2 阿蒂亚-辛格指标定理 V.3 巴拿赫-塔尔斯基悖论 V.4 Birch-Swinnerton-Dyer 猜想 V.5 卡尔松定理 V.6 中心极限定理 ...