123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- In diesem Abschnitt der Arbeit wird die GUI aus Implementationssicht betrachtet und es wird erläutert, wie genau die einzelnen Features, die in
- Abschnitt \ref{ch:guifunktionen} erwähnt wurden, umgesetzt worden sind.
- Bei der Implementation einer Anwendung ist es üblich für bestimmte Bereiche fertige Bibliotheken zu verwenden.
- Auf diese Weise muss nicht jeder Entwickler immer wieder den gleichen Code schreiben und es wird viel Zeit gespart.
- Des Weiteren ist es üblich den Code gut zu strukturieren, damit er auch später noch gut verstanden und verändert werden kann.
- Hierfür verwendet man bestimmte Architektur- und Entwurfsmuster, welche sich im Laufe der Jahre als vorteilhaft etabliert haben.
- In den folgenden Abschnitten werden die verwendeten Bibliotheken und Entwurfs- und Architekturmuster vorgestellt.
- \section{Verwendete Bibliotheken}\label{sec:bibliotheken}
- Für die Implementierung der GUI wurde die objektorientierte Programmiersprache C++~\cite{perry1996introduction} gewählt.
- Objektorientierte Programmiersprachen bieten den Vorteil, dass das Programm besser strukturiert werden kann, da man es in verschiedene Klassen aufteilen kann.
- Für die Gestaltung der GUI Oberflächen wurde das Qt-Framework\footnote{http://doc.qt.io/qt-5/index.html} verwendet. Dies bietet den Vorteil,
- dass es plattformübergreifend ist und daher nicht für jedes Betriebssystem neuer Code geschrieben werden muss.
- Das Qt-Framework bietet außerdem viele praktische Funktionen um eine GUI zu entwerfen.
- Als Entwicklungsumgebung wurde der in Qt enthaltene QtCreator verwendet.
- Dieser bietet speziell für das Qt-Framework ausgelegte Funktionen, die es einem Entwickler erheblich erleichtern eine Anwendung mit dem Qt-Framework zu entwickeln.
- Der QtCreator erleichtert den Entwurf einer GUI vor allem durch die Möglichkeit diese grafisch zu erstellen.
- Es muss also nicht jedes GUI Element erst aufwendig im Quellcode erstellt werden, sondern kann in einem grafischen GUI Editor platziert werden.
- Für die Kommunikation mit dem oben erwähnten Server, welcher die Vorabannotationen bereitstellt,
- wurde die ZeroMQ Bibliothek\footnote{http://zguide.zeromq.org/page:all} verwendet.
- ZeroMQ ist eine bekannte Bibliothek für die Kommunikation zwischen verschiedenen Anwendungen.
- ZeroMQ läuft auf den meisten modernen Betriebssystemen und ist zudem einfach und schnell zu benutzen~\cite{Dworak:1391410}.
- Für das Arbeiten mit Bildern, wie zum Beispiel für die Extraktion der Objekt-Polygone aus einem Bild, wurde die OpenCV Bibliothek\footnote{https://opencv.org/} verwendet.
- OpenCV steht für Open Source Computer Vision und bietet viele nützliche Algorithmen für Bild- und Videoanalyse an~\cite{culjak2012brief}.
- Für das Lesen und Speichern der Annotationen im XML Format wurde TinyXML-2\footnote{http://leethomason.github.io/tinyxml2/} verwendet.
- TinyXML-2 ist eine kleine Bibliothek zum Laden und Speichern von XML Dateien in C++, welche einfach einzubinden ist.
- \section{Architekturmuster}\label{sec:muster}
- Bei der Implementierung der GUI wurde besonders darauf geachtet, dass die Programmstruktur gut wartbar und veränderbar ist.
- Hierfür wurde das Model-View-Controller Architekturmuster~\cite{deacon2009model} verwendet.
- Dabei wird zwischen der Datenhaltung (Model), der Darstellung (View) und den Schnittstellen für die Nutzereingaben (Controller) unterschieden.
- Abbildung \ref{fig:mvc} zeigt eine grafische Darstellung des Model-View-Controller Architekturmusters.
- Hierbei sind die Klassen für Model, View und Controller abstrakt, so dass es verschiedene Umsetzungen von ihnen in einer Anwendung geben kann.
- Das hat den Vorteil, dass man ohne viel Aufwand verschiedene Views implementieren kann, welche die gleichen Daten unterschiedlich darstellen.
- Auch kann man verschiedene Controller implementieren, welche die Nutzereingaben unterschiedlich verarbeiten und die Daten auf andere Weise ändern.
- Auf diese Weise erhält man eine hohe Flexibilität des Codes.
- Durch die Aufteilung des Codes in die drei Bereiche ist der Code außerdem gut strukturiert und kann leicht außeinandergehalten werden.
- Wenn zum Beispiel die Oberfläche der Anwendung angepasst werden soll, muss auch nur Code in diesem Bereich verändert werden.
- \graphicsfigure{bilder/mvc}{Model-View-Controller}{fig:mvc}{0.50\textwidth}
- Im Folgenden wird beschrieben wie das Model-View-Controller Architekturmuster zur Implementierung der GUI eingesetzt wurde.
- \subsection{Datenhaltung}\label{sub:datenhaltung}
- Im Wesentlichen gibt es in der GUI zwei unterschiedliche Model-Klassen.
- Die Eine, das Arbeitsmodel, enthält lediglich Daten, die für die korrekte Darstellung der Arbeitsfläche notwendig sind, aber nicht zur geöffneten Bildsequenz gehören.
- Dazu gehört beispielsweise ein unvollendetes Polygon, welches vom Nutzer erst noch fertig erstellt wird, bis es schließlich zu einem neuen Objekt in der Sequenz wird
- (mit dem \glq Neues Objekt\grq{ } Werkzeug aus Abschnitt \ref{subsec:newObject}).
- Auch wird dort ein Objekt gespeichert, welches vom Nutzer kopiert wurde, aber noch nicht eingefügt wurde (mit dem \glq Kopieren und Einfügen\grq{ } Werkzeug aus Abschnitt \ref{subsec:kopyAndPaste}).
- \svgfigure{bilder/datenhaltung}{Klassendiagramm der Datenhaltung-Klassen für eine Bildsequenz}{fig:datenhaltung}
- Die andere Model-Klasse, die Sequenz Klasse, enthält alle Daten der geöffneten Bildsequenz.
- Dafür benutzt sie wegen der Vielzahl an verschiedenen Daten in einer Sequenz einige weitere Hilfsklassen.
- In Abbildung \ref{fig:datenhaltung} befindet sich dazu ein gekürztes UML (Unified Modeling Language) Klassendiagramm.
- Da die GUI dazu verwendet werden soll große Bildsequenzen mit mehreren tausend Bildern zu annotieren,
- ist es nicht möglich alle Bilder gleichzeitig in den Arbeitsspeicher zu laden.
- Daher lädt die GUI zunächst nur die Pfade zu den Bild-Dateien, und lädt die Bilder nur, wenn sie gebraucht werden.
- Dazu gibt es eine Frame Klasse, welche für das Laden von Bildern zuständig ist. Außerdem enthält sie eine Liste mit Objekt-Polygonen zu einem Bild.
- Des Weiteren gibt es eine Mask Klasse, welche für das Laden und Speichern der Masken Bilder zuständig ist.
- Zur Verwaltung dieser Klassen gibt es eine Kamera Klasse, welche eine Liste von Frame Objekten, ein Mask Objekt und den Namen einer Kamera enthält.
- Die verschiedenen Kamera Objekte werden von der Sequenz Klasse verwaltet.
- Zusätzlich enthält die Sequenz Klasse auch noch eine Liste mit allen Objektklassen und eine Liste mit allen in der Sequenz vorkommenden Objekten.
- Letzteres wurde dabei aus Performance Gründen hinzugefügt. Wenn festgestellt werden soll, ob ein bestimmtes Objekt existiert,
- müssen so nicht immer alle Kameras und Bilder durchsucht werden. Das wird zum Beispiel von der Objekt-Zuweisungsoberfläche aus Abschnitt
- \ref{sec:objektZuweisungsansicht} benötigt, in der eine Liste mit allen vorhandenen Objekten angezeigt wird.
- Zwei Views, welche die Sequenz Klasse als Model verwenden, sind der Navigationsbaum aus Abschnitt \ref{sec:objectBaum}
- und die Arbeitsfläche aus Abschnitt \ref{sec:arbeitsansicht}. Letztere greift dabei hauptsächlich auf die Frame Klasse zu, welche das aktuell ausgewählte Bild enthält.
- \subsection{Controller}\label{sub:controller}
- Im Wesentlichen ist in der GUI immer ein Haupt-Controller aktiv, welcher für die Auswahl der Werkzeuge und für Aktionen wie zum Beispiel das
- Laden einer Sequenz in der Hauptansicht \ref{sec:hauptansicht} zuständig ist.
- Dieser Controller aktiviert dann je nach ausgewähltem Werkzeug andere Controller,
- die nur für die Interaktion des Nutzers mit der Arbeitsfläche aus Abschnitt \ref{sec:arbeitsansicht} zuständig sind.
- Dafür erben diese Controller von einer ArbeitsController Klasse, die bereits über grundlegende Funktionalitäten verfügt, welche bei allen Controllern gleich sind.
- Dazu gehört zum Beispiel die Möglichkeit mit dem Mausrad näher ran oder weiter weg zu zoomen.
- Die erbenden Controller erweitern diese Funktionen, so dass sie die Funktionen von jeweils einem Werkzeug anbieten.
- Eine Ausnahmen ist dabei das \glq Kopieren und Einfügen\grq{ } Werkzeug, welches in zwei Controller, dem CopyController und dem PasteController, aufgespalten ist.
- Dies ist als UML Klassendiagramm in Abbildung \ref{fig:controller} verkürzt dargestellt.
- \svgfigure{bilder/controller}{Klassendiagramm der Controller-Klassen}{fig:controller}
- \subsection{Entwurfsmuster}\label{sub:entwurfsmuster}
- Bei der Implementierung der GUI wurden neben dem Model-View-Controller Architekturmuster auch noch einige Entwurfsmuster eingesetzt.
- Entwurfsmuster werden dafür eingesetzt, wiederkehrende Probleme beim Design von Software zu lösen.
- Dabei wird beim Entwurf nach einem bestimmten Muster vorgegangen, welches sich für eine bestimmte Problemstellung bewährt hat.
- Wie oben bereits erwähnt, ist die Datenhaltung in der GUI ein komplexes System von Klassen, die miteinander interagieren.
- Der Navigationsbaum der GUI, welcher mit Hilfe des Qt-Frameworks implementiert wurde,
- braucht Zugriff auf dieses komplexe System, damit er die Daten korrekt darstellen kann.
- Um dies effizient zu gewährleisten, wurde das Fassade Entwurfsmuster eingesetzt.
- Bei diesem wird eine Klasse als Schnittstelle für ein komplexes System eingesetzt~\cite{gamma2011entwurfsmuster}.
- Das Fassade Entwurfsmuster sorgt für eine gute Strukturierung des Codes, da die Anwendung einfacher in ihre verschiedenen Komponenten zerlegt werden kann.
- Ein weiteres verwendetes Entwurfsmuster ist das Singleton Entwurfsmuster. Bei diesem Entwurfsmuster geht es darum sicherzustellen,
- dass zur Laufzeit von einer Klasse immer nur ein einziges Objekt existiert~\cite{gamma2011entwurfsmuster}.
- Dieses Entwurfsmuster wurde bei der GUI für die Controller-Klassen aus Abbildung \ref{fig:controller} verwendet.
- Durch die Verwendung des Singleton Entwurfsmusters an dieser Stelle wird sichergestellt,
- dass immer nur ein einzelnes Controller Objekt von einer Klasse existiert. Dies ist sinnvoll, weil die Nutzereingaben nicht mehrfach verarbeitet werden sollen.
|