Swing Anzeigeproblem mit TableModels abhängig von deren Inhalt

Collinwood

Mitglied
Hallo zusammen, hallo speziell rme (da dies das Folgethema zu dem erledigten "Thread-Thema" ist).

Es geht darum, dass ich während der Ausführung eines Threads 2 Tabellen in zwei verschiedenen Panels laufend aktualisieren lasse. Dabei läuft die eine Aktualisierung super, und die andere eher gar nicht. Da sich das im Programm über etliche Klassen zieht, kann ich leider mit keinem "zusammengefassten" Code dienen. Was ich ja schon festgestellt habe, ist, dass beide Tabellen wie gewünscht aktualisiert werden, wenn ich dem "kränkelnden" TableModel beim Instanziieren eine andere Liste mitgebe.

Um das einigermaßen für die Öffentlichkeit abzubilden, werde ich jetzt 2 runnable jars kreieren, einmal "kaputt aber korrekt" und einmal "funktionierend aber gefaked". Zusätzlich poste ich den Code für die Instantiierung der TableModels und die beiden Listenklassen um die es geht. Falls mehr Information gebraucht wird, bitte einfach sagen. Irgendwie findet sich schon ein einigermaßen brauchbarer Approach an die Sache.... hoffe ich. :toll:

Kurzanleitung, was im Tool zu tun ist:

- "Auswahl generieren" drücken
- Slider auf 100 stellen
- Bei Rückschrittlänge eine 0 eingeben
- "Angleichung durchführen" drücken
- Beobachten und vergleichen.

Konstruktor der Klasse Launcher (welche Main() enthält), die Zuweisung für this.mcModel bezieht sich auf das Modell der Tabelle des mittleren Panels. Im weiteren Verlauf wird der Tabelle natürlich das Modell zugewiesen und dem Panel die Tabelle, ich denke an diesen weiteren Zuweisungen wird nichts streitbares dran sein...
Java:
public Launcher() {

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.updater = new UpdaterThread();
		this.mixer = new Mixer(this.updater);
		this.mixer.setSourceCardList(new SourceCardList());
		this.mixer.setMixedCardList(new MixedCardList());
		this.mixer.setProposalCardList(new ProposalCardList());
		this.csvm = new CSVManagement();
		this.csvm.read();
		this.csvm.putDataToSourceTable(this.mixer.getSourceCardList());
		this.mcModel = new CardModel(this.mixer.getMixedCardList());
		//this.mcModel = new CardModel(this.mixer.getProposalCardList());
		this.scModel = new CardModel(this.mixer.getSourceCardList());
		this.pModel = new ProposalModel(this.mixer.getProposalCardList());
		
		initComponents();
		this.mixer.setProgressBar(this.progressBar1);
		this.mixer.setSlider(this.slider1);
		this.mixer.setpModel(this.pModel);
		this.mixer.setTable3(this.table3);
		//this.mixer.run();
		//this.updater.run();
		
	}

Das eigene Tabellenmodell "CardModel" sieht so aus:
Java:
package middleware;
import java.util.List;
import javax.swing.table.AbstractTableModel;

import data.Card;


/**
 * 
 * @author sash
 */
public class CardModel extends AbstractTableModel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final String[] HEADER = { "Name", "Ausgewählt", }; //$NON-NLS-1$ //$NON-NLS-2$
	private List<Card> data;

	public CardModel(List<Card> data) {
		this.data = data;
	}

	public boolean isCellEditable(int row, int column) {
		return column != 4;
	}

	public int getRowCount() {
		return this.data.size();
	}

	public int getColumnCount() {
		return HEADER.length;
	}

	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		Card card = this.data.get(rowIndex);
		switch (columnIndex) {
		case 0:
			card.setName(aValue.toString());
			//$FALL-THROUGH$
		case 1:
			card.setPicked(Boolean.parseBoolean(aValue.toString()));
			//$FALL-THROUGH$
		default:
			//
		}
		this.data.set(rowIndex, card);
	}

	@SuppressWarnings("boxing")
	public Object getValueAt(int row, int column) {
		Card card = this.data.get(row);

		switch (column) {
		case 0:
			return card.getName();
		case 1:
			return card.isPicked();
		default:
			return ""; //$NON-NLS-1$
		}
	}

	@Override
	public String getColumnName(int column) {
		return HEADER[column];
	}

	// Hier kann man die Klasse für eine Spalte ändern.
	@Override
	public Class<?> getColumnClass(int column) {
		if (column == 1) {
			return Boolean.class;
		}
		return super.getColumnClass(column);
	}

}

Und hier hätten wir dann noch die beiden Listenklassen, wo meine eigene Vermutung mir sagt dass diese ja zum momentanen Zeitpunkt gar keinen signifikanten Unterschied aufweisen, aber das kann natürlich auch ein Irrtum sein, sonst würde ich die Frage nicht öffentlich stellen:

Java:
package data;

import java.util.ArrayList;

public class ProposalCardList extends ArrayList<Card>{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

}

Java:
package data;

import java.util.ArrayList;

public class MixedCardList extends ArrayList<Card>{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

}

Bitte nicht vergessen, beim Testen des Programms die csv-Datei im gleichen Verzeichnis zu parken.

Vielen Dank vorab!!!
 

Anhänge

  • paket20131216.zip
    331 KB · Aufrufe: 4
Zuletzt bearbeitet:

rme

Top Contributor
Hallo,

die JARs bringen leider nicht so viel - selbst, wenn man dort das Problem sieht, fällt dadurch ja nicht die Lösung vom Himmel :/ Ohne lauffähiges Minimalbeispiel in Code-Form können wir daher nur aus Erfahrung raten, was das Problem sein könnte. Paar Ideen:

* Wie informierst du die Table darüber, dass neue Daten da sind? Ich sehe kein fireTableDataChanged-Aufruf oder ähnliches.

* Achtest du darauf, das Model nur aus dem Swing-Thread heraus zu ändern? Ansonsten geht die GUI kaputt
 

Collinwood

Mitglied
Hi,

danke für die Antwort!

die fire-Methoden habe ich jetzt in den EDT-Thread gepackt. Die waren im "alten" Thema ganz am Anfang im Gespräch. Die werden natürlich weiterhin ausgeführt, ich dachte nur dass sie im Moment nicht von Belang sind.

Ja, ich arbeite jetzt aus der vorgeschriebenen Hilfsmethode im Swing-eigenen Thread, wie schon eingangs erwähnt. Allerdings ist das jetzt geschachtelt, Thread innerhalb des Threads und ich denke, das hat das Problem an sich nicht gelöst.

Ich werde probieren, den Code schrumpfen zu lassen und eine Art Testprogramm bereit zu stellen.
 

Collinwood

Mitglied
So,

ich hab codetechnisch leider keinen Ansatz gefunden, um das ganze irgendwie künstlich abzukürzen, aber was ich gemacht habe, ist dass ganze Projekt auf 16 Klassen zu verkleinern, davon sind 13 Klassen total klein und überschaubar, die größte ("Balancer") muss gar nicht angeschaut werden und dann bleiben noch 2, das sind "Launcher" und "Mixer". Die relevanten Codestellen dieser beiden, also einen Mini-Ablauf, poste ich hier gleich drunter. Des Weiteren hab ich alle Strings de-externalisiert und alle packages aufgelöst. Das hab ich gemacht, damit ich nur noch .java-Dateien im src-Package habe. Im Anhang hab ich also diese 16 Files als Rohdaten gepackt, damit man sie auch ganz sicher in einem neuen Projekt in das src-Folder entpacken kann. Was ich leider nicht verhindern kann ist die Benutzung zweier externer Libraries, deswegen habe ich die auch noch mit hochgeladen. Die müssten zur Benutzung halt einfach dem neuen Projekt hinzugefügt werden.

Die Benutzung des Programms habe ich ja weiter oben schon gepostet, deshalb dann jetzt zum Coding:

1. im Konstruktor von "Launcher" weise ich meinen Tabellenmodellen jeweils eine abzubildende Liste zu. 2 der Modelle sind vom Typ CardModel, 1 ist vom Typ ProposalModel:

Java:
this.mcModel = new CardModel(this.mixer.getMixedCardList());
//this.mcModel = new CardModel(this.mixer.getProposalCardList());
this.scModel = new CardModel(this.mixer.getSourceCardList());
this.pModel = new ProposalModel(this.mixer.getProposalCardList());

Das ganze wird dann den GUI-Elementen im Verlauf geaddet.

2. Auf drücken des "Vorschlag generieren" Buttons hin wird das hier in Gang gesetzt:

Java:
void button1ActionPerformed(@SuppressWarnings("unused") ActionEvent e) {
		
		this.generateProposal();
	}

3. Der Start des Mixers und Balancers passiert im Launcher so:

Java:
	void button3ActionPerformed(@SuppressWarnings("unused") ActionEvent e) {

		this.rueckschrittlaenge = parseWithDefault(
				Launcher.this.textField2.getText(), 1);
		this.mixer.setRueckschrittlaenge(this.rueckschrittlaenge);
		this.mixer.setTargetValue(Launcher.this.slider1.getValue());
		this.mixer.setButtonPressed(true);

	}

4. Im Mixer ist es dann die Run-Methode, da Mixer ein Thread ist. Dort "hört" der Thread quasi die eben auf true gesetzte boolsche Variable ab...
Java:
	public void run() {

		while (!Thread.interrupted()) {

			if (this.buttonPressed) {
... und alles weitere findet ab da genauso in der Run-Methode statt. Auch das hier:

Java:
try {
	Thread.sleep(25);

	SwingUtilities.invokeLater(new Runnable() {
		public void run() {
			if (Mixer.this.dtm != null) {
			  ((AbstractTableModel) Mixer.this.dtm)
			  .fireTableDataChanged();
			  ((AbstractTableModel) Mixer.this.dtm2)
			  .fireTableDataChanged();
		        }
	        }
	});
}

Ich hoffe, das kann man jetzt einigermaßen nachstellen/nachvollziehen?

Ich werde jetzt probieren, meiner eigenen Anleitung zu folgen, und editiere was dabei herausgekommen ist.

PS: Nochmal der Hinweis, dass die csv-Datei im Projektordner liegen muss. Habe ich gerade selbst falsch gemacht. Hat ansonsten geklappt.
 

Anhänge

  • forms-1.3.0.jar
    120,5 KB · Aufrufe: 4
  • opencsv-2.3.jar
    13,8 KB · Aufrufe: 5
  • src.zip
    17,6 KB · Aufrufe: 5
  • csvdatei.zip
    605 Bytes · Aufrufe: 3
Zuletzt bearbeitet:

rme

Top Contributor
So, kompilieren und starten konnte ich es schonmal. Da dein Encoding weiterhin falsch ist und du offenbar ein Null-Layout verwendest, sieht es bei mir allerdings seltsam aus (siehe Anhang). Ob da rechts noch was im Fenster fehlt, weiß ich nicht - ich bin gerade unterwegs und hab hier keinen größeren Monitor.

Wenn ich deine Anleitung ausführe und auf "Angleichen" klicke, werden im mittleren und rechten Bereich jeweils neue Dinge geschrieben, die zufällig wirken. Wie ist denn das gewünschte Verhalten?
 

Anhänge

  • Screen Shot 2013-12-19 at 19.32.17.jpg
    Screen Shot 2013-12-19 at 19.32.17.jpg
    66,7 KB · Aufrufe: 28

Collinwood

Mitglied
Hi und danke fürs testen!

Das mit dem Encoding solltest du mir nochmal näher bringen, wenn mir mit dem Hauptthema durch sind. Ich kann das begrifflich nicht einordnen deshalb weiß ich nicht was zu tun ist.

Ansonsten finde ich seltsam dass der Fehler wegen dem ich poste offenbar bei dir gar nicht auftritt. Zufällige daten mitte und rechts ist perfekt. Bei mir tut sich aber im mittleren Abschnitt während einer Angleichung gar nichts. Davor und danach sind alte/neue daten so wie gewünscht zu sehen. Reden wir da jetzt von einem anzeigefehler bedingt durchs Betriebssystem oder hat das was dem Encoding zu tun wovon du sprichst?
Fehler durch threading oder falsche GUI-Methoden-Verwendung kann man ja jetzt ausschließen, oder bin ich da auf dem Holzweg?

EDIT: das mit dem nulllayout verstehe ich nicht, in initComponents() weise ich der contentPane ein FormLayout zu...
 
Zuletzt bearbeitet:

rme

Top Contributor
Java arbeitet eigentlich unabhängig vom Betriebssystem überall gleich. Jetzt deutet alles darauf hin, dass es ein Swing-Thread-Problem ist. Ich schau mir mal näher an, wie du das getan hast.
 

rme

Top Contributor
Das mit dem NullLayout war nur geraten, sorry. Denn wenn ich die Fenstergröße ändere, passen sich die Komponenten nicht an, sondern ich sehe einfach weniger. Dadurch ist es bei mir unmöglich, den ganzen Inhalt zu sehen, weil meine Auflösung dafür an diesem System zu gering ist. Sowas kenne ich eigentlich nur vom Null-Layout.

Das eigentliche Problem ist aber nun klar: Mixer ist ein eigener Thread und da passieren sehr, sehr viele Sachen, die allesamt illegal sind:

* progressBar.setValue -> verboten, da nicht im Swing-Thread

* slider.setValue -> ebenfalls

* insertShuffledCollection benutzt setValueAt auf eine JTable -> ebenfalls verboten

Merke: Fast alle Klassen, die mit J anfangen, dürfen nicht aus fremden Threads heraus geschrieben werden, da muss man immer invokeLater benutzen. Eigentlich programmiert man in Swing so, dass ein Klick auf einen Button oder ähnliches einem Thread signalisiert, dass er arbeiten soll - dieser arbeitet dann und signalisiert der GUI, dass er fertig ist (via invokeLater) und von dort holt die GUI sich dann selbständig alle Daten ab.
 

Collinwood

Mitglied
Hi und vielen Dank für deine Ausführungen und die damit verbundene Mühe!

Eigentlich programmiert man in Swing so, dass ein Klick auf einen Button oder ähnliches einem Thread signalisiert, dass er arbeiten soll - dieser arbeitet dann und signalisiert der GUI, dass er fertig ist (via invokeLater) und von dort holt die GUI sich dann selbständig alle Daten ab.

Also, die Übereinstimmung ist ja bei meinem Ablauf, dass ein Klick auf den Button dem Thread signalisiert, dass dieser arbeiten soll. Unterschiedlich ist, dass du von einem Signal an die GUI redest, wenn dieser "fertig" ist.

Bedeutet das im Umkehrschluss, dass das, was ich möchte, nämlich eine Aktualisierung während des laufenden Threads, schlicht und einfach nicht möglich ist? Was wäre die Alternative? Auf SWT/AWT umsteigen?
 
Zuletzt bearbeitet:

rme

Top Contributor
Doch, das ist natürlich möglich. Lass die GUI ein Listener oder Observer deines sich im Thread ändernden Objekts sein, sodass sie erfährt, wenn sich etwas geändert hat - dann benutzt du an der Stelle invokeLater, damit alles sich ändert. Bei JTables geht das einigermaßen automatisch, wenn du ein TableModel verwendest, also zum Beispiel von AbstractTableModel ableitest, und dann alle Änderungen via invokeLater oder invokeAndWait synchronisierst.

Die Synchronisierung in deinem Thread über eine Boolean-Variable ist übrigens keine schöne - denn der Thread läuft nun die ganze Zeit, obwohl er eigentlich nix zu tun hat. Besser wäre es, den Thread überhaupt erst zu starten, wenn er benötigt wird.

Schau dir mal dieses Tutorial an: SwingWorker - Eine Einführung - Java-Forum.org
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
O Swing JFrame Anzeigeproblem AWT, Swing, JavaFX & SWT 2
P JTable Anzeigeproblem AWT, Swing, JavaFX & SWT 3
Mr. Pink AWT Anzeigeproblem JPanel/Hintergrundbild AWT, Swing, JavaFX & SWT 1
B JMenuItem Anzeigeproblem AWT, Swing, JavaFX & SWT 6
P CardLayout - Anzeigeproblem AWT, Swing, JavaFX & SWT 2
F PDF-Anzeigeproblem Tool Multivalent AWT, Swing, JavaFX & SWT 2
C Swing Update von swing-TableModels per Thread. Eins geht, das andere nicht, warum? AWT, Swing, JavaFX & SWT 12
Eldorado Swing Optimierung eines TableModels AWT, Swing, JavaFX & SWT 2
G mehrere TableModels AWT, Swing, JavaFX & SWT 2
A checkbox in einer TableView abhängig von einem anderen Celleninhalt disablen AWT, Swing, JavaFX & SWT 1
H preferred size abhängig von Fenstergröße berechnen AWT, Swing, JavaFX & SWT 11
A JProgressBar updaten abhängig vom Output eines externen Skripts AWT, Swing, JavaFX & SWT 2
U JTable: Zeilen abhängig vom Datensatz einfärben AWT, Swing, JavaFX & SWT 4
R JTable Hintergrund der Zeile abhängig von Inhalt einer Zelle verändern AWT, Swing, JavaFX & SWT 3
A Flexible JTable mit editierbaren Zellen,abhängig von Data AWT, Swing, JavaFX & SWT 2
P seltsame Performance Probleme bei 2 Guis abhängig vom Aufruf AWT, Swing, JavaFX & SWT 8
P Zeichen abhängig davon, ob sich eine Variable geändert hat AWT, Swing, JavaFX & SWT 2
H Screenshot abhängig von Grafikkarte? AWT, Swing, JavaFX & SWT 15
G Button abhängig von aktiviertem tab anzeigen ? AWT, Swing, JavaFX & SWT 8
T Pixel OS abhängig AWT, Swing, JavaFX & SWT 3
R "\t" LnF abhängig !? - Ändern? AWT, Swing, JavaFX & SWT 7

Ähnliche Java Themen

Neue Themen


Oben