Edit: Die Sache hat sich geklärt. Wer sich dafür interessiert, darf natürlich gerne trotzdem was dazu sagen. Aber Pfadfinderenergie braucht ihr hier nicht mehr aufwenden. 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hiho, folgende Situation:
Ich habe eine JTable, deren Daten bei jedem Neuzeichnen aktualisiert werden. (Die Tabelle zeigt Sensormessungen an)
Falls die Daten der entsprechenden Objekte sich geändert haben (muss nicht bei jedem Frame sein, KANN aber), wird das TableModel mit
neu befüllt.
Das Überprüfen und falls notwendig Aktualisieren des Datenmodels wird über die paintComponent des übergeordneten Panels (oberhalb der ScrollPane der Tabelle) angestoßen.
Etwa alle 10ms wird in der Anwendung ein repaint() gepostet. (Die Rate trifft natürlich nicht 100%, das ist aber nicht weiter schlimm) Die Sensordaten werden unabhängig, aus einem anderen Thread in die jeweiligen Objekte übertragen. (teilweise mit etwa 200Hz) Sobald die Tabelle neu gezeichnet wird, wird geprüft, ob sich an den Sensordaten etwas verändet hat. Falls das der Fall ist, wird das TableModel neu geschrieben.
Soweit funktioniert das auch ganz gut, ohne Darstellungsprobleme. Jetzt sagt mir aber mein Gefühl, dass es doch eigentlich schneller gehen müsste, wenn man
model.setDataVector(...) verwendet und die kompletten Daten auf einmal überträgt.
Allerdings setzt diese Methode wirklich nur den neuen Datenvektor. Anderes als setRowCount() und addRow(), feuert sie nicht die entsprechenden Events, dass sich die Daten geändert haben.
Dementsprechend ist die JTable dann "verwirrt" und muss ihre Darstellung neu ermitteln. Das sorgt anscheinend für böses Flimmern, weil die JTable erst ohne die passende Größe und dann korrekt resized angezeigt wird.
Das sieht dann so aus, als ob bei jedem zweiten Zeichnen die Tabelle mit der minimalen Feldbreite gezeichnet wird. Sie nutzt das ScrollPane nur zu einem Drittel aus. Im Wechsel dazu wird die Tabelle dann mit der angepassten Breite gezeichnet und füllt das ScrollPane komplett. Die Zeilen sind IMMER alle vorhanden und IMMER mit Daten befüllt. (weiteres zu "threadsafe" weiter unten)
Die Funktion setRowCount() feuert
und bei addRow() wird wieder "fireTableRowsInserted(rowCount, rowCount)" abgesetzt
Das AbstractTableModel sagt zu den Funktionen
Jetzt stellt sich die Frage, wie ich die Events absetzen muss, um den Datenvektor direkt schreiben zu können, ohne jede Zeile einzeln hinzuzufügen.
Wenn ich setRowCount(0) zum Löschen der Tabelle erstmal weiterverwende müsste anschließend doch ein
möglich sein?
Aber auch hier flimmert die Tabelle wieder.
Also stimmt da wohl etwas mit den Events noch nicht?
Weiterer Gedanke: Das DefaultTableModel ist nicht threadsafe. Man bekommt also Probleme, wenn man aus einem Thread Daten ändert und Swing in dem Moment gerade zeichnen will.
Das sollte aber eigentlich nicht das Problem sein. Die nicht-skalierte "schmale" Darstellung der Tabelle, enthält sämtliche Zeilen und alle Daten.
Es ist also nicht so, dass schon gezeichnet wird bevor die Tabelle wieder konsistent ist.
Außerdem wird die paintComponent ja vom Swing Renderthread ausgeführt. Egal ob die JTable nun vor oder nach dem Aktualisieren der Werte gezeichnet wird, kann der Thread das ja nicht mischen.
Desweiteren würde das Flimmern dann auch nicht verschwinden, wenn jede Zeile einzeln hinzugefügt wird.
Würde der Renderthread zwischen "setDataModel" und "fireEvent" unterbrechen, dann würde er das genauso auch während "addRow" tun, bevor dort intern "fireEvent" ausgeführt wird. (passiert aber nicht)
Zudem hab ich noch ein paar Tests gemacht. Ich finde keinen weg, die falsch skalierte, schmale Darstellung von Hand zu erzeugen.
Egal, was ich in die Tabelle schreibe, wird sie immer auf die volle Breite des ScrollPanes gestreckt.
Desweiteren werden auch immer alle Zeilen gesetzt. Es gibt keine Unterbechungen und kaskadierende Befüllung.
Joa, ziemlich viel Text für eine Sache, die bereits zufriedenstellend funktioniert.
Bin mal gespannt, ob überhaupt irgendwer was dazu sagen kann.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hiho, folgende Situation:
Ich habe eine JTable, deren Daten bei jedem Neuzeichnen aktualisiert werden. (Die Tabelle zeigt Sensormessungen an)
Falls die Daten der entsprechenden Objekte sich geändert haben (muss nicht bei jedem Frame sein, KANN aber), wird das TableModel mit
Code:
model.setRowCount(0);
"loop"
model.addRow(data[...])
Das Überprüfen und falls notwendig Aktualisieren des Datenmodels wird über die paintComponent des übergeordneten Panels (oberhalb der ScrollPane der Tabelle) angestoßen.
Etwa alle 10ms wird in der Anwendung ein repaint() gepostet. (Die Rate trifft natürlich nicht 100%, das ist aber nicht weiter schlimm) Die Sensordaten werden unabhängig, aus einem anderen Thread in die jeweiligen Objekte übertragen. (teilweise mit etwa 200Hz) Sobald die Tabelle neu gezeichnet wird, wird geprüft, ob sich an den Sensordaten etwas verändet hat. Falls das der Fall ist, wird das TableModel neu geschrieben.
Soweit funktioniert das auch ganz gut, ohne Darstellungsprobleme. Jetzt sagt mir aber mein Gefühl, dass es doch eigentlich schneller gehen müsste, wenn man
model.setDataVector(...) verwendet und die kompletten Daten auf einmal überträgt.
Allerdings setzt diese Methode wirklich nur den neuen Datenvektor. Anderes als setRowCount() und addRow(), feuert sie nicht die entsprechenden Events, dass sich die Daten geändert haben.
Dementsprechend ist die JTable dann "verwirrt" und muss ihre Darstellung neu ermitteln. Das sorgt anscheinend für böses Flimmern, weil die JTable erst ohne die passende Größe und dann korrekt resized angezeigt wird.
Das sieht dann so aus, als ob bei jedem zweiten Zeichnen die Tabelle mit der minimalen Feldbreite gezeichnet wird. Sie nutzt das ScrollPane nur zu einem Drittel aus. Im Wechsel dazu wird die Tabelle dann mit der angepassten Breite gezeichnet und füllt das ScrollPane komplett. Die Zeilen sind IMMER alle vorhanden und IMMER mit Daten befüllt. (weiteres zu "threadsafe" weiter unten)
Die Funktion setRowCount() feuert
Code:
// durch setRowCount Zeilen entfernt
fireTableRowsDeleted(rowCount, oldCount-1);
// durch setRowCount Zeilen hinzugefügt
fireTableRowsInserted(oldCount, rowCount-1);
Das AbstractTableModel sagt zu den Funktionen
Code:
fireTableRowsDeleted(int firstRow, int lastRow)
Notifies all listeners that rows in the range [firstRow, lastRow], inclusive, have been deleted.
Parameters:
firstRow the first row
lastRow the last row
fireTableRowsInserted(int firstRow, int lastRow)
Notifies all listeners that rows in the range [firstRow, lastRow], inclusive, have been inserted.
Parameters:
firstRow the first row
lastRow the last row
Jetzt stellt sich die Frage, wie ich die Events absetzen muss, um den Datenvektor direkt schreiben zu können, ohne jede Zeile einzeln hinzuzufügen.
Wenn ich setRowCount(0) zum Löschen der Tabelle erstmal weiterverwende müsste anschließend doch ein
Code:
model.setDataVector( "dataVector", "columnVector");
und
model.fireTableRowsInserted(0, "dataVector.size()");
Aber auch hier flimmert die Tabelle wieder.
Also stimmt da wohl etwas mit den Events noch nicht?
Weiterer Gedanke: Das DefaultTableModel ist nicht threadsafe. Man bekommt also Probleme, wenn man aus einem Thread Daten ändert und Swing in dem Moment gerade zeichnen will.
Das sollte aber eigentlich nicht das Problem sein. Die nicht-skalierte "schmale" Darstellung der Tabelle, enthält sämtliche Zeilen und alle Daten.
Es ist also nicht so, dass schon gezeichnet wird bevor die Tabelle wieder konsistent ist.
Außerdem wird die paintComponent ja vom Swing Renderthread ausgeführt. Egal ob die JTable nun vor oder nach dem Aktualisieren der Werte gezeichnet wird, kann der Thread das ja nicht mischen.
Desweiteren würde das Flimmern dann auch nicht verschwinden, wenn jede Zeile einzeln hinzugefügt wird.
Würde der Renderthread zwischen "setDataModel" und "fireEvent" unterbrechen, dann würde er das genauso auch während "addRow" tun, bevor dort intern "fireEvent" ausgeführt wird. (passiert aber nicht)
Zudem hab ich noch ein paar Tests gemacht. Ich finde keinen weg, die falsch skalierte, schmale Darstellung von Hand zu erzeugen.
Egal, was ich in die Tabelle schreibe, wird sie immer auf die volle Breite des ScrollPanes gestreckt.
Desweiteren werden auch immer alle Zeilen gesetzt. Es gibt keine Unterbechungen und kaskadierende Befüllung.
Joa, ziemlich viel Text für eine Sache, die bereits zufriedenstellend funktioniert.
Bin mal gespannt, ob überhaupt irgendwer was dazu sagen kann.
Zuletzt bearbeitet: