最近的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. ORA-03001,GATHER_TABLE_STATS数据库自动收集统计信息报错

    1.根据Alert报错信息,查询Trace日志 /oracle/app/oracle/admin/fgsquery/bdump/fgsquery_j001_11111.trc Oracle Datab ...

  2. 【opencv基础】linux系统卸载opencv

    找到opencv某个版本的源码文件,进入build目录: cd opencv_build sudo make uninstall cd .. sudo rm -r build sudo rm -r / ...

  3. 二叉树求逆序对(伪AC 23333)

    成链的时候 是最坏情况 O(n^2)的复杂度呢! 按照输入的数据 一个一个的插入建树 然后维护左右儿子的个数  (我们规定, 左儿子 小于  父亲  右儿子大于父亲) 往左走 说明存在逆序对 逆序对的 ...

  4. Oracle导入导出表

    使用PL SQL Developer进行操作 一.导出 工具<<导出表<<sql插入<<选择用户和要导出的表,勾选创建表,选择输出文件(格式最好为.sql),点击导 ...

  5. css完成下图

    <div></div> div{ height: 48px; width: 80px; padding: 0 16px 0 32px; background: rgba(0,0 ...

  6. 20155208徐子涵 2016-2017-2 《Java程序设计》第10周学习总结

    #### **教材学习总结**网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就是狭义的网络编程范畴. * ...

  7. 11月15Sprint计划会议及内容·

    今天对整体设计做了明确的规划 工作分配: 1规划 2规则制定 3窗体设计 4模型设计 5代码编写 6美化 7产品交付 8后期宣传 王超群前四项 吕浩宇后四项

  8. python:代码复用与函数递归

    #recursion.py:打印斐波那契数列 def fact(n): if n==1 or n==2: return 1 else: return fact(n-1)+fact(n-2) while ...

  9. 【java编程】java中什么是bridge method(桥接方法)

    https://blog.csdn.net/mhmyqn/article/details/47342577 https://www.cnblogs.com/strinkbug/p/5019453.ht ...

  10. vuex简介(转载)

    安装.使用 vuex 首先我们在 vue.js 2.0 开发环境中安装 vuex : npm install vuex --save 然后 , 在 main.js 中加入 : import vuex ...