MITK Tutorial--Step10: Adding New Interaction
来源:互联网 发布:js金沙.com 编辑:程序博客网 时间:2024/06/01 10:12
Step1:怎么使用现有的Datainteractor
MITK有些已经写好的DataInteractor,可以直接在插件或模块中使用。它们可以在MITK源文件的Core/Code/Interactions中可以找到。
mitk::DataInteractor 包含四个部分:描述功能的类以及两个XML文件(statemachine和config);每个mitk::DataInteractor 都必须作用于一个mitk::DataNode。这几个部分必不可少。
例子
参考mitk::PointSetDataInteractor插件。PointSetDataInteractor是DataInteractor的子类。
为使用PointSetDataInteractor,首先需要一个DataNode来保存PointSets,node必须添加到mitk::DataStorage。
mitk::DataNode::Pointer dataNode = mitk::DataNode::New();GetDataStorage()->Add(dataNode.GetPointer());
然后创建mitk::PointSetDataInteractor对象,并载入已经写好的statemachine和configuration文件
m_CurrentInteractor = mitk::PointSetDataInteractor::New();m_CurrentInteractor->LoadStateMachine("PointSet.xml");m_CurrentInteractor->SetEventConfig("PointSetConfig.xml");
最后DataNode添加到mitk::DataInteractor中:
m_CurrentInteractor->SetDataNode(dataNode);
这样,DataInteractor就能发挥作用了。
Step 2: 如何修改DataInteractor的行为
mitk::DataInteractor的行为由两方面决定。state machine决定动作执行的顺序。configuration决定用户交互触发了什么动作。
如何修改display interactor的行为
mitk::DisplayInteractor控制zooming、panning和scrolling等。当我们想改变它的行为时,可以调用InteractionEventObserver,给DisplayInteractor指派一个新的configuration。如下例所示:
std::ifstream* configStream = new std::ifstream( #path to alternative configuration file# );mitk::EventConfig newConfig(configStream);// Requesting all registered EventObserversstd::list<mitk::ServiceReference> listEventObserver = GetModuleContext()->GetServiceReferences<InteractionEventObserver>();for (std::list<mitk::ServiceReference>::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it){ DisplayInteractor* displayInteractor = dynamic_cast<DisplayInteractor*>(GetModuleContext()->GetService<InteractionEventObserver>(*it)); // filtering: only adjust the DisplayInteractor if (displayInteractor != NULL) { displayInteractor->SetEventConfig(newConfig); }}
如何编写新的DataInteractor
本章介绍如何根据自己的需要编写DataInteractor,实现对node数据的操作。MITK交互机制初步知识请参考MITK交互概念。你可能还想知道交互概念的实施。
下面第一节介绍config文件中所有用到的参数以及使用方法。第二节介绍编写
state machine文件;最后一节介绍怎么组合config和state machine文件,同时给出例子,继承mitk::DataInteractor编写自己的DataInteractor。
如何写Config文件
Event
事件由其参数描述。每个事件都有自己的一组参数。如果有的参数被省略,意味着它被设为默认值。下面列出了所有可能的参数。
事件的参数以属性的方式存在。事件的描述由 event class和event variant完成。如
<event_variant class="InteractionKeyEvent" name="StdA">
注:event class的介绍见下面
1. Mouse Buttons
mitk::InteractionEvent::MouseButtons表示鼠标按钮。它可以作为两个属性被使用:EventButton属性表示触发事件的按钮,通常是单个按钮;ButtonState属性表示事件生成时有哪些按钮被按下。比如,假设鼠标右键和中间键已经按下,现在左键也被按下再次触发一个事件,这个状态可以被描述为:
<attribute name="EventButton" value="LeftMouseButton"/><attribute name="ButtonState" value="RightMouseButton,MiddleMouseButton"/>
注意: 技术上来讲,LeftMouseButton也被按下了,应该列在ButtonState中,但这已经被mitk::EventFactory处理好了。
2. Key Events
mitk::InteractionKeyEvent表示一个被按下的键。哪个键被按下可以按照下面表示:
<attribute name="Key" value="A"/>
又比如
<attribute name="Key" value="Escape"/>
注意: 键盘事件不需要显式configuration,因为所有键盘事件已经有预定义好的event variant,名字为 ‘Std’ + value,比如键盘a被命名为’StdA’
特别键盘名字列出如下:
// Special Keys static const std::string KeyEsc; // = "Escape"; static const std::string KeyEnter; // = "Enter"; static const std::string KeyReturn; // = "Return"; static const std::string KeyDelete; // = "Delete"; static const std::string KeyArrowUp; // = "ArrowUp"; static const std::string KeyArrowDown; // = "ArrowDown"; static const std::string KeyArrowLeft; // = "ArrowLeft"; static const std::string KeyArrowRight; // = "ArrowRight"; static const std::string KeyF1; // = "F1"; static const std::string KeyF2; // = "F2"; static const std::string KeyF3; // = "F3"; static const std::string KeyF4; // = "F4"; static const std::string KeyF5; // = "F5"; static const std::string KeyF6; // = "F6"; static const std::string KeyF7; // = "F7"; static const std::string KeyF8; // = "F8"; static const std::string KeyF9; // = "F9"; static const std::string KeyF10; // = "F10"; static const std::string KeyF11; // = "F11"; static const std::string KeyF12; // = "F12"; static const std::string KeyPos1; // = "Pos1"; static const std::string KeyEnd; // = "End"; static const std::string KeyInsert; // = "Insert"; static const std::string KeyPageUp; // = "PageUp"; static const std::string KeyPageDown; // = "PageDown"; static const std::string KeySpace; // = "Space"; // End special keys
3. Modifier Keys
mitk::InteractionEvent::ModifierKeys表示按下辅助按键,多个辅助按键同时被按下,中间用逗号隔开:
<!-- shift and control key are pressed --><attribute name="Modifiers" value="shift,ctrl"/>
4. 滚动方向
mitk::MouseWheelEvent事件,表示鼠标滚轮滚动的方向。
<attribute name="ScrollDirection" value="up"/><!-- or --><attribute name="ScrollDirection" value="down"/>
5. 例子
键盘事件例子:
<config> <!-- Event of key 'a' pressed --> <event_variant class="InteractionKeyEvent" name="StdA"> <attribute name="Key" value="A"/> </event_variant> <!-- Event of key 'b' pressed while modifiers ctrl and shift are pressed--> <event_variant class="InteractionKeyEvent" name="StdB"> <attribute name="Key" value="B"/> <attribute name="Modifiers" value="shift,ctrl"/> </event_variant></config>
鼠标按键事件:
<!-- Standard left click --><config> <event_variant class="MousePressEvent" name="StdMousePressPrimaryButton"> <attribute name="EventButton" value="LeftMouseButton"/> </event_variant><!-- right click with control key pressed--> <event_variant class="MousePressEvent" name="RightWithCTRL"> <attribute name="EventButton" value="RightMouseButton"/> <attribute name="Modifiers" value="ctrl"/> </event_variant></config>
MITK中已经有一个写好的关于大多数事件的标准configuration文件,叫GlobalConfig.xml。可以作为默认config文件,或者是在此基础上扩展。
6. 参数描述
config文件中还可以存储参数,比如:
<config name="example2"> <param name="property1" value="yes"/> <param name="scrollModus" value="leftright"/></config>
怎么写State Machine
状态机以XML文件的格式存储。
1. states
States用state标签标示。每个状态都有一个名字。每个状态机中都必须有一个state作为开始状态,表示状态机构造完成后处于这个初始状态。一个有效的状态机例子如下:
<statemachine> <state name="start" startstate="true"/></statemachine>
有时候,可以选择性地给state增加一个特殊mode,影响事件的转发。这几个mode为:GRAB_INPUT , PREFER_INPUT 和 REGULAR。REGULAR为默认值,无需明确指出。只在必要时使用这些special mode,因为它们让其他DataInteractors都无法接收到事件。
2. Transitions
Transitions表示状态的过渡,对于交互至关重要。Transition包含触发过渡的事件以及所要过渡到的状态。 event class描述事件类型(去mitk::InteractionEvent查看不同的类),event variant代表具体的事件,这两者共同决定哪个事件能够触发过渡。比如StdMousePressPrimaryButton事件发生时(左键按下),状态机将从状态A过渡到状态B。
Event Class
给定如下继承结构的类:
在状态机里,mitk::InteractionPositionEvent 可以被声明为 event class,代表带有位置信息的事件。而它真正是哪个鼠标按键事件可以在config文件中给出。这样, mitk::InteractionPositionEvent itself, 或者mitk::MousePressEvent, mitk::MouseReleaseEvent, mitk::TouchEvent都能在状态机里以同一个身份出现,不管输入设备是什么,只要在相同的类继承结构中,状态机就能以不变应万变。
<statemachine> <state name="A" startstate="true"> <transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="B"/> <state/> <state name="B" /></statemachine>
3. Actions
Actions要被添加到transitions中,表示过渡中需要执行的函数。下面这个状态机监听鼠标左键事件并执行两个动作(循环往复,永不停止)。
<statemachine> <state name="start" startstate="true"> <transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="start"> <action name="addPoint"/> <action name="countClicks"/> </transition> </state></statemachine>
为了告诉mitk::DataInteractor需要执行哪个函数, mitk::DataInteractor 使用CONNECT_FUNCTION将action和函数连接起来。下面表示一个继承了DataInteractor的ExampleInteractor类,该类实现了AddPoint和CountClicks两个函数。CONNECT_FUNCTION必须在重写的ConnectActionsAndFunctions()虚拟函数中使用。
void mitk::ExampleInteractor::ConnectActionsAndFunctions(){ CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks);}
4. Conditions
Conditions可以添加在transitions中,mitk::DataInteractor要进行状态过渡时会调用它们。condition决定是否能够状态过渡以及是否能够执行action。
例子
<statemachine> <state name="start" startstate="true"> <transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="start"> <condition name="checkPoint"/><!-- 如果条件满足,执行下面两个动作 --> <action name="addPoint"/> <action name="countClicks"/> </transition> <transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="start"> <condition name="checkPoint" inverted="true"/><!-- 如果条件不满足,执行下面动作 --> <action name="doSomethingElse"/> </transition> </state></statemachine>
需要在ConnectActionsAndFunctions()函数中进行连接:
void mitk::ExampleInteractor::ConnectActionsAndFunctions(){ CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); CONNECT_FUNCTION("doSomethingElse", DoSomethingElse);}
注意:上面例子中我们可以看到针对同一condition,我们可以定义这个条件在满足以及不满足(inverted是否设为true)情况下的行为。
连接config和状态机
自定义的config和state machine文件需要存储在自定义插件或模块中的/Resources/Interactions文件夹内。并将文件路径添加到对应的files.cmake文件中。像这样:
set(RESOURCE_FILES
Interactions/CustomStateMachinePattern.xml
Interactions/CustomConfig.xml
)
当从模块中载入此二文件时,需要把模块作为参数:
#include "usModule.h"#include "usGetModuleContext.h"... ...m_CurrentInteractor = mitk::CustomDataInteractor::New();m_CurrentInteractor->LoadStateMachine("CustomStateMachinePattern.xml", us::GetModuleContext()->GetModule());m_CurrentInteractor->SetEventConfig("CustomConfig.xml", us::GetModuleContext()->GetModule());... ...
参考注册状态机和config。
重写mitk::DataInteractor
自定义DataInteractor需要继承mitk::DataInteractor,函数的写法如下:
对于actions:
bool SomeFunctionality(StateMachineAction* , InteractionEvent*);
对于conditions:
bool SomeFunctionality(const InteractionEvent*);
使用ConnectActionsAndFunctions()函数将对应函数连接到actions、conditions:
void mitk::ExampleInteractor::ConnectActionsAndFunctions(){ CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints);}
现在只要写好state machine 和config就行了。
示例可参考 mitk::PointSetDataInteractor。里面有完整的注释。
使用InternalEvent的Interactor例子
创建DataInteractors时一个有用的工具是mitk::InternalEvent,它能够让mitk::DataInteractor自己发送信号。
下面举例说明如何写一个mitk::DataInteractor。ExampleInteractor类不断地添加点,直到接收到一个 mitk::InternalEvent事件,告诉它点已经达到指定数量。事先指定的数量可以保存在config文件中。下面先写状态机:
<statemachine> <state name="start" startstate="true" > <transition event_class="MousePressEvent" event_variant="AddPointClick" target="start"> <condition name="checkPoint"/> <action name="addPoint"/> </transition> <transition event_class="InternalEvent" event_variant="enoughPointsAdded" target="final"> <action name="enoughPoints"/> </transition> </state> <state name="final"> <--! dead state, nothing happens any more, once we reached this --> </state></statemachine>
在config文件中,将点的最大数量设为10,并将AddPointClick定义为鼠标右键和ctrl键同时按下。
<config> <param name="NumberOfPoints" value="10"> <event_variant class="MousePressEvent" name="AddPointClick"> <attribute name="EventButton" value="RightMouseButton"/> <attribute name="Modifiers" value="ctrl"/> </event_variant></config>
本例可见
Step10.h
Step10.cpp
在类中,重写函数:
protected: ExampleInteractor(); virtual ~ExampleInteractor(); virtual void ConnectActionsAndFunctions(); virtual void ConfigurationChanged();
连接函数:
void mitk::ExampleInteractor::ConnectActionsAndFunctions(){ // connect the action and condition names of the state machine pattern with function within // this DataInteractor CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints);}
ConfigurationChanged函数将在新的configuration文件载入时被调用(由mitk::InteractionEventHandler调用)。这个函数允许执行初始化语句,本例中我们想设定点数上限。
void mitk::ExampleInteractor::ConfigurationChanged(){ // read how many points we accept from the config properties mitk::PropertyList::Pointer properties = GetPropertyList(); std::string maxNumber; properties->GetStringProperty("NumberOfPoints",maxNumber); m_MaximalNumberOfPoints = atoi(maxNumber.c_str());}
接下来,需要写action函数了,它们的声明如下:
private: bool AddPoint(StateMachineAction* , InteractionEvent*); // function to add new points bool EnoughPoints(StateMachineAction* , InteractionEvent*); // function changes color of pointset to indicate, it is full bool CheckPoint(const InteractionEvent* interactionEvent); // function checks if the clicked point is valid
bool mitk::ExampleInteractor::AddPoint(StateMachineAction*, InteractionEvent* interactionEvent){ // cast InteractionEvent to a position event in order to read out the mouse position // we stay here as general as possible so that a different state machine pattern // can reuse this code with MouseRelease or MouseMoveEvents. InteractionPositionEvent* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent); if (positionEvent != NULL) { // query the position of the mouse in the world geometry mitk::Point3D point = positionEvent->GetPositionInWorld(); m_PointSet->InsertPoint(m_NumberOfPoints, point, 0); m_NumberOfPoints++; GetDataNode()->SetData(m_PointSet); GetDataNode()->Modified(); if (m_NumberOfPoints != 0 && m_NumberOfPoints >= m_MaximalNumberOfPoints) { // create internal event that signal that the maximal number of points is reached InternalEvent::Pointer event = InternalEvent::New(NULL,this, "enoughPointsAdded"); // add the internal event to the event queue of the Dispatcher positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } // update the RenderWindow to show new points mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { return false; }}bool mitk::ExampleInteractor::EnoughPoints(StateMachineAction*, InteractionEvent*){ GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 1.0, 0.0)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true;} //-
如果条件函数返回false,那么过渡以及它的action将不执行。如果条件不通过,这个事件就被认为是未处理的,将被转交给其它Interactors。
bool mitk::ExampleInteractor::CheckPoint(const InteractionEvent *interactionEvent){ // check if a point close to the clicked position already exists float epsilon = 0.3; // do not accept new points within 3mm range of existing points InteractionPositionEvent* positionEvent = dynamic_cast<InteractionPositionEvent*>(interactionEvent); if (positionEvent != NULL) { // query the position of the mouse in the world geometry mitk::Point3D point = positionEvent->GetPositionInWorld(); int retVal = m_PointSet->SearchPoint(point, epsilon); if ( retVal == -1 ) // SearchPoint returns -1 if no point was found within given range return true; } return false; // if the positionEvent is NULL or a point was found return false. AddPoint will not be executed //end
这里,一个internal event将在点数达到上限后发送消息。该事件在创建后需要添加到dispatchers事件队列中:
// create internal event that signal that the maximal number of points is reached InternalEvent::Pointer event = InternalEvent::New(NULL,this, "enoughPointsAdded"); // add the internal event to the event queue of the Dispatcher positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer());
nternal events不需要映射到event variants。它们的信号名字与event variant相同。
了解更多可参考
mitk::DataInteractor和mitk::InteractionEventObserver
mitk::PointSetDataInteractor
mitk::DisplayInteractor
- MITK Tutorial--Step10: Adding New Interaction
- MITK Concepts - Interaction/MITK中的交互概念
- Adding new Space Window
- zoj 3540 Adding New Machine
- hdu 4052 Adding New Machine
- hdu4052-Adding New Machine-题解
- UVALive 5694 Adding New Machine
- Adding new nodes to GI
- C++ Qt Game Tutorial 5 - Adding Enemies
- Adding new icon to toolbar with script
- Adding New Functions to Compiled Code
- Adding New Functions to Compiled Code
- Adding a new board to Uclinux
- [线段树]HDU 4052 Adding New Machine
- Adding new items to the Checklist
- Adding new disk to GPFS - Very practical
- UVA 1492 - Adding New Machine(线段树)
- uva 1492 - Adding New Machine(线段树)
- 考研英语单词8—141-160
- apache日志分析
- memcached安装与参数说明
- ie7 去除iframe 边框
- from nova to ironic(6)
- MITK Tutorial--Step10: Adding New Interaction
- Big Picture
- 移动端web产品开发框架appframework浅探
- eclipse写文件入hdfs失败
- 第六周项目零:阅读程序(1):结构函数和析构函数
- BMFont中文字体图集制作的方法~(for unity ngui)
- Unable to execute dex: Multiple dex files define 解决方法
- 东半球最好的TV桌面开源项目
- AFNetworking图片缓存问题<转>