2012-09-09 11 views
8

Ich schreibe einen grafischen Editor für ein "Modell" (dh eine Sammlung von Boxen und Zeilen mit irgendeiner Art von Semantik, wie UML, deren Details hier keine Rolle spielen) . Ich möchte also eine Datenstruktur, die das Modell darstellt, und ein Diagramm, bei dem eine Bearbeitung des Diagramms eine entsprechende Änderung im Modell bewirkt. Wenn zum Beispiel ein Modellelement einen Text in einem Attribut hat und ich den Text im Diagramm bearbeite, möchte ich, dass das Modellelement aktualisiert wird.Zipper Datenstruktur für grafischen Modell Editor

Das Modell wird wahrscheinlich als Baum dargestellt, aber ich möchte, dass der Diagrammeditor so wenig wie möglich über die Modelldarstellung weiß. (Ich verwende das diagrams Framework, so dass die Zuordnung beliebiger Informationen zu einem grafischen Element einfach ist). Es wird wahrscheinlich eine "Modell" -Klasse geben, um die Schnittstelle zu kodieren, wenn ich nur herausfinden kann, was das sein soll.

Wenn ich dies in einer imperativen Sprache tun würde, wäre es einfach: Ich hätte nur eine Referenz vom grafischen Element im Diagramm zurück zum Modellelement. Theoretisch könnte ich das immer noch tun, indem ich das Modell aus einer riesigen Sammlung von IORefs aufbaute, aber das würde ein Java-Programm in Haskell schreiben.

Offensichtlich wird jedem graphischen Element eine Art Cookie zugeordnet, die die Aktualisierung des Modells ermöglicht. Eine einfache Antwort wäre, jedem Modellelement einen eindeutigen Bezeichner zu geben und das Modell in einer Data.Map-Nachschlagetabelle zu speichern. Dies erfordert jedoch eine signifikante Buchführung, um sicherzustellen, dass keine zwei Modellelemente die gleiche Kennung erhalten. Es erscheint mir auch als eine "stringly typed" Lösung; Sie müssen Fälle behandeln, in denen ein Objekt gelöscht wird, aber an anderen Stellen gibt es einen baumelnden Verweis darauf, und es ist schwierig, etwas über die interne Struktur des Modells in Ihren Typen zu sagen.

Auf der anderen Seite Oleg's writings über Reißverschlüsse mit mehreren Löchern und Cursor mit klaren transaktionalen Teilen klingt wie eine bessere Option, wenn ich es nur verstehen könnte. Ich bekomme die Grundidee von List- und Tree-Zippern und der Differenzierung einer Datenstruktur. Wäre es möglich, dass jedes Element in einem Diagramm einen Cursor in einen Reißverschluss des Modells hält? Wenn also eine Änderung vorgenommen wird, kann sie dann allen anderen Cursorn übergeben werden. Einschließlich Baumoperationen (z. B. Verschieben einer Teilstruktur von einem Ort zu einem anderen)?

Es würde mir an dieser Stelle besonders helfen, wenn es eine Art Tutorial über abgegrenzte Fortsetzungen gäbe, und eine Erklärung, wie Olegs Multi-Cursor-Reißverschlüsse arbeiten, die etwas weniger steil sind als Olegs Beiträge?

+1

Vielleicht Chung-chieh Shans Blog? : http://conway.rutgers.edu/~ccshan/wiki/blog/posts/WalkZip1/. Beachten Sie, Blobs (wxHaskell) ist eine Bibliothek zum Schreiben von "Netzwerk-Editoren" - es hat wahrscheinlich etwas verrottet, aber es kann weniger Arbeit sein, es zu aktualisieren als neu mit Diagrammen zu beginnen. –

+0

Danke. Ich habe angefangen, damit zu arbeiten. Ich werde dich wissen lassen, ob es zu einer Antwort führt. –

Antwort

2

Ich denke, dass Sie derzeit von einem Design arbeiten, bei dem jeder Knoten in der Modellstruktur durch ein separates grafisches Widget dargestellt wird und jedes dieser Widgets das Modell unabhängig aktualisieren kann. Wenn ja, glaube ich nicht, dass ein Multi-Loch-Reißverschluss sehr praktisch sein wird. Das Problem ist, dass die Komplexität des Reißverschlusses mit der Anzahl der Löcher wächst, die Sie unterstützen möchten. Wenn Sie mehr als 2 Begriffe bekommen, wird die Größe des Reißverschlusses ziemlich groß. Aus der Sicht der Differenzierung ist ein 2-Loch-Reißverschluss ein Reißverschluss über 1-Loch-Reißverschlüssen, so dass die Komplexität durch den Einsatz der Kettenregel wächst.

Stattdessen können Sie eine Idee von MVC ausleihen. Jeder Knoten ist weiterhin mit einem Widget verknüpft, kommuniziert jedoch nicht direkt. Vielmehr durchlaufen sie einen Zwischenregler, der einen einzigen Reißverschluss unterhält. Wenn Widgets aktualisiert werden, benachrichtigen sie den Controller, der alle Aktualisierungen serialisiert und den Reißverschluss entsprechend modifiziert.

Die Widgets benötigen weiterhin eine Art von Bezeichnern, um auf Modellknoten zu verweisen. Ich habe herausgefunden, dass es am einfachsten ist, den Pfad des Knotens zu verwenden, z. [0] für die Wurzel, [1,0] für das zweite Kind der Wurzel usw. Dies hat ein paar Vorteile. Es ist einfach, den Knoten zu bestimmen, auf den sich ein Pfad bezieht, und es ist auch einfach für einen Zipper, den kürzesten Pfad vom aktuellen Standort zu einem bestimmten Knoten zu berechnen.Für eine Baumstruktur sind sie auch bis zum Löschen und Wiedereinfügen einzigartig. Auch das ist normalerweise kein Problem, denn wenn der Controller benachrichtigt wird, dass Knoten gelöscht werden sollen, kann er die entsprechenden Widgets löschen und zugehörige Aktualisierungen ignorieren. Solange die Lebensdauer des Widgets an die Lebensdauer jedes Knotens gebunden ist, ist der Pfad ausreichend eindeutig, um Änderungen zu erkennen.

Für Tree-Operationen würde ich wahrscheinlich zerstören dann grafische Widgets neu erstellen.

Als Beispiel habe ich einen Code, der does this sort of thing. In diesem Modell gibt es keine separaten Widgets für jeden Knoten, vielmehr rendere ich alles mithilfe von Diagrammen und frage dann das Diagramm basierend auf der Klickposition ab, um den Pfad in das Datenmodell zu bekommen. Es ist bei weitem nicht vollständig, und ich habe es eine ganze Weile nicht angeschaut, so dass es vielleicht nicht funktioniert, aber der Code könnte Ihnen einige Ideen geben.

+0

Die Sache mit Olegs Version von Zippern ist, dass es nicht um die Differenzierung der Datenstruktur geht, sondern um Fortsetzungen, um einen partiellen Spaziergang zu erfassen. –

+0

@PaulJohnson - Auch wenn Fortsetzungen einen partiellen Spaziergang erfassen, gibt es immer noch die gleiche Anzahl von Zuständen, aus denen die Komplexität kommt. Es wird anders verwaltet, weil Oleg eine explizite Reihenfolge zwischen Fortsetzungen verwendet, was bedeutet, dass Sie die Komplexität verwalten müssen, indem Sie sicherstellen, dass die Fortsetzungen in der richtigen Reihenfolge aktualisiert werden. Ich bin nicht sicher, was in der Praxis einfacher ist, aber ich erwarte, dass jeder Ansatz nicht trivial ist. –

Verwandte Themen