MITK交互概念

来源:互联网 发布:淘宝买家评价修改 编辑:程序博客网 时间:2024/05/23 00:09

(学习笔记,错误难免,请指正;私人劳动,转载请注明出处)

MITK有特殊的交互概念,让同样的交互方案可以在不同的情境中使用。MITK交互的核心基于DataInteractors,它们监听事先定义的事件,并在事件被触发时做出响应(执行actions)。

下面介绍交互的几个组成部分。

Event Handling & GUI Toolkit Abstraction

下图介绍了mitk::DataInteractor中创建事件到执行action的过程。该图的前提条件是使用QT框架,同时展示了MITK交互概念是独立于任何图形用户接口工具包的。

这里写图片描述

  1. 一个用户触发了事件,消息传递给MITK。
  2. 本层作为GUI toolkit(此处指QT)到MITK内部事件(后面称为InteractionEvents)的适配器
  3. 事件经过适配后,发往与渲染窗口连接的mitk::Dispatcher。
  4. 在mitk::Dispatcher层,所有能对事件(mitk::DataInteractor和mitk::InteractionEventObserver实例)进行响应的对象都能被找到。
  5. 一个mitk::DataInteractor被赋予一个事件,然后检查自己的mitk::EventConfig对象,看这个事件自己是否有定义并返回值。
  6. 如果DataInteractor中有定义,则检查其state machine,看当前状态下是否能够做出响应。
  7. 如果检查到与状态变化(transition)相对应的行为,则执行该行为,自此事件被完整处理。

事件

事件可以是任何用户输入,例如键盘、鼠标或触摸手势。这些事件从QT这样的UI框架映射到MITK内部(这时候只知道有消息要发送了,但不知道发给谁),而后被发送到mitk::Dispatcher做进一步消息分发(这时候的分发就是具体到发给谁了)。这些事件并不局限于传统输入,用户还可以给比如追踪装置等定义新的类去描述事件(如何创建新事件?)。

InteractionEventHandler

描述能够处理事件的对象。这些对象可以被划分为两组:DataInteractors和mitk::InteractionEventObserver。他们间不同之处在于:mitk::DataInteractor实例作用于一个mitk::DataNode。mitk::InteractionEventObserver和DataNode没有关系,因此不能处理数据(mitk::DataNode实例)。

这里写图片描述

DataInteractors

DataInteractors继承了mitk::InteractionEventHandler,且更加具体。它能够通过状态机(state machine)处理与DataNode相关的事件。

StateMachines

事件的具体行为响应取决于要操作的数据和交互状态。比如用鼠标画一条线,第一次双击应该是添加一个点,第二次双击应该加一个点并结束交互。这样第三次双击就会无效。诸如相同的用户交互事件,根据目前状态而触发不同的行为这样的事情,状态机(state machine)能够很好地完成。

DataInteractor通过状态机模式完成事件处理,其基本思想是每次交互都能被描述为state和transition(触发行为)。不同的状态机可以用于同一个mitk::DataInteractor,让DataInteractor执行不同的用户交互。举例来说:

假设一个mitk::DataInteractor 具有两个功能:
1.在鼠标所在位置添加点并用直线连接这些点。
2.检查两个点是否在同一位置。

在不同的mitk::StateMachine模式下,这个mitk::DataInteractor 能够执行不同的交互方案。

状态机模式1:我们想让用户画一条直线,一个包含三种状态的状态机如下:

这里写图片描述

每次MousePress事件发生时,AddPoint函数被调用,在鼠标位置添加一个点,除非已添加两个点。

状态机模式2:同样的mitk::DataInteractor可以按如下状态机进行操作,画一个闭合的轮廓。
mitk::DataInteractor能够在已有的点上检测AddPoint事件,并处罚一个PointsMatch事件。

这里写图片描述

这样不同的交互行为可以由状态机来完成,开发者只需更换状态机,而不用修改程序代码。

定义状态机

状态机以XML文件的形式存储,它的定义由四部分组成。

  • States:状态,当前交互所处的状态。
  • Transitions:过渡事件,从一个状态过渡到另一个状态所需要的事件。
  • Conditions:过渡条件,在过渡发生前执行,作为是否允许状态过渡的条件。
  • Actions:行为,过渡条件满足,允许状态过渡,触发此行为。

每个状态机都需要一个初始状态(StartState),即状态机刚被建立时就为这个状态。下面是一个状态机的例子,描述状态机模式2:添加点到轮廓直到PointsMatch事件被触发。

<statemachine>    <!-- startstate设为true,表示这是起始状态 -->    <state name="StartState" startstate="true" >        <transition event_class="MousePressEvent" event_variant="MousePress" target="StartState">            <condition name="CheckPoint"/>            <action name="AddPoint"/>        </transition>        <transition event_class="InternalEvent" event_variant="PointsMatch" target="ClosedContour">            <action name="AddPoint"/>        </transition>    </state>    <state name="ClosedContour"/></statemachine>

Transitions

上面代码中可以看到,触发transition的事件由两部分共同决定: event_class和event_variant。event class代表事件类型(查询有哪些事件类型可参考mitk::InteractionEvent)。event variant为更具体的事件描述,且event variant的值来自config文件。比如下面的例子,当StdMousePressPrimaryButton事件触发时(鼠标左键按下),状态机从状态A转换到状态B。

例子:

状态机
<statemachine>
<state name="A" startstate="true">
<transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="B"/>
<state/>
<state name="B" />
</statemachine>

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>

InteractionEventObserver

mitk::InteractionEventObserver实例接收所有用户的输入,且目的在于观察,它不能修改任何DataNode。mitk::InteractionEventObserver可以选择使用或者不使用状态机,默认是不使用。如何使用状态机功能可参考文档mitk::InteractionEventObserver::Notify。

这里写图片描述

Configuration

许多情况下,独立于特定事件(如鼠标左击)的交互更为方便,以便能够方便地更换。这就需要InteractionEventHandlers的configuration来完成。它允许运行中更换行为。

通过载入不同的configuration,mitk::InteractionEventHandler能够非常容易地更换用户输入所应触发的行为。configuration也是用XML文件来描述的。mitk::InteractionEventHandler能够载入这些文件,从而建立起用户输入到行为的映射。

这里需要区分specific event和event variant。specific event由它自己的类来描述,比如mitk::MousePressEvent,且它的参数保证它的独一无二。event variant是赋予某个特定事件的名字。mitk::InteractionEventHandler监听这些名字。

例如,下面列出了两个不同的configuration文件。假设 mitk::InteractionEventHandler监听的event variant是 ‘AddPoint’。

示例2:鼠标左击事件描述

<config name="one"> <event_variant name="MousePress" class="MousePressEvent">  <attribute name="EventButton" value="LeftMouseButton"/> </event_variant></config>

示例3:按住Shift键同时鼠标左击事件描述

<config name="two"> <event_variant name="MousePress" class="MousePressEvent">  <attribute name="EventButton" value="RightMouseButton"/>  <attribute name="Modifiers" value="shift"/> </event_variant></config>

如果mitk::InteractionEventHandler载入的是第一个configuration,则鼠标左键按下时,event variant “MousePress” 被触发。如果载入的是第二个configuration,按住shift键的同时鼠标右键按下时,”MousePress” 被触发。如此,所有继承自mitk::InteractionEventHandler的对象都可以被configured。如何创建configuration XML文件参考Interaction Concept Implementation。

Dispatcher

mitk::Dispatcher的实例接收所有事件,并将这些事件转发给相关的mitk::DataInteractor实例。根据DataInteractors对应的mitk::DataNode的layer level,对DataInteractors进行降序排序。事件被分发给第一个mitk::DataInteractor,由其检查自己是否能够处理这个事件。然后是第二个,第三个……DataInteractor,直到有DataInteractor能处理该事件。之后,其他的DataInteractors被跳过,且所有的InteractionEventObservers都会接到通知。

1 0