(学习笔记,错误难免,请指正;私人劳动,转载请注明出处)
下面以MITK自带的mitkPointSetDataInteractor.h、mitkPointSetDataInteractor.cpp、PointSet.xml、PointSetConfig.xml为例,解释MITK中的交互原理,以及State machine和configuration的调用顺序。
PointSet.xml:
<statemachine>
<state name="start" startstate="true">
<transition event_class="InteractionPositionEvent" event_variant="AddPointClick" target="start">
<action name="addpoint"/>
</transition>
<transition event_class="MouseMoveEvent" event_variant="CheckSelected" target="selected">
<condition name="isoverpoint"/>
<action name="selectpoint"/>
</transition>
<transition event_class="InteractionKeyEvent" event_variant="Abort" target="start">
<action name="abort"/>
</transition>
</state>
<state name="selected">
<transition event_class="MouseMoveEvent" event_variant="CheckSelected" target="start">
<condition name="isoverpoint" inverted="true"/>
<action name="unselectAll"/>
</transition>
<transition event_class="MousePressEvent" event_variant="DeletePoint" target="start">
<action name="removePoint"/>
</transition>
<transition event_class="MousePressEvent" event_variant="StartMove" target="MovementInitalized">
<action name="initMove"/>
</transition>
</state>
<state name="MovementInitalized">
<transition event_class="MouseMoveEvent" event_variant="PointMove" target="MovementInitalized">
<action name="movePoint"/>
</transition>
<transition event_class="MouseReleaseEvent" event_variant="EndMovement" target="selected">
<action name="finishMovement"/>
</transition>
</state>
</statemachine>
PointSetConfig.xml:
<config>
<!-- Set a maximal number of points here:
<param name="MaxPoints" value="4"/>
-->
<event_variant class="InternalEvent" name="EnoughPoints">
<attribute name="SignalName" value="MaxNumberOfPoints"/>
</event_variant>
<event_variant class="InternalEvent" name="NotEnoughPoints">
<attribute name="SignalName" value="haslessthen3points"/>
</event_variant>
<event_variant class="InteractionKeyEvent" name="Abort">
<attribute name="Key" value="Escape"/>
</event_variant>
<event_variant class="MousePressEvent" name="DeletePoint">
<attribute name="EventButton" value="LeftMouseButton"/>
<attribute name="Modifiers" value="alt"/>
</event_variant>
<event_variant class="MousePressEvent" name="AddPointClick">
<attribute name="EventButton" value="LeftMouseButton"/>
<attribute name="Modifiers" value="shift"/>
</event_variant>
<event_variant class="MousePressEvent" name="StartMove">
<attribute name="EventButton" value="LeftMouseButton"/>
</event_variant>
<event_variant class="MouseMoveEvent" name="CheckSelected"/>
<event_variant class="MouseReleaseEvent" name="EndMovement">
<attribute name="EventButton" value="LeftMouseButton"/>
</event_variant>
<event_variant class="MouseMoveEvent" name="PointMove">
<attribute name="ButtonState" value="LeftMouseButton"/>
</event_variant>
</config>
自定义DataInteraction需要继承mitk::DataInteractor。自定义的函数必须通过重写ConnectActionsAndFunctions()函数与statemachine中的行为连接起来。比如mitkPointSetDataInteractor.cpp中:
void mitk::PointSetDataInteractor::ConnectActionsAndFunctions()
{
// Condition which is evaluated before transition is taken
// following actions in the statemachine are only executed if it returns TRUE
CONNECT_CONDITION("isoverpoint", CheckSelection);
CONNECT_FUNCTION("addpoint", AddPoint);
CONNECT_FUNCTION("selectpoint", SelectPoint);
CONNECT_FUNCTION("unselect", UnSelectPointAtPosition);
CONNECT_FUNCTION("unselectAll", UnSelectAll);
CONNECT_FUNCTION("initMove", InitMove);
CONNECT_FUNCTION("movePoint", MovePoint);
CONNECT_FUNCTION("finishMovement", FinishMove);
CONNECT_FUNCTION("removePoint", RemovePoint);
}
调用顺序
当一个用户交互事件,如按住shift键单击左键发生时,MousePressEvent,经mitk::Dispatcher分发给对应的mitk::DataInteractor。该mitk::DataInteractor根据随事件传递过来的参数(value=”LeftMouseButton”、value=”shift”)会检查PointSetConfig.xml,结果发现有对应的事件:
<event_variant class="MousePressEvent" name="AddPointClick">
<attribute name="EventButton" value="LeftMouseButton"/>
<attribute name="Modifiers" value="shift"/>
</event_variant>
这里两个attribute标签即为事件的参数。找到shift+左键对应的event variant,返回event variant的名字“AddPointClick”。接着状态机在PointSet.xml中查找目前状态下是否存在能够被触发的transition,发现有,event_variant=”AddPointClick”。这个transition在start状态里,执行动作“AddPoint”(这个动作对应的函数在上面已经连接好了,为AddPoint()),然后跳到状态”start”。
如果触发的是configuration文件里的CheckSelected,则状态机里对应的是“start”状态里的MouseMoveEvent,检查条件“isoverpoint”是否满足(调用函数CheckSelection()),如果满足,执行行为”selectpoint”(调用函数SelectPoint()),然后根据target=”selected”跳到“selected”状态。这时如果configuration文件里的CheckSelected再被触发,则对应的transition是“selected”状态里的:
<transition event_class="MouseMoveEvent" event_variant="CheckSelected" target="start">
<condition name="isoverpoint" inverted="true"/>
<action name="unselectAll"/>
我们看到,这时应该执行的行为就变为”unselectAll”了。
上行代码中 inverted=”true”是什么意思?