Initialisierung Pro BaseRenderer gibt es einen Dispatcher und einen BindDispatcherInteractor. Beide werden im Konstruktor des BaseRenderes mit angelegt. BindDispatcherInteractor (BDI) registriert sich für DataStorage-Events und wird bei Änderungen benachrichtigt. Ein Interactor wird unter Angabe einer DataNode erzeugt, und added sich selbst dann zu dieser DataNode (Methode existiert bereits: DataNode.AddInteractor() ) Wenn Interactor zu einer DataNode geadded wird (die dem BaseRenderer zugeordnet ist) wird entsprechender BDI informiert, und registriert den Interactor beim Dispatcher. Für Interactor-'User' bleibt Interactor-Registrierung und Dispatcher-Auswahl somit transparent. #!highlight cpp mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); m_DataStorage->Add(pointSetNode); mitk::Interactor::New("interactor", pointSetNode)); Ereignisse des DataStorage BDI registriert sich für Ereignisse der DataStorage. DS sendet Ereignis: InteractorChanged mit Pointer auf Interactor bei AddInteractor, bzw null bei RemoveInteractor. Außerdem sendet DS das gleiche Ereignis wenn ein DataNode hinzugefügt wird der schon einen Interactor hat. Sequenz-Diagram Initialisierung.png EventHandler Klassenhierarchie: EventHandler_Hirarchy.png Listener/EventObserver Verwaltung über MicroServices. Listener melden sich als Services an, geben an für welche Quellen sie Ergeinisse erhalten wollen: spez. Renderer oder DataStorage, alle. Events Systemevents Events werden durch Event-Klassen beschrieben. Jede Event-Klasse wird durch eine C++ Klasse implementiert. Um verschiedene Frameworks zu unterstützden müssen aus System-Events mitkEvents erzeugt werden. Dafür wird eine dem Event entsprechendes Objekt erstellt und alle verfügbaren Parameter dem Konstruktor übergeben. Anschließend wird dieses Objekt an den Dispatcher gesendet. Beispiel für ein Qt MouseMove Event: #!highlight cpp QMouseEvent e; if (e == QEvent::MouseMove) { mitk::mitkMouseMoveEvent mouseMove(renderer,e.x, e.y, e->button(), e->buttons); this.GetDispatcher()->ProcessEvent(mouseMove); } Die Interaktion kann um Events erweitert werden indem weitere Event-Klassen erzeugt werden. Liste der geplanten Event-Klassen (ohne MultiTouch). Events.png StateMachine Events Events die in StateMachines genutzt werden um Transitions und somit Aktionen auszulösen, werden zweistufig beschrieben. Zum einen durch ihre Event-Klasse (MouseMove, KeyPress, etc. ), und zum anderen durch ihre Variante. Wobei die Variante die exakte Ausprägung des Events beschreibt (also welche Maustaste wurde gedrückt, mit oder ohne Modifier, etc). Die Varianten-Beschreibung ist in eine separate Datei ausgelagert um die Konfiguration zu erleichtern. Beispiel Beschreibung in der StateMachine (allgemein) #!highlight xml Beispiel Beschreibung der Konfiguration (spezifisch) #!highlight xml Die im Tag angegebenen Werte, werden in der StateMachine als Properties gesetzt. Konfiguration Die Konfiguration wird in einem EventConfig Objekt abgebildet. Das EventConfig Objekt liest die Konfiguration.xml und baut für jede Event-Konfiguration ein spezifisches mitkEvent auf, dem der angegebene event_variant Name mitgegeben wird. Dieses EventConfig Objekt wird an einen Interakor gehängt, und löst für diesen die Events die er vom Dispatcher bekommt auf eine spezielle Variante auf. (s. Schaubild) D.h. es bekommt ein mitkEvent und vergleicht alle ihm bekannten (Config) Events damit, bei Gleichheit liefert es den event_variant Namen zurück. EventMapping.png Instanziierung: #!highlight cpp EventHandler eh("eventhandler.xml"); EventConfigObject config("config.xml"); eh.SetEventConfigObject(config); Beschreibung einer StateMachine #!highlight xml Abarbeitungslogik Jeder Event-Interaktor hat genau einen DataNode. Für die Abarbeitung werden die Interaktoren nach der Layer ihres DataNodes priorisiert. Der Reihe nach bekommt jeder Interaktor das Event angeboten, und bearbeitet es oder nicht (binäre Antwort). Event wird von genau einem Interaktor bearbeitet. Alle Listener bekommen das Event. "Partielle Bearbeitung eines Events möglich. zB bei MultiTouch Events mit mehreren Berührungspunkten kann für jeden Berührungspunkt ein neues Event erzeugt werden in in die Event-Schleife des Dispatcher hinzugefügt werden. Innerhalb einer logischen Interaktionseinheit (Mouse-Down, Mouse-Move, Mouse-Up) wird der Ziel Interaktor nicht neu vergeben, dh der Interaktor der das MouseDown Event verarbeitet bekommt auch die nachfolgenden beiden). Eine SM kann einen Zustand als besonderen Modus kennzeichnen (GrabInput), wenn eine SM in einem Zustand mit diesem Modus ist, bekommt sie allein alle Inputs. Anwendungsfall: SM bildet Erstellen einer Linie ab, durch Clicken auf 2 Punkte ab. Wenn Vorgang beginnt und der erste Punkt gesetzt wurde, sind die einzigen sinnvollen nächsten Schritte den zweiten Punkt zu setzten um die Linie zu vollenden, oder die Aktion abzubrechen. Der Zustand "Ein Punkt ist gesetzt" würde also mit GrabInput markiert, und die SM so lange alle Events bekommen, bis dieser Zustand wieder verlassen wurde. Übergangslösung Gegenüberstellung der beiden Abarbeitungssequenzen (grün: neue Klassen) Legacy.png Mögliche Übergangslösung: RenderWindowBase, BaseRenderer sowie MapEvent so umschreiben, das Ergebnis an aufrufendes Objekt zurückgegeben wird. Dh. die "alten" Events stehen dann auch auf Ebene von QmitkRenderWindow zur Verfügung. Dem Dispatcher werden dann in der Übergangszeit beide Events gegeben, und falls dieser keinen Abnehmer findet, leitet er "altes" Event an die GlobalInteraction zur Abarbeitung. Nach Übergangszeit werden "alte" Events nicht mehr erzeugt, EventAdapter und MapEvent gelöscht, und Dispatcher nur noch mit "neuen" Events aufgerufen.