Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Ich befinde mich bei meiner JOGL_GUI in einem recht weit fortgeschrittenen Zustand.
Letzte Woche ist es mir gelungen eine scrollbare Komponente zu implementieren. Mit der kann ich jetzt theoretisch beliebig viele Daten in einem begrenzten Raum anzeigen.
Der letzte Programmierschritt vor der Vorstellung des Projekts ist eine scrollbare Tabelle.
Vor diesem Brocken sitze ich jetzt schon eine Weile und grüble, wie ich das so designe, dass es simple zu nutzen ist, wenig Code benötigt - vor allem wenig Sonderfallbehandlungen, und am besten auf Bestehendes aufbaut.
Ich schildere erstmal was die Tabelle können soll:
- vertikal feststehende Kopfzeile (Tableheader) -> aber horizontal scrollbar
- horizontal feststehende Kopfspalte (Tableheader) -> aber vertical scrollbar
- ob und welche Header sichtbar sind, soll einstellbar sein.
- Datenbereich 2dimensional scrollbar.
- Tabellenzellen können Text oder eine beliebige andere Komponente (z.B. Button) enthalten.
- Tabellenzellen sollen festlegen ob sie einen Rahmen haben und wie dick dieser ist und in welcher Farbe.
- Tabellenzellen sollen ein Padding haben und eine beliebige Hintergrundfarbe
Meine bisherigen Überlegungen:
- Die Tabelle soll von der TglScrollComponent abgeleitet werden. Diese kann bereits genau 1 Komponente beliebiger Größe scrollen. Auf diesem Scrollbereich sollen die Zellen abgelegt werden. Zusätzlich zum Datenbereich sollen zwei weitere Bereiche definiert werden, welche die Headerzeile bzw. spalte enthalten soll. Diese sollen über die Scrollbars des Datenbereich mitgescrollt werden, allerdings nur in die, für den jeweiligen Header zulässige, Richtung.
- Die Zellen werden durch eine eigene Klasse "TglTableCell" abgebildet. Von dieser Klasse gibt es zwei unterformen: "TglTableTextCell" und "TglTableComponentCell".
So. Das klingt ja bisher alles ganz fluffig. Allerdings kam ich in meinen Gedankenexperimenten an die Stelle, wo ich Daten in die Tabelle füllen wollte. Wie mach ich das am besten?
Ich mein, wie sollte man am besten mit einer Tabelle arbeiten können, damit sie praktisch ist.
Und wie sollte man es einrichten, dass man solche Sachen wie Padding, Hintergrundfarbe, Border etc. für alle Zellen einer Zeile resp. Spalte setzen kann.
Mir fehlt hier konkret noch die nichtsichtbare(n) Datenstruktur(en) die im Hintergrund die Daten halten und die Getter und Setter dafür.
Für Inspiration wäre ich dankbar.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Mir fehlt hier konkret noch die nichtsichtbare(n) Datenstruktur(en) die im Hintergrund die Daten halten und die Getter und Setter dafür.
Am besten gar keine ... Würde dafür Events nehmen, die erfragen, was denn bitte in der Tabelle in Zelle x,y drin steht und evtl. sogar ein draw zur verfügung stellen - das erhöht die Flexibilität von solchen Tabellen dramatisch. Weil im Prinzip hast Du ja keine Ahnung, ob die Tabelle dünn oder stark bestzt ist, und Du vielleicht versuchst mit völlig falschen Methoden die Daten zu halten. Kannst ja dann immernoch eine weitere Klasse ableiten, die ein 2D-Array von Strings trägt, in das man seine Daten hineinlegen kann. Wenn es außerdem eine Möglichkeit gibt, sicht+interagierbare komponenten in die Zellen hineinzulegen ist das auch immer nicht schlecht....
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Ja, der Ansatz von Nico ist denke ich ganz gut.
Du kannst dabei am besten mal in die TVirtualTreeView von Mike Lischke reinschauen. Das ist zwar ein Treeview, aber mit einem Optionsswitch kannst du daraus ein Grid machen... Ist also Mächtig das ding. Das arbeitet auch nach dem Prinzip für-alles-ein-Event. Im Prinzip hat das ding garkeine wirklichen Datenstrukturen (außer interne, die die Verkettung der Elemente angben) und ruft beim Zeichnen immer per Events alles ab, was es braucht. Zusätzlich hat der Benutzer die möglichkeit, einen Pointer oder ein Record in den genannten internen Strukturen abzulegen und aus diesen abzurufen, sodass man sich selber trotzdem eine menge aufbauen kann.
Zum Beispiel legt man in jedem Node ein Objekt ab, welches Methoden für GetText, Paint und ähnliches hat. Dann behandelt man die Events, indem man sich den Pointer auf das Objekt rausreichen lässt und die entsprechende Methode im Objekt aufruft.
Für deine Tabelle würde ich sagen, ist das auch ein garnicht so schlechter ansatz. Du könntest ein 2D-Array aus Feldern erstellen, wo du platz für einen Pointer lässt. Und dann beim erzeugen der Tabelle bzw beim Festlegen der Größe rufst du dann ein Event auf, welches die möglichkeit hat, einen Pointer zurückzugeben, der dann in deiner Struktur abgelegt wird.
Beim Zeichnen rufst du dann ein Event auf, welches die Koordinaten übergeben bekommt und einen Text zurückliefert oder so. Oder einfach nur ein Paint event. Oder was-auch-immer.
Dann könntest du davon eine Klasse ableiten, die diese Events selber überschreibt und dann Anstatt eines einfachen Pointers Referenzen auf Objekte annimmt oder sowas. Ich glaube, da sind der Phantasie keine Grenzen gesetzt.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Für den Datenbereich könnte man vielleicht so eine Schnittstelle verwenden:
Die Tabelle enthält ein 2D Array mit den gerade sichtbaren Zellen.
Wenn gescrollt wird (oder am Anfang) werden die Zellen neu aus dem Model angefordert.
Code:
interface ITableModel
{
int GetRows();
int GetColumns();
TglTableCell GetCell(int row, int column);
}
Eine Klasse die z.B. das kleine 1x1 anzeigt, sähe z.B. so aus.
Code:
public class TestModel implements ITableModel
{
public int GetRows()
{
return 10;
}
public int GetColumns()
{
return 10;
}
public TglTextCell GetCell(int row, int column)
{
return new TglTextCell(((row+1) * (column+1)).toString());
}
}
Ein Kritikpunkt wäre, dass hier die Daten und deren Darstellung nicht voneinander getrennt sind. Daher ist die Klasse eigentlich kein Model sondern ein ViewModel. Falls man dies benötigt wäre es über ein Adapter möglich. Davon braucht aber die Tabelle nichts zu wissen.
z.B. wäre ein ViewModel denkbar, dass seine Informationen über Reflection ermittelt und man über Annotationen festlegt, wie ein Feld dargestellt werden soll.
Vermutlich braucht man keine Benachrichtigungen, da bei OpenGL sowieso immer alles gezeichnet wird. Ansonsten kann so ein Modell optional noch Observable implementieren.
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Also danke erstmal für die Antworten.
Zuerst zu Lars:
Du würdest es also so machen, dass alle Daten die in einer Tabelle angezeigt werden sollen in einer beliebigen Datenstruktur liegen, hauptsache diese DS bietet ein Interface an, aus dem dann die Tablle z.b. zum Rendern die Daten liefert?
Das ist keine schlechte Idee. Wenn ich die Idee dahingehe ausbaue, dass es ein ITableModel gibt von dem ein ITableModelText und ein ITableModelComponent abgeleitet wird, dann könnte das ein interessanter Ansatz werden.
@Nico und L.H.:
So richtig verstanden hab ich eure Idee noch nicht. Könntet ihr eventuell mal einen Pseudokode tippen wie man mit einer Tabelle umgehen soll die eurem Prinzip folgt? Dann wirds vielleicht klarrer.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Hallo Flash,
Na, zum Beispiel eine Objektliste.
Jedes Objekt kennt seinen Index(X/Y) in der Tabelle; sortiert sind sie nach X und innerhalb der X nach Y (oder umgekehrt, je nach Geschmack). Auf diese Art sind sie mit einem schnellen Suchalgorithmus gut wiederzufinden. Leere Zellen brauchen keinen Eintrag. Fürs Einfügen und Löschen von Zeilen und Spalten muss man dann ein wenig tüfteln.
In den Spalten stecken dann die Zellinhalte als Objekt: ein Objekt mit seinen eigenen Daten, Methoden und Ereignissen: z.B. „OnDrawCell“, was Du eben brauchst.
Ah ja, und nicht vergessen: wenn die Zellen editierbar sein sollen, sollte man ein Undo einbauen. Ich sage das, weil man das Undo am Besten von Anfang an berücksichtigt, denn später einbauen wird immer ein Krampf.
Das Interface fürs Rendern ist natürlich gut, wenn man verschiedene Graphic Bibliotheken „dranstecken“ will, muss aber nicht sein.
Traude
EDIT: Entschuldigung, es muss natürlich oben heißen: "In den Objekten stecken dann die Zellinhalte" und nicht "In den Spalten stecken dann die Zellinhalte als Objekt". Es sollen ja gerade eben keine Spalten sein.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Um meinen Vorschlag zu verdeutlichen:
Code:
// Pseudocode
function drawCell()
clearCellBackground // eventuell, wird ja wahrscheinlich zusammen mit
// dem ganzen rest ausgelöscht, wenn du nicht mit Invalidate arbeitest
callEvent('afterClean', CellPointer)
text = retreiveTextViaEvent('getText', CellPointer)
callEvent('beforePaint', CellPointer)
if (not callEvent('drawCell', CellPointer))
drawCellBorder
drawCellContents(text)
callEvent('afterPaint', CellPointer)
end
CellPointer ist halt jeweils ein Pointer, der genau die Zelle beschreibt. Ich würde sagen, dass das eigentlich drei Werte sind: X, Y und Pointer auf die Zellstruktur, woraus man dann eventuell ein Userdata-Feld bekommen kann oder so.
So sähe dann eine Zeichenmethode nach dem Virtual Paradigm aus. Wie gesagt, schau dir am besten mal die VirtualTree View Komponente. Du musst dir ja nicht die > 1MiB Source code reinziehen, allein die Dokumentation gibt schon eine menge Auskunft. Ich kann dir ja mal ein Projekt zukommen lassen, wo ich die VirtualTreeView-Komponente verwende und anspreche, dann siehst du vielleicht besser, was ich meine. Allerdings wird das noch einen Moment dauern, da ich gerade in der Schule sitze und dementsprechend wenig Zugriff auf meine Projekte habe.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Also, ich habe jetzt eine Weile darüber nachgedacht und rausgekommen ist eine ziemlich tolle Skizze auf der Rückseite eines Briefumschlags und die Idee mal eine (wie ich denke) ordentliche Model View Controller Architektur in einem Tutorial zu erklären.
Ich denke ich werde die Idee von Lars aufgreifen und ein Interface für das GUI Interne Datenmodell anbieten. Das Werde ich in 2 Ausführungen anbieten. Für Text und für Komponenten. Über das Komponenteninterface kann die Tabelle dann Ereignisse (Mouseclick etc) an die Componenten weiterleiten.
Ich hatte mir sorgen gemacht, dass der Umgang mit der GUI (bzw. Tabelle) zu viele Klassen nötig macht. Aber wie man in der Skizze sieht, sind dafür die GUI Controller der Anwendung zuständig. Und die müssen ja sonst nicht viel mehr machen.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Wo ist das kreuz, wo ist das kreuz, ich will den Schatz finden. Oh... Ist ja garkeine Schatzkarte
Letzendlich läuft es dann ja auf das gleiche hinaus wie bei den Events, nur dass das ganze in Interfaces gekapselt ist. Auch nicht schlecht.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Mitglieder in diesem Forum: 0 Mitglieder und 16 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.