Swing GUI update vs. Process Speed

Status
Nicht offen für weitere Antworten.

hdi

Top Contributor
Hallo,

etwas kryptischer Titel.. Gegeben ist ein JFrame mit nem Table, der beim setVisible(true) des Frames noch keine Daten enthält. Danach werden Daten geaddet, und entsprechend immer ein fireTableDataChanged() aufgerufen (auf dem EDT).

Nun konnte ich folgendes beobachten: Sagen wir wir haben 1000 Datensätze, die eingelesen werden. Lass ich das voll durchrechnen, also ohne sleeps zwischen dem Lesen der einzelnen Datensätze (warum auch künstlich in die Länge ziehen?), dann ist das Frame beim Programm-Start ne knappe Sekunde leer. Also noch nicht gemalt, außer dem Rand und einer weißen Fläche. Nachdem alle Daten gelesen wurden, erscheint der Table usw.

...wenn ich jetzt zwischen den Datensätzen ein kurzes Sleep einbaue (2 ms reicht schon), dann ist der Table und alles beim Programmstart sofort da, und man sieht wie sich der Table füllt.

Ich erklär mir das so: Beim Einlesen der Daten wird 1000 mal geaddet, und 1000 mal fireTableDataChanged() aufgerufen. Aber der EDT bekommt scheinbar keine Rechenzeit zugewiesen?? Wenn ich in meiner Einlese-Schleife kurze Pausen mache, hat der EDT Zeit zum repainten.

...aber irgendwie kann das ja nicht sein!? Also muss man selber darauf achten dass der EDT genug Rechenzeit kriegt...? Dann müsste man ja irgendwie alle 200 Zeilen oder so nen sleep im Programm einbauen. Es macht ja keinen Unterschied ob ich in einer Schleife hänge oder einfach meine Programmlogik durch hundert Methoden läuft.
Die JVM hat doch da ein internes Scheduling, sodass der EDT auch regelmässig drankommt. So dachte ich zumindest. Aber wie erklärt man sich dann dieses Phänomen? Kann mich bitte jmd aufklären.. Danke!

lg
 
Zuletzt bearbeitet:

André Uhres

Top Contributor
Aber wie erklärt man sich dann dieses Phänomen?
Wenn die EDT-Queue mit invokeLater schneller gefüllt wird, als sie abgearbeitet werden kann, dann sieht man logischerweise nicht jeden Schritt, sondern jeweils nur einen nach vollständigem Abarbeiten der Queue. Alternativ kannst du auch invokeAndWait versuchen, dann siehst du alle Schritte (der Prozess wird dadurch selbstverständlich verlangsamt).
Übrigens würde ich nach einem Insert keinen fireTableDataChanged machen, sondern einen fireTableRowsInserted.
 
Zuletzt bearbeitet:

byte

Top Contributor
Ansonsten wäre ein
Code:
 Thread.yield()
in diesem Fall besser, denn Du willst den Worker-Thread ja gar nicht wirklich schlafen legen.
 

André Uhres

Top Contributor
Ansonsten wäre ein
Code:
 Thread.yield()
in diesem Fall besser, denn Du willst den Worker-Thread ja gar nicht wirklich schlafen legen.
Das ändert aber wenig an dem beschriebenen Phänomen, denn mit Thread.yield kann die EDT-Queue trotzdem schneller gefüllt werden, als sie abgearbeitet werden kann ;) . Das gleiche gilt für den Vorschlag von Wildcard.
 
Zuletzt bearbeitet:

Wildcard

Top Contributor
Das ändert etwas, da man nicht hundert und tausendfach das Table Modell updaten muss. Man berechnet im Hintergrund, zeigt die Progress bar an und setzt anschließend das Table Modell
 

hdi

Top Contributor
Danke für eure Antworten!

Übrigens würde ich nach einem Insert keinen fireTableDataChanged machen, sondern einen fireTableRowsInserted.
Hatte ich zuerst auch, allerdings weiss ich nicht immer welche Indices ich der Methode mitgeben soll. Denn wenn mein Table SortKeys hat, ist das neueste Element nicht immer das letzte. Und ein convert kann ich ja nicht mal aufrufen weil mein TableModel kennt den Table gar nicht. D.h. wenn schon dann kann ich nur ein
Java:
fireTableRowsInserted(0,mydata.length()-1);
machen. Ist das denn schneller als ein fireTableDataChanged?

Ansonsten wäre ein Thread.yield() in diesem Fall besser, denn Du willst den Worker-Thread ja gar nicht wirklich schlafen legen.
Hatte ich auch versucht, aber siehe André's Antwort: Das führt zum gleichen Effekt wie wenn ich es komplett weglasse.

Alternativ kannst du auch invokeAndWait versuchen, dann siehst du alle Schritte (der Prozess wird dadurch selbstverständlich verlangsamt).
Das werde ich dann jetzt wohl machen. Ihr seht es doch auch so, dass man lieber sofort ne komplette GUI hat, und sieht wie die Daten eingeladen werden, als dass man beim Start ein weißes Fenster hat dass erstmal "hängt". Auch wenn es in ersterem Fall vllt etwas länger dauert...
Oder seht ihr das anders?

..ich könnte ja auch einen counter in meine Schleife einbauen und nur bei jedem zehnten Element ein invokeAndWait aufrufen oder so.. Ich spiel mal damit rum.

Danke auf jeden Fall für eure Antworten!
 

hdi

Top Contributor
Ich meld mich nochmal.. ich hab grad eben wohl zu voreilig geantwortet. Wenn ich nochmal so drüber nachdenke, verstehe ich es noch immer nicht..

Also mit invokeAndWait funzt es, ist aber in der Tat sehr langsam. Zu langsam (bei 1000 Elementen dauert es gut 8 Sekunden @2x3.75 Ghz).

Ich hab jetzt nochmal die Variante mit invokeLater und einem sleep(1) versucht. Ich krieg da auch wieder Race Conditions.

Also nach etwas hin und her sieht es jetzt so aus, das ist schnell genug und bisher auch keine Fehler, ich hoffe ich mache alles richtig:

Java:
	public void addMovie(final Movie m) {
		movies.add(m);
	}

	public void readDBFile() {
		for (int i = 1; i <= 1000; i++) {
			final Movie m = new Movie();
			m.setTitleGerman("Film % " + (i % 5));
			m.setRating(i);
			addMovie(m);
			if (i % 20 == 0) {
				fireMovieAdded();
			}
		}
		fireMovieAdded();
	}

	public void fireMovieAdded() {
		try {
			EventQueue.invokeAndWait(new Runnable() {
				@Override
				public void run() {
					fireTableRowsInserted(0, movies.size() - 1);
				}
			});
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

Das einzige Drawback ist, dass ich jetzt in die addMovie() nicht mehr direkt ein fire() einbauen kann. Sprich wenn ich später zur Laufzeit neue Movies adde muss ich selber die fireMovieAdded() aufrufen. Normalerweise sieht man ja in Bsp dass die fire() immer gleich in einer add/remove Methode drinnen ist. Aber mein Gott, muss ich halt dran denken xD

...ansonsten, passt das jetzt so? Oder seht ihr einen Fehler :bahnhof:

Merci
 

André Uhres

Top Contributor
wenn mein Table SortKeys hat, ist das neueste Element nicht immer das letzte
Ob JTable einen RowSorter hat oder nicht, sollte dem Model egal sein. Sortiert wird ja nur die View, nicht das Model.
@Wildcard: achso, ja dann hast du recht. Ich dachte, du wolltest die Progressbar zusätzlich anzeigen.
 

hdi

Top Contributor
@André

Bei
Java:
fireTableRowsInserted(0, movies.size()-1);
funktioniert alles, wenn ich die 0 aber ändere, zB in
Java:
fireTableRowsInserted(movies.size()-1, movies.size()-1); // es wurde ein Element ans Ende der Liste geaddet
dann bekomme ich während meines Einelese-Vorgangs permanent die Exception:

java.lang.reflect.InvocationTargetException
at java.awt.EventQueue.invokeAndWait(Unknown Source)
at core.Database.fireMovieAdded(Database.java:108)
at core.Database.readDBFile(Database.java:131)
at core.Start.main(Start.java:24)
Caused by: java.lang.IndexOutOfBoundsException: Invalid range
at javax.swing.DefaultRowSorter.checkAgainstModel(Unknown Source)
at javax.swing.DefaultRowSorter.rowsInserted(Unknown Source)
at javax.swing.JTable.notifySorter(Unknown Source)
at javax.swing.JTable.sortedTableChanged(Unknown Source)
at javax.swing.JTable.tableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableRowsInserted(Unknown Source)
at core.Database$1.run(Database.java:111)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Das passiert sobald ich nicht die komplette Range der Liste angebe, d.h. auch schon wenn ich
Java:
fireTableRowsInserted(1, movies.size()-1);
schreibe.

Den Code, wie er im Moment ist, habe ich in meinem letzten Post gepostet.
 

André Uhres

Top Contributor
Das einzige Drawback ist, dass ich jetzt in die addMovie() nicht mehr direkt ein fire() einbauen kann.
Das können wir ja über einen boolean steuern, etwa so (ich bekomme übrigens keine IndexOutOfBoundsException):
Java:
        public void readDBFile() {
            for (int i = 1; i <= 1000; i++) {
                final Movie m = new Movie();
                m.setTitleGerman("Film % " + (i % 5));
                m.setRating(i);
                addMovie(m, false);
            }
            fireMovieAdded(getRowCount() - 1, true);
        }

        public void addMovie(final Movie m) {
            addMovie(m, true);
        }

        public void addMovie(final Movie m, final boolean normal) {
            int row = movies.size();
            movies.add(m);
            if (normal) {
                fireTableRowsInserted(row, row);
            } else {
                fireMovieAdded(row, false);
            }
        }

        public void fireMovieAdded(final int row, boolean end) {
            if (row % 20 == 0 || end) {
                try {
                    EventQueue.invokeAndWait(new Runnable() {

                        @Override
                        public void run() {
                            fireTableRowsInserted(row - 20, row);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
 
Zuletzt bearbeitet:

hdi

Top Contributor
Also den Code finde ich ehrlich gesagt etwas schlecht verständlich.
Da ruf ich lieber nach einem addMovie() ein fireMovieAdded() auf und fertig.

Aber du sagst, du kriegst keine Exceptions? Also ich schon..
Und ich bin noch immer der Überzeugung dass das was mit dem RowSorter zu tun hat, denn

1) wenn ich den nicht setze, bekomme ich keine Exception
2) die Exception sieht auch so aus als gäbe es ein Prob mit dem Sorter:

Caused by: java.lang.IndexOutOfBoundsException: Invalid range
at javax.swing.DefaultRowSorter.checkAgainstModel(Unknown Source)
at javax.swing.DefaultRowSorter.rowsInserted(Unknown Source)
at javax.swing.JTable.notifySorter(Unknown Source)
at javax.swing.JTable.sortedTableChanged(Unknown Source)
at javax.swing.JTable.tableChanged(Unknown Source)

Also das würde ich schon gerne klären, denn für mich sieht das so aus als ob fireTableRowsInserted sich auf den viewIndex bezieht, nicht aufs Model. Der Parameter heisst ja auch "rowIndex". Das klingt schon so nach Tabelle...

Steht jetzt leider in der API ncht explizit da. Da sollte man ja schon davon ausgehen dass es das Model ist. Aber warum dann die Exceptions? Selbst wenn zwischen dem fire()-invoke noch weitere Movies geaddet werden, (was eig. nicht sein kann weil ich ja invokeAndWait verwende) wird die Size ja nur immer größer. Also wieso indexOutOfbounds?

Also mit dem Sorter hat das irgendwas zu tun.. Die Frage ist was ich falsch mache.
Es ist übrigens egal ob ich tatsächlich SortKeys habe, oder lediglich einen "leeren" RowSorter. Sobald ich folgende Zeile in den Konstruktor des Tables einbaue:

Java:
setRowSorter(new TableRowSorter<Database>(db)); // db instanceof Database

bekomme ich die Exception. Lasse ich sie weg, ist alles wunderbar.

edit: Was ich grad etwas verächtig finde:
Wieso steht in der Exception was vom DefaultRowSorter, wo ich doch einen eigenen gesetzt habe? Aber das muss nix heißen, ist mir grad nur aufgefallen.

edit2
Ich hab hier grad was geschrieben, aber das war Quatsch
 
Zuletzt bearbeitet:

hdi

Top Contributor
So also.. ich hab jetzt noch etwas rumprobiert. Irgendwie find ich das alles etwas seltsam, deshalb poste ich euch jetzt meinen Code, auch wenn er für mich jetzt wunderbar funktioniert.. Vllt mach ich ja doch wieder etwas falsch oder total blöd:

Java:
	public void addMovie(final Movie m) {
		movies.add(m);
	}

	public void readDBFile() {
		for (int i = 1; i <= 1000; i++) {
			final Movie m = new Movie();
			m.setTitleGerman("Film " + i);
			addMovie(m);
			if (i == 1 || i % 10 == 0) {
				updateTable();
			}
		}
		updateTable();
	}

	public void updateTable() {
		try {
			EventQueue.invokeAndWait(new Runnable() {
				@Override
				public void run() {
					fireTableDataChanged();
				}
			});
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

Also zum ersten nehme ich jetzt doch fireTableDataChanged(). Mit dem fireRowsInserted() klappt irgendwas nicht, mir fliegen da wegen dem RowSorter Exceptions um die Ohren..

Beim Einlesen der Daten update ich den Table nach dem ersten Element, und zwar mit invokeAndWait. Damit baut sich die komplette GUI sofort auf und man sieht alles. (kein Fenster mit Rahmen aber ohne Inhalt)
Dann update ich das ganze nach jeden 10.Film.
Bei mir (2x3.75 ghz, 7400 rpm hdd) dauert das Einlesen von 1000 Filmen 3 Sekunden mit dieser Technik, man sieht aber eben sofort die volle GUI, und ich blende ein GlassPane ein "Lese Datenbank...", während man im Hintergrund sieht wie sich die Tabelle füllt.
Und 1000 Filme ist eh schon sehr viel, denn das Programm soll für Privat-Anwender sein, mit dem man seine eigene Film-Sammlung verwalten kann.

Also damit kann ich mich anfreunden, und ich bekomme jetzt keine Fehler.

Natürlich wäre es mir nach wie vor lieber, es so performant zu machen wie möglich. Aber mit invokeLater klappt das gar nicht, weil er einfach nicht dazu kommt etwas zu malen, was eben beim Programmstart zu diesem hässlichen, halb aufgebauten JFrame führt. Also brauch ich invokeAndWait. Und das nach jedem adden aufzurufen, dafür ist wohl fireTableDataChanged zu rechenintensiv, oder der EDT zu lahm beim malen. (was ich gerne glaube, Swing ist ja echt nicht der Held wenn es ums Malen geht, siehe JFrame resize)

Naja also wenn mir jmd das noch "absegnen" würde, würde ich mich freuen. Ich will nur wissen ob ich nicht irgendwas total schlimmes wieder getan habe :D
 

André Uhres

Top Contributor
Also den Code finde ich ehrlich gesagt etwas schlecht verständlich. Da ruf ich lieber nach einem addMovie() ein fireMovieAdded() auf und fertig.
admitted :D

hdi hat gesagt.:
Aber du sagst, du kriegst keine Exceptions?
Sorry, ich habe doch Exceptions. Wir müssen addMovie auch auf dem EDT aufrufen, etwa so:
Java:
public void readDBFile() {
    for (int i = 1; i <= 1000; i++) {
        final Movie m = new Movie();
        m.setTitleGerman("Film % " + (i % 5));
        m.setRating(i);
        Runnable addMovie = new Runnable() {

            public void run() {
                addMovie(m);
            }
        };
        SwingUtilities.invokeLater(addMovie);
        if (i % 20 == 0) {
            fireMovieAdded();
        }
    }
    fireMovieAdded();
}
 

hdi

Top Contributor
Ahh ich glaube jetzt hab ich den Dreh endgültig raus :)

Wir müssen addMovie auch auf dem EDT aufrufen

Ich bin mal so dreist und sage: Ne ;)
Denn wenn ich addMovie() vor fire() aufrufe, dann ist doch auf jeden Fall die Liste um ein Element erweitert, wenn fire() ausgeührt wird.
Ich meine es kann sein, dass bei

Java:
fire(); // on EDT (invokeLater, nicht invokeAndWait!)
add(); // not on EDT

zuerst add() ausgeführt wird. Aber andersrum kann das doch nicht sein, also wenn ich add() vor fire() ausführe, kann sicherlich nicht fire() zuerst ausgeführt werden.

Das heißt das ist nicht der Fehler, der Fehler war dass ich natürlich ne Exception krieg bei:

Java:
fireTableRowsInserted(movies.size() - 1, movies.size() - 1);

weil ich im Einlese-Loop diese Methode nur jedes zehnte mal ausführe. Also ist ein

Java:
fireTableRowsInserted(movies.size() - 11, movies.size() - 1);

angebracht. Und damit hab ich bisher keinen Fehler bekommen, und spüre einen deutlichen Performance-Boost im Vergleich zu fireTableDataChanged().
(Voher 3 Sek. bei 1000 Filmen, jetzt 1 Sek.)

:toll:
 

hdi

Top Contributor
Mein Fehler war grad dass ich angenommen habe, dass das nur einmal passiert. Ein einziges mal add() und dann fire() garantiert halt schon, dass zuerst add() ausgeführt wird und dann fire(). Da sind wir uns ja wohl einig, oder? Immerhin ist es nur 1 Thread.

Aber das passiert ja in einer Schleife, d.h. das vorher "requestete" fire() kann tatsächlich ausgeführt werden, während mein Thread grad beim adden ist. Da hast du Recht.
Also werd ich das auf den EDT legen. Trotzdem war der "größere" Fehler bei mir das mit den falschen Indices beim fire(). Zumindest hab ich bisher, auch ohne add() auf EDT, noch nie einen Fehler bekommen.

So.. aber jetzt ham 'mers mal zam ;) add() auf EDT und damit ist alles okay. Dank dir!
 
Zuletzt bearbeitet:

hdi

Top Contributor
edit:
Puhh... so, das war echt gar nicht so leicht. Jetzt bin ich mir zu ~100% sicher dass ich alle Fälle abgedeckt hab. Es werden jetzt alle Einträge angezeigt und das ganze sieht gut aus und geht auch recht flott:

Java:
	public void addMovie(final Movie m) {
		movies.add(m);
	}

	public void readDBFile() {
		/*
		 * Number of elements to be added until the table gets an update
		 * notification
		 */
		final int tableUpdateEveryNth = 20;
		/*
		 * The current number of elements added
		 */
		int count = 0;
		for (int i = 0; i <= 100; i++) {
			final Movie m = new Movie();
			m.setTitleGerman("Film " + i);
			try {
				EventQueue.invokeAndWait(new Runnable() {
					@Override
					public void run() {
						addMovie(m);
					}
				});
				count++;

				/*
				 * If this is the very first element, it should cause a table
				 * update, so that the GUI is displayed immediately. For
				 * following updates, we use the variable holding the update
				 * period.
				 */
				if (count == 1 || count % tableUpdateEveryNth == 0) {
					EventQueue.invokeAndWait(new Runnable() {
						@Override
						public void run() {
							/*
							 * Note that is causes no problem when the
							 * firstIndex is < 0
							 */
							fireTableRowsInserted(movies.size()
									- tableUpdateEveryNth, movies.size() - 1);
						}
					});
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}

		/*
		 * There may still be some elements that have not yet been painted.
		 * (This might be the case if count % tableUpdateEveryNth does not add
		 * up)
		 */
		int calcNotPainted = 0;
		if (count % tableUpdateEveryNth > 0) {
			calcNotPainted = (count / tableUpdateEveryNth > 0 ? count
					% tableUpdateEveryNth : count);
		}
		final int toPaint = calcNotPainted;
		if (toPaint > 0) {
			try {
				EventQueue.invokeAndWait(new Runnable() {
					@Override
					public void run() {
						fireTableRowsInserted(movies.size() - toPaint, movies
								.size() - 1);
					}
				});
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
 
Zuletzt bearbeitet:

André Uhres

Top Contributor
  • Mit "wesentlicher" meinte ich, daß die dadurch verursachten Probleme schwerer nachvollziehbar sind.
  • Die Abfrage auf "count == 1 ||" ist suspekt und muss entfernt werden. Eine einzeilige Tabelle kann eh nicht früher am Bildschirm erscheinen als eine leere.
  • Den ersten Aufruf von "invokeAndWait" sollten wir in "invokeLater" umändern. Es gibt keinen Grund, hier schon zu warten.
  • Das Ganze könnte dann etwa so aussehen:
Java:
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
public class MovieTest extends JFrame {
    private final JButton btLoad;
    private final MyModel model;
    private final JTable table;
    public MovieTest() {
        super("MovieTest");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(800, 300);
        setLocationRelativeTo(null);
        btLoad = new JButton("Load");
        model = new MyModel();
        table = new JTable(model);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        table.setAutoCreateRowSorter(true);
        table.getRowSorter().toggleSortOrder(0);
        getContentPane().add(btLoad, BorderLayout.PAGE_START);
        getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
        btLoad.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent evt) {
                load();
            }
        });
        load();
    }
    private void load(){
        new Thread(new Runnable() {
            public void run() {
                btLoad.setEnabled(false);
                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                model.readDBFile();
                btLoad.setEnabled(true);
                setCursor(Cursor.getDefaultCursor());
            }
        }).start();
    }
    public static void main(final String[] args) {
        Runnable gui = new Runnable() {
            public void run() {
                new MovieTest().setVisible(true);
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}
class MyModel extends AbstractTableModel {
    private List<List> movies;
    public MyModel() {
        movies = new ArrayList<List>();
    }
    public int getRowCount() {
        return movies.size();
    }
    public int getColumnCount() {
        return 12;
    }
    @Override
    public String getColumnName(final int column) {
        return "Movie";
    }
    @Override
    public Class<?> getColumnClass(final int columnIndex) {
        return Movie.class;
    }
    public Object getValueAt(final int rowIndex, final int columnIndex) {
        return movies.get(rowIndex).get(columnIndex);
    }
    public void addMovie(final Movie m) {
        List rowData = new ArrayList();
        rowData.add(m);
        for (int column = 1; column < getColumnCount(); column++) {
            rowData.add("Test_" + (column + 1));
        }
        movies.add(rowData);
    }
    public void readDBFile() {
        /*
         * Number of elements to be added until the table gets an update
         * notification
         */
        final int tableUpdateEveryNth = 80000;
        /*
         * The current number of elements added
         */
        int count = 0;
        for (int i = 0; i <= 80000; i++) {
            final Movie m = new Movie();
            m.setTitleGerman("Film " + i);
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    addMovie(m);
                }
            });
            count++;
            if (count % tableUpdateEveryNth == 0) {
                try {
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            fireTableRowsInserted(movies.size() - tableUpdateEveryNth, movies.size() - 1);
                        }
                    });
                } catch (final InterruptedException ex) {
                    ex.printStackTrace();
                } catch (final InvocationTargetException ex) {
                    ex.printStackTrace();
                }
            }
        }
        /*
         * There may still be some elements that have not yet been painted.
         * (This might be the case if count % tableUpdateEveryNth does not add
         * up)
         */
        int calcNotPainted = 0;
        if (count % tableUpdateEveryNth > 0) {
            calcNotPainted = (count / tableUpdateEveryNth > 0 ? count
                    % tableUpdateEveryNth : count);
        }
        final int toPaint = calcNotPainted;
        if (toPaint > 0) {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {

                    @Override
                    public void run() {
                        fireTableRowsInserted(movies.size() - toPaint, movies.size() - 1);
                    }
                });
            } catch (final InterruptedException e) {
                e.printStackTrace();
            } catch (final InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}
class Movie implements Comparable {
    private String titleGerman = "";
    private int rating = 0;
    void setTitleGerman(final String string) {
        titleGerman = string;
    }
    void setRating(final int i) {
        rating = i;
    }
    @Override
    public String toString() {
        return "Movie:" + titleGerman + ", rating:" + rating;
    }
    public int compareTo(final Object o) {
        Movie other = (Movie) o;
        return this.toString().compareTo(other.toString());
    }
}
 
Zuletzt bearbeitet:

hdi

Top Contributor
Hey, danke für's KSKB.

Die Abfrage auf "count == 1 ||" ist suspekt und muss entfernt werden. Eine einzeilige Tabelle kann eh nicht früher am Bildschirm erscheinen als eine leere.
Bei dir trifft das zu, bei mir nicht! Also... das klingt jetzt etwas unglaubwürdig ;) Und so ist es auch nicht. Was ich meine: Ich hab ne Verzögerung zwischen JFrame Anzeige und Painten der Tabelle, wenn ich dass add() nicht per invokeAndWait sondern mit invokeLater mache. In letzterem Fall ist mein JFrame ne halbe Sekunde nur halb aufgebaut (Rahmen ist da, aber man kann durchsehen). Das ist auch erst bei großen Werten bemerkbar, zB wenn updateTableEveryNth = 20000 oder so ist.
Aber bei den gleichen Werten in deiner Demo ist deine Tabelle trotzdem sofort da...

Ich vermute aber es liegt daran dass meine Tabelle nicht so "einfach" ist wie deine. Sprich ich hab mehr Spalten,und eigene Cell und HeaderRenderer. zB braucht deine Demo ~650ms um 5000 Elemente zu lesen, mein Code braucht ~5 Sekunden, obwohl unsere readDbFile()-Methoden zu 100% identisch sind. Aber wenn ich zB getColumnCount() von 6 auf 1 setze, sind es bei mir auch nur noch 1,5 Sekunden. Ich vermute mal wenn ich jetzt noch die eigenen Renderer löschen würde, und nur Table-Defaults verwende,komme ich auch auf die 650ms.

Und genau wegen diesen etwas komplexeren Paintings in meinem Table, so glaub ich, hab ich auch diese Verzögerung beim Start, wenn ich nicht sofort ein invokeAndWait mache.
...ich hab das Gefühl dass Schleifen vom Scheduler hoch priorisiert sind, und er darin irgendwie nicht zum EDT switcht, wenn er keine Zeit dafür hat.
edit: Oder sagen wir so: Nicht-EDT Threads sind höher priorisiert im allgemeinen.

Wenn du die invokeAndWait's zu invokeLater's machst in deinem Code,und dann die Anzahl der Elemente mal auf zB 50.000 setzt, wirst du merken dass bei einigen Starts die Tabelle komplett leer bleibt bis die Schleife komplett durch ist. Und dann kommen alle. Sprich die 2500 repaint-Anfragen, die in dieser Zeit per invokeLater auf den EDT gesetzt wurden, wurden während der Schleife nicht bearbeitet. Und zwar nicht mal der allererste davon!

..und das ist der Grund warum ich eben invokeAndWait mache. Nur so paintet sich das, ansonsten hat der EDT scheinbar keine Chance wenn ich in ner Schleife stecke. (Es sei denn ich bau ein sleep ein, aber invokeAndWait ist ja - jetzt aus dem Standpunkt eines Schedulers bzw. Resourcenverbrauch von Threads - eig. das selbe wie invokeLater und sleep)

PS: Deine Demo ist mal mit ner Exception abgestürtzt. Komischerweise hieß die Exception nur "Exception in Thread 4", und danach kam irgendwas mit Exception in EventQueue. Aber keine Zeilen- oder Methodenangabe.
Das war als ich die Demo mit 500.000 Elementen gestartet hab, ich glaub danach hab ich nochmal load geklickt.

Also summed up: Meine komplette Tabelle (also ich meine nicht Rows, sondern die Table bzw sogar das ScrollPane!) wird erst gezeichnet sobald das erste repaint Event mit invokeAndWait auf den EDT gelegt wird. Jegliche invokeAndLater's werden nicht bearbeitet, solange bis die Schleife durch ist, auch wenn das 10 Sekunden dauert! Dann hab ich 10 Sekunden ein halbes JFrame ohne Tabelle, dafür aber mit einem hübschen Ausschnitt aus Eclipse ;).
 
Zuletzt bearbeitet:

André Uhres

Top Contributor
  • Du musst die Gui auf dem EDT starten, dann ist sie sofort da (ohne spezielle Abfrage).
  • Ich bin nicht gegen invokeAnWait, aber einmal alle 20 Zeilen reicht allemal.
  • Wenn meine Demo einmal abgestürzt sein sollte, dann vermutlich wegen Platzmangel im Hauptspeicher.
 

hdi

Top Contributor
Ich starte die GUI auf dem EDT. Das Frame ist auch sofort da, aber nicht alle Inhalte.

Meine komplette Tabelle (also ich meine nicht Rows, sondern die Table bzw sogar das ScrollPane!) wird erst gezeichnet sobald das erste repaint Event mit invokeAndWait auf den EDT gelegt wird. Jegliche invokeAndLater's werden nicht bearbeitet, solange bis die Schleife durch ist, auch wenn das 10 Sekunden dauert!
Ich hab grad festgestellt das stimmt doch nicht völlig. Wenn ich updateTableEveryNth zB auf 60.000 setze, dann werden die ersten Daten natürlich erst recht spät angezeigt. Aber der leere Table wird ca 0.5 Sek. vorher schon gepaintet. ABER: Trotzdem hab ich auch gute 0.5 Sek. ein JFrame, dass keinen Table enthält sondern eine Durchsicht auf den Desktop oder was auch immer dahinterliegt.

Wenn ich das add per invokeAndWait mach ist die volle GUI samt (leerem) Table immer SOFORT da. Obwohl da gar kein fire() gemacht wird, aber der Thread wartet bis alle Events auf dem EDT abgearbeitet sind, bevor er mit der Schleife anfängt. Und dann ist das Frame voll gemalt. Bei invokeLater fängt er mit der Schleife an bevor alle "Default"-Events des EDT vom JFrame abgearbeitet sind, und deshalb laggt das Painten, weil er es erst Stück für Stück macht, und nicht genug Zeit hat weil meine Schleife dazwischenfunkt.

Das ist bei deiner Demo genauso. Es wird halt erst bemerkbar wenn der EDT beim "Startup" genug Tasks in der Queue hat. In deinem Fall, wo die GUI nur aus einem kleinen Table besteht, muss man halt die Werte stark ändern, um den Unterschied zu sehen. Meine GUI ist komplexer und deshalb merkt man es bei mir krasser.

Aber überzeuge dich selbst: Stelle deine Schleife auf 250.000 Elemente, mach tableUpdateEveryNth = 200.000, und dann starte. Du wirst (evlt nach einigen Starts, das ist nicht immer gleich schnell) erkennen, dass man kurz nur den Rahmen des Frames sieht, bevor die Table erscheint. Je höher du die Werte setzt, sprich je weiter du das tableUpdate (= das erste invokeAndWait) hinauszögerst, desto offensichtlicher ist das.

Wenn du nun das adden per invokeAndWait machst, wartet er bis der EDT quasi komplett abgearbeitet wird, und fängt dann mit dem Befüllen an. Das führt dazu dass das Frame samt Inhalt SOFORT aufgebaut ist beim Start.

Wie gesagt, hau noch n paar Buttons in deine GUI oder Spalten in die Tabelle, je mehr er beim setVisible(true) painten muss, desto länger hängt das Frame in diesem halbfertigen Zustand, bis die CPU voll und ganz dem EDT gewidmet werden kann. (Was der Fall ist wenn die Schleife fertig ist oder der Thread auf Grund eines invokeAndWait pausiert)
 
Zuletzt bearbeitet:

André Uhres

Top Contributor
Aber überzeuge dich selbst: Stelle deine Schleife auf 250.000 Elemente, mach tableUpdateEveryNth = 200.000, und dann starte. Du wirst (evlt nach einigen Starts, das ist nicht immer gleich schnell) erkennen, dass man kurz nur den Rahmen des Frames sieht, bevor die Table erscheint. Je höher du die Werte setzt, sprich je weiter du das tableUpdate (= das erste invokeAndWait) hinauszögerst, desto offensichtlicher ist das.
Bei mir ist die GUI immer sofort da. Ich habe jetzt mein Beispiel oben editiert und so abgeändert: 12 Spalten, Schleife auf 80.000 Elemente, tableUpdateEveryNth = 80.000. Dann sehe ich sofort die leere Tabelle und nach 10 Sekunden füllt sie sich.
 

hdi

Top Contributor
Hm.. also bei mir ist es so, dass eben jedes 3. mal oder so man noch erkennen kann wie das Frame nicht komplett gefüllt ist.
Als Beweis ein Screenshot, du siehst es dauert zumindest so lange dass ich einen Screenshot davon machen kann. Natürlich hab ich den quasi gleichzeitig mit dem programmstart gemacht, und ich hab erst beim 2.Versuch ne GUI bekommen, wo der Table noch fehlt.

..aber man sieht das halt auch, für eben ca. 0.5 Sek. Vllt verschätze ich mich auch und es sind nur 0.2 Sek oder so, aber mir persönlich fällts auf:



...sei mir nicht böse, aber evtl fällt dir das nicht so sehr auf wie mir aus 2 Gründen
1) Vllt ist die Reaktionszeit deiner Augen nicht so gut wie meine (es liegen immerhin ca 30 Jahre zwischen uns würde ich sagen)
2) und das ist vllt wahrscheinlicher: Du achtest nich so drauf. Mir ist es mal aufgefallen und jetzt achte ich da drauf, und es stört mich.

...ich geb dir Recht, dass wohl kein User denken würde "boah was war denn das für eine hässlich halb-aufgebaute GUI hier grad für zwei zehntel Sekunden"... Aber mich stört das, und wenn ich die GUI dazu bringen wirklichsofort da zu sein, warum sollte ich es nicht tun.

Aber hey, ich will dir kein Unrecht tun. Wenn du echt felsenfest davon überzeugt bist dass deine GUI immer sofort da ist.. dann verstehe ich aber nicht warum. Denn der Code ist ja bei uns der gleiche.
 

Wildcard

Top Contributor
...sei mir nicht böse, aber evtl fällt dir das nicht so sehr auf wie mir aus 2 Gründen
1) Vllt ist die Reaktionszeit deiner Augen nicht so gut wie meine (es liegen immerhin ca 30 Jahre zwischen uns würde ich sagen)
Du meinst zwischen dir und James Gosling (54), oder auf was beziehst du dich?
 

hdi

Top Contributor
Ne zwischen mir und André. Oder ist das Foto nicht er? Also der Mann auf dem Foto sieht schon etwas älter aus als ich :) Und wenn es James Gosling ist (werd ich gleich mal googlen, noch nie gehört), dann hab ich sogar ganz gut geschätzt, denn ich werd demnächst 24 ;)
 

hdi

Top Contributor
Oh Gott, Erfinder von Java :lol: Noch nie gehört oder gesehen :oops:

Naja dann schließen wir das mit den schlechteren Augen mal aus. Außer André ist tortzdem 54 ;) Aber ich halte das eh nicht für wahrscheinlich. Ich hab mich da reingesteigert, mir ist das mal aufgefallen und es stört mich seit dem..
Vergleicht mal das Open-Verhalten von zB dem Windows Explorer mit einem JFrame, dass ein paar Komponenten hat. Der Explorer ist schneller. Und mit dem invokeAndWait konnte ich jetzt mein Frame dazu bringen, auch sofort voll da zu sein...
 

javimka

Top Contributor
Ich habe das KSKB auch mal getestet und folgendes herausgefunden.
Auf Windows 7 mit dem Aero Theme erscheint das Frame sofort und ist nicht leer zu sehen. Aus deinem Screenshot habe ich aber entnommen, dass du Basic Theme verwendest und mal umgeschaltet. Und siehe da, ganz ganz kurz, aber wenn mans weiss doch für einen Augenblick sichtbar, war das Frame leer :)
==> Windows ist schuld ?!?
 

hdi

Top Contributor
==> Windows ist schuld ?!?

Tatsache, kurz auf Aero geswitcht und schon lief's perfekt. Allerdings kann man nicht sagen dass Windows "Schuld" ist. Das Ding ist dass Aero Fenster einblendet, sowohl vom Alpha auch als von der Größe. Sprich bis es dann groß und sichtbar genug ist wurde der Inhalt geladen.
Bei Basic ist es sofort da und man sieht noch das leere Frame.

Also im Endeffekt ist es schon die Schuld vom EDT, der ist einfach zu langsam, bzw. bekommt vom Scheduler nicht genug Rechenzeit. Wenn man seinen Thread nicht zwingt zu warten, kann's halt n bisschen dauern.

So naja.. kA also ich lasse es jetzt bei invokeAndWait, weil wie gesagt damit ist das Fenster sofort da, d.h. auch bei Basic theme sieht man es gleich vollständig.

Weitere Diskussion können wir ja führen, aber für mich ist das jetzt erstmal zufriedenstellend gelöst. Also nochmal danke an alle!
 
Zuletzt bearbeitet:

André Uhres

Top Contributor
Der Vollständigkeit halber hier noch eine Version mit SwingWorker (die kommt also ganz ohne Runnables und invokeLater/invokeAndWait aus, ausser in der main-Metode):
Java:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class MovieTest extends JFrame {
    private final JToolBar tools;
    private final JButton btLoad;
    private final MovieModel model;
    private final JTable table;
    private final JSlider slSpeed;
    private final JLabel lbSpeed;
    private final JProgressBar progress;
    private int speed = 1;
    private final int MOVIES = 1000;
    public MovieTest() {
        super("MovieTest2");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(700, 300);
        setLocationRelativeTo(null);
        tools = new JToolBar("Tools");
        btLoad = new JButton("Load");
        model = new MovieModel();
        table = new JTable(model);
        lbSpeed = new JLabel("Speed: ");
        tools.setLayout(new FlowLayout(FlowLayout.LEFT));
        slSpeed = new JSlider(0, 10, speed);
        progress = new JProgressBar(0, MOVIES);
        table.setAutoCreateRowSorter(true);
        table.getColumn("Movie_1").setPreferredWidth(200);
        table.getRowSorter().toggleSortOrder(0);
        slSpeed.setPaintLabels(true);
        slSpeed.setPaintTicks(true);
        slSpeed.setPaintTrack(true);
        slSpeed.setMajorTickSpacing(1);
        tools.add(progress);
        tools.add(btLoad);
        tools.add(lbSpeed);
        tools.add(slSpeed);
        getContentPane().add(tools, BorderLayout.PAGE_START);
        getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
        btLoad.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent evt) {
                load();
            }
        });
        slSpeed.addChangeListener(new ChangeListener() {
            public void stateChanged(final ChangeEvent e) {
                speed = slSpeed.getValue();
            }
        });
        load();
    }
    private void load(){
        model.clear();
        new SwingWorker<MovieModel, Movie>(){
            @Override
            protected MovieModel doInBackground() throws Exception {
                btLoad.setEnabled(false);
                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                readDBFile();
                btLoad.setEnabled(true);
                setCursor(Cursor.getDefaultCursor());
                return model;
            }
            private void readDBFile() {
                for (int i = 0; i <= MOVIES; i++) {
                    final Movie m = new Movie();
                    m.setTitleGerman("Film " + i);
                    publish(m);
                    try {
                        Thread.sleep(speed);
                    } catch (InterruptedException ex) {
                    }
                }
            }
            @Override
            protected void process(final List<Movie> chunks) {
                int from = model.getRowCount();
                int to = from;
                for (Movie movie : chunks) {
                    to = model.getRowCount();
                    model.addMovie(movie);
                    progress.setValue(model.getRowCount());
                }
                model.fireTableRowsInserted(from, to);
            }
        }.execute();
    }
    public static void main(final String[] args) {
        Runnable gui = new Runnable() {
            public void run() {
                new MovieTest().setVisible(true);
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}
class MovieModel extends AbstractTableModel {
    protected List<List> movies;
    public MovieModel() {
        movies = new ArrayList<List>();
    }
    public int getRowCount() {
        return movies.size();
    }
    public int getColumnCount() {
        return 6;
    }
    @Override
    public String getColumnName(final int column) {
        return "Movie_" + (column + 1);
    }
    @Override
    public Class<?> getColumnClass(final int columnIndex) {
        return Movie.class;
    }
    public Object getValueAt(final int rowIndex, final int columnIndex) {
        return movies.get(rowIndex).get(columnIndex);
    }
    public void addMovie(final Movie m) {
        List rowData = new ArrayList();
        rowData.add(m);
        for (int column = 0; column < getColumnCount(); column++) {
            rowData.add("Test_" + getRowCount() +"_"+ (column + 1));
        }
        movies.add(rowData);
    }
    public void clear() {
        movies.clear();
        fireTableDataChanged();
    }
}
class Movie implements Comparable {
    private String titleGerman = "";
    private int rating = 0;
    void setTitleGerman(final String string) {
        titleGerman = string;
    }
    void setRating(final int i) {
        rating = i;
    }
    @Override
    public String toString() {
        return "Movie:" + titleGerman + ", rating:" + rating;
    }
    public int compareTo(final Object o) {
        Movie other = (Movie) o;
        return this.toString().compareTo(other.toString());
    }
}
 
Zuletzt bearbeitet:

Nader

Mitglied
Der Vollständigkeit halber hier noch eine Version mit SwingWorker (die kommt also ganz ohne Runnables und invokeLater/invokeAndWait aus..

Danke, ein sehr schönes Beispiel! :) Ich glaube auch, dass man generell versuchen sollte mit SwingWorker zu arbeiten. Damit geht es meistens alles gut, aber mit invokeLater/invokeAndWait muss man oft Glück haben bis etwas richtig funktioniert.
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
T ProgressBox - Update AWT, Swing, JavaFX & SWT 9
Ernesto95 JavaFX JavaFX GUI mit sehr vielen Update requests AWT, Swing, JavaFX & SWT 4
G update, paintComponent AWT, Swing, JavaFX & SWT 1
T GUI Update /EDT AWT, Swing, JavaFX & SWT 7
izoards JavaFX Concurrency Update UI AWT, Swing, JavaFX & SWT 35
ms_cikar Update swingUtilities Repaint in der Schleife AWT, Swing, JavaFX & SWT 3
J TableView Update/Refresh CPU AWT, Swing, JavaFX & SWT 2
A Verständnisfragen im Umgang mit update() und JFrames AWT, Swing, JavaFX & SWT 5
ralfb1105 Swing JComboBox update der Daten AWT, Swing, JavaFX & SWT 8
blazingblade JavaFX Tableview Clock Column update AWT, Swing, JavaFX & SWT 5
R Update eines Labels bei Methodenaufruf einer anderen Klasse AWT, Swing, JavaFX & SWT 9
B Swing Update Swing Komponente bevor Methode startet. AWT, Swing, JavaFX & SWT 4
M JavaFX ComboBox: Update zur Laufzeit AWT, Swing, JavaFX & SWT 16
W Swing ProgressBar update AWT, Swing, JavaFX & SWT 4
B IconImage update im Tabbedpane AWT, Swing, JavaFX & SWT 3
M Update überschreiben klappt nicht AWT, Swing, JavaFX & SWT 4
Q JList Update Problem AWT, Swing, JavaFX & SWT 1
N Observer: update ruft nicht repaint auf AWT, Swing, JavaFX & SWT 0
M "Update" der JTable funktioniert nicht AWT, Swing, JavaFX & SWT 2
S Swing Update eine JTabelle nach einer Drag&Drop Operation AWT, Swing, JavaFX & SWT 0
C Swing Update von swing-TableModels per Thread. Eins geht, das andere nicht, warum? AWT, Swing, JavaFX & SWT 12
V Swing Update Textarea AWT, Swing, JavaFX & SWT 2
T Event Handling JFreeChart Update AWT, Swing, JavaFX & SWT 2
Farbtopf Live update JFreeChart AWT, Swing, JavaFX & SWT 3
F Swing GUI-Thread für automatisches Update nutzen AWT, Swing, JavaFX & SWT 10
El_Lobo Swing bei Update von Graphik Koordinatensystem nicht jedesmal neu zeichnen AWT, Swing, JavaFX & SWT 2
M Update JPanel AWT, Swing, JavaFX & SWT 12
N update model nach dem filtern AWT, Swing, JavaFX & SWT 2
E Umgang mit der Update Methode AWT, Swing, JavaFX & SWT 38
E Swing Update JTable AWT, Swing, JavaFX & SWT 6
L Update JTree Verzeichnisse AWT, Swing, JavaFX & SWT 9
G Swing Update-Funktion für Swing-Anwendung AWT, Swing, JavaFX & SWT 5
E Swing JTextField Listener nach Update?! AWT, Swing, JavaFX & SWT 2
D Swing JTable Problem bei automatischem update von Zellen AWT, Swing, JavaFX & SWT 3
P 2D-Grafik PaintComponent() übernimmt keine Werte aus update() AWT, Swing, JavaFX & SWT 8
D Swing update eines Labels nicht sichtbar AWT, Swing, JavaFX & SWT 9
N Tablle nach SQL-Update neu Laden AWT, Swing, JavaFX & SWT 4
M SWT grabExcessHorizontalSpace update ? refresh ? AWT, Swing, JavaFX & SWT 6
P Observer und GUI Update AWT, Swing, JavaFX & SWT 2
w0ddes Swing Update: Laufendes GUI updaten AWT, Swing, JavaFX & SWT 8
D JTable während edit kein update machen lassen AWT, Swing, JavaFX & SWT 2
M Swing Kein update bei simulierten HTML-Link AWT, Swing, JavaFX & SWT 4
C SWT Shell update probleme - Mausbewegung nötig AWT, Swing, JavaFX & SWT 2
hdi Swing Gui Update Problem (EDT) AWT, Swing, JavaFX & SWT 6
C JList update über tabbedPane? AWT, Swing, JavaFX & SWT 18
M Update einer JTEextArea AWT, Swing, JavaFX & SWT 2
H JTable mySQL Update AWT, Swing, JavaFX & SWT 8
S Update des fensters beim Ersetzen von JPanels AWT, Swing, JavaFX & SWT 9
G Features nach Update löschen AWT, Swing, JavaFX & SWT 2
J Fragen zur Vererbung und Update AWT, Swing, JavaFX & SWT 12
B Update von JLabels AWT, Swing, JavaFX & SWT 2
C Habe Probleme beim Bild laden! *Update 30.11.2006* AWT, Swing, JavaFX & SWT 28
C JTreeTable update Problem AWT, Swing, JavaFX & SWT 4
S Probleme mit dem Update einer JList AWT, Swing, JavaFX & SWT 7
B View zeichnet Daten aus dem Model ohne Update AWT, Swing, JavaFX & SWT 4
A Update von Frameinhalt und Scrollbar AWT, Swing, JavaFX & SWT 11
F MVC: Update von View und Controller AWT, Swing, JavaFX & SWT 5
C JTable update: Selektion beibehalten AWT, Swing, JavaFX & SWT 12
P paintComponent /paint/ update/ offscreenImage / Graphics2D / AWT, Swing, JavaFX & SWT 4
S JMenuBar + update AWT, Swing, JavaFX & SWT 3
G JTree - ungültiger selection update AWT, Swing, JavaFX & SWT 2
G Problem mit JLabel Update AWT, Swing, JavaFX & SWT 3
C [JTable] Update der Datenbank AWT, Swing, JavaFX & SWT 6
N update()-Methode für Canvas AWT, Swing, JavaFX & SWT 9
A problem mit update nach event, JSplitpane spinnt AWT, Swing, JavaFX & SWT 2
S Update von Grafik auf JPanel AWT, Swing, JavaFX & SWT 2
M GUI Update während der Verarbeitung einer Methode AWT, Swing, JavaFX & SWT 3
M update JTable AWT, Swing, JavaFX & SWT 3
T Problem bei Update von JTables in JTabbedPane AWT, Swing, JavaFX & SWT 2
D Problem beim Update von unsichtbaren JComponents mit Timern AWT, Swing, JavaFX & SWT 5
D Swing: GUI-Update-Problem AWT, Swing, JavaFX & SWT 3
J Warum funktioniert das Update des UI nicht? AWT, Swing, JavaFX & SWT 8
I GUI-Update-Probleme AWT, Swing, JavaFX & SWT 2
G Update von JPanel nach Buttonbetätigung AWT, Swing, JavaFX & SWT 1
Psypsy Swing SwingWorker unterschied zwischen setProgress und process AWT, Swing, JavaFX & SWT 2
G Process in JTextArea ausgeben AWT, Swing, JavaFX & SWT 4
N Speed Click Robot? AWT, Swing, JavaFX & SWT 7

Ähnliche Java Themen

Neue Themen


Oben