Swing Genaue Funktionsweise von modalem JDialog?

hdi

Top Contributor
Hey,

Untiges KSKB zeigt ein Frame, in dem ein modaler JDialog über einen Button angezeigt werden kann. Irgendwie ist überall nur die Rede davon, dass modale JDialogs User-Eingaben in anderen Fenster unterbinden. Ja ok, passt. Aber fast nirgendwo wird erwähnt, dass das Anzeigen eines solchen Dialogs dazu führt, dass es in der Ausführung im Code nicht mehr weitergeht, bis er wieder geschlossen ist.

JEtzt könnte man meinen, "nicht mehr weitergehen" heißt, dass der aufrufende Thread blockiert wird. Aber offensichtlich ist das nicht der FAll - denn in dieser Demo ist der aufrufende Thread der EDT. Und dass genau dieser Thread (siehe sysouts!) während der Anzeige des Dialogs noch andere Dinge tun kann zeigt, dass er nicht wirklich in der actionPerformed hängt.

Was ich mich jetzt Frage: Wie kann man sich die Implementierung davon eig. vorstellen? Ist das quasi so ein temporäres return-Statement nach dem showMessageDialog(), zu dem dann per
Code:
goto
(ist ja eig gar nicht implementiert?) beim Schließen des Dialogs zurückgesprungen wird?

Ich hoffe ihr versteht was ich meine. Ich kann mir halt grad nicht vorstellen wie ein und der selbe Thread in einer Methode A pausieren kann, während der gleichzeitig eine Methode B ausführt. Ein einziger Thread kann doch immer nur streng sequentiell arbeiten und nicht gleichzeitig an verschiedenen Stellen sein??? Nach meinem Verständnis müsste das Repainten der PRogressBar hängen bleiben, solange bis der Dialog geschlossen wurde.

So ist es ja bsp der Fall wenn man jetzt in der actionPerfmored statt dem Anzeigen des Dialogs einfach ein sleep() einbaut. Solange der EDT nicht aus der Methode retruned, passiert logischerweise inder GUI nix mehr. Aber hier ist das was anderes... ???:L

Bin dankbar für Aufklärung!
lg

Java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

public class KSKB extends JFrame {

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new KSKB().setVisible(true);
			}
		});
	}

	KSKB() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JButton btnShowDialog = new JButton("Show MODAL dialog");
		btnShowDialog.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println(threadInfo() + "betritt actionPerformed...");
				JOptionPane
						.showMessageDialog(
								null,
								"Der Thread, der die Anzeige dieses Dialogs angestossen hat,\nhängt jetzt in seiner Methode, bis dieser Dialog geschlossen wurde...");
				System.out
						.println(threadInfo() + "verlässt actionPerformed...");
			}
		});

		final JProgressBar bar = new JProgressBar(0, 10);
		bar.setPreferredSize(new Dimension(200, 50));
		new Thread() {
			public void run() {
				while (true) {

					SwingUtilities.invokeLater(new Runnable() {
						@Override
						public void run() {
							System.out.println(threadInfo()
									+ "verarbeitet ProgressBar Event...");
							int v = bar.getValue();
							if (v < 10) {
								v++;
							} else {
								v = 0;
							}
							bar.setValue(v);
						}
					});
					try {
						Thread.sleep(1500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

				}
			};
		}.start();

		add(btnShowDialog, BorderLayout.SOUTH);
		add(bar, BorderLayout.CENTER);

		pack();
		setLocation(100, 100);
	}

	static String threadInfo() {
		return "[Thread " + Thread.currentThread().getName() + " ("
				+ Thread.currentThread().getId() + ")] ";
	}
}
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Das wurde schonmal in anderen Threads angedeutet (ich ich hatte irgendwie das Gefühl, du hättest das schonmal gefragt, aber es war wohl nur was ähnliches).

Eigentlich wollte ich jetzt das gleiche schreiben, wie schonmal: "Ein modaler Dialog ist sehr kompliziert, da wird ein neuer AppContext und ein neuer(!) EDT erstellt, der sich um den Dialog kümmert" usw. Aber jetzt hab' ich nochmal geschaut: Es ist ja tatsächlich so, dass derSELBE Thread die beiden Aufgaben erledigt.... Und bei einem erneuten Blick auf den source code: Das ist noch deutlich komplizierter, als ich es in Erinnerung hatte (wobei das auch mit der "new modality API" zusammenhängen könnte). Im Endeffekt könnte es noch sooo viel mehr sein: Wenn man mal ganz frech sowas wie
Code:
System.out.println(threadInfo()+ "verarbeitet ProgressBar Event...");
new Exception().printStackTrace(); //---> :D
einfügt, sieht man, dass die eigentliche Verarbeitung IMMER aus einem der Runnables heraus gemacht wird, die in der Dialog#show-Methode erstellt werden. Aber die genauen Mechanismen durchschaue ich im diese Uhrzeit auf die schnelle auch nicht mehr. Vielleicht morgen nochmal schauen.
 

hdi

Top Contributor
Das mit dem Stack Trace war ne sehr gute Idee! Der Hinweis auf die show-Methode reicht mir, du musst dich da nicht durchquälen wenn du dazu keine Lust hast, ich mach das schon selber. Irgendwann. Oder ich warte noch 2 Jahre bis ich es wieder vergessen hab und stell die Frage noch ein drittes mal :D

Dank dir!
 
B

bERt0r

Gast
Was soll denn daran verwunderlich sein? Du erstellst 3 Threads, der der den modalen Dialog aufruft blockiert und die andern laufen natürlich weiter. Der Dialog blockiert schliesslich erst, wenn auf den Button gedrückt wird, zu dem Zeitpunkt laufen die 2 Progressbar Threads bereits. Der aufrufende Thread ist der, der das new KSKB() macht und der blockiert.
Dass der Progressbar noch geupdatet wird ist auch klar, schliesslich ruft nicht der KSKB Frame die blockende Methode auf, sondern dein annonymer ActionListener. Wenn du erreichen willst, dass dein Frame blockiert wird, schreib ihm eine Funktion showMessage(), übergib die Frameklasse an den Listener und ruf sie von dort auf.
 

hdi

Top Contributor
Hä was ???:L Sorry, ich unterstelle dir jetzt mal dass du nicht ganz weißt wovon du sprichst ;) Sagt dir der Begriff Event Dispatch Thread etwas? Weißt du, wie das Event & Listener Konzept funktioniert? Hört sich nicht so an. Ein ActionListener ruft gar nix auf, das ist kein Thread sondern nur ein Objekt. Und in meinem Programm gibt's (zumindest sobald die ersten paar Nanosekunden (main-Methode) durch sind) auch nicht 3 Threads, sondern nur zwei: Den EDT und den Thread, der im Konstruktor gestartet wird.

Falls ich dir unrecht tue, dann musst du das nochmal etwas genauer erklären - vorallem mit den passenden Ausgaben aufzeigen. Ich glaube aber wenn du dir die sysouts meiner Demo nochmal richtig ankuckst stellst du fest, dass das was du sagst nicht stimmen kann :bae:

Der aufrufende Thread ist der, der das new KSKB() macht und der blockiert.
Der aufrufende Thread ist der EDT. Und genau dieser Thread ist auch für das Zeichnen der ProgressBar zuständig. Siehe sysouts.
 

Marco13

Top Contributor
Dort steht auch nur "Der startet einen neuen Event Pump, mehr dazu steht im source code". So weit waren sowohl vermutlich hdi als auch ich auch schon.

Wie genau dieser Event Pump aber funktioniert ist ja das spannende. Woher weiß er, dass er die progressBar.setValue-Aufrufe, die aus dem anderen Thread auf die EventQueue gelegt werden, verarbeiten soll, aber MouseEvents, die direkt an die ProgressBar gehen würden, abgefangen werden?

Ganz grob, mit viel Spekulation, wenig Schlaf und Koffein und dunkler Erinnerung an Methodennamen wie "pumpEventForFilters": Offenbar läuft dort doch kein komplett neuer EDT. Es scheinbar nur eine "Abfang-Schicht" um den (immer weiter laufenden) EDT drumgewickelt, die dafür sorgt, dass die Events für den Dialog weiterverarbeitet werden, und auch Dinge, die mit SwingUtilities.invokeLater in einen InvocationEvent gepackt und gequeued werden, aber Benutzereingaben für alles andere als den Dialog abgefangen werden.

Die Details reichen aber anscheinend recht schnell in Implementierungsspezifika von Dialog, AppContext, EventDispatchThread und Toolkit, und könnten deswegen schwer nachzuvollziehen sein.
 
B

bERt0r

Gast
Java:
private boolean conditionalShow(Component JavaDoc toFocus, AtomicLong JavaDoc time) {
440         boolean retval;
441
442         synchronized (getTreeLock()) {
443             if (peer == null) {
444                 addNotify();
445             }
446             validate();
447             if (visible) {
448                 toFront();
449                 retval = false;
450             } else {
451                 visible = retval = true;
452                 if (toFocus != null && time != null && isFocusable() && isEnabled())
453                 {
454                     // keep the KeyEvents from being dispatched
455 // until the focus has been transfered
456 time.set(Toolkit.getEventQueue().getMostRecentEventTimeEx());
457                     KeyboardFocusManager.getCurrentKeyboardFocusManager().
458                         enqueueKeyEvents(time.get(), toFocus);
459                 }
460
461                 peer.show(); // now guaranteed never to block
462
463                 for (int i = 0; i < ownedWindowList.size(); i++) {
464                     Window JavaDoc child = ownedWindowList.elementAt(i).get();
465                     if ((child != null) && child.showWithParent) {
466                         child.show();
467                         child.showWithParent = false;
468                     } // endif
469 } // endfor
470 Window.updateChildFocusableWindowState(this);
471
472         createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
473                       this, parent,
474                       HierarchyEvent.SHOWING_CHANGED,
475                                       Toolkit.enabledOnToolkit(AWTEvent.HIERARCHY_EVENT_MASK));
476             }
477         if (retval && (componentListener != null ||
478                (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 ||
479                            Toolkit.enabledOnToolkit(AWTEvent.COMPONENT_EVENT_MASK))) {
480             ComponentEvent e =
481             new ComponentEvent(this, ComponentEvent.COMPONENT_SHOWN);
482         Toolkit.getEventQueue().postEvent(e);
483         }
484         }
485
486         if (retval && (state & OPENED) == 0) {
487             postWindowEvent(WindowEvent.WINDOW_OPENED);
488             state |= OPENED;
489         }
490
491         return retval;
492     }

Read more: [url=http://kickjava.com/src/java/awt/Dialog.java.htm#ixzz1SXb9wgL5]Java > Open Source Codes > java > awt > Dialog _ Java API By Example, From Geeks To Geeks.[/url]
Der Dialog fängt nur die KeyEvents ab, ChangeEvents nicht.
 

Sonecc

Gesperrter Benutzer
Der Sourcecode auf den du dich beziehst ist veraltet

Edit: Hab aber nicht geprüft ob Unterschiede zur aktuellen Version bestehen
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Abgesehen davon, dass er veraltet zu sein scheint (d.h. anscheinend(!) noch nicht die oben angedeutete "new modality API" enthält) gibt es zu dem "conditionalShow" nicht viel zu sagen, außer
Code:
507         if (!isModal()) {
508             conditionalShow(null, null);
509         } else {
...
 (HIER kommt dann der relevante und deutlich kompliziertere Teil)
 
B

bERt0r

Gast
Also sehr viel Energie investierst du nicht nur um irgendwas zu bemängeln. Die definition eines Modalen Dialogs sagt ja schon bereits dass er den Event dispatch thread nicht blockt, sonder nur die Benutzereingaben.
@param modal specifies whether dialog blocks user input to other top-level
* windows when shown. If <code>true</code>, the modality type property is set to
* <code>DEFAULT_MODALITY_TYPE</code>, otherwise the dialog is modeless.
 

hdi

Top Contributor
Die definition eines Modalen Dialogs sagt ja schon bereits dass er den Event dispatch thread nicht blockt, sonder nur die Benutzereingaben.

Genau darum geht's ja:

Aber fast nirgendwo wird erwähnt, dass das Anzeigen eines solchen Dialogs dazu führt, dass es in der Ausführung im Code nicht mehr weitergeht, bis er wieder geschlossen ist.

Und wir versuchen hier herauszufinden wie das nun implementiert ist

Aber ihr habt meine Frage schon ausreichend beantwortet, danke. Ich hab jetzt persönlich auch nicht unbedingt Lust mir den gesamten Source Code dazu reinzupfeifen. Es ging mir nur darum diese "schwarze Magie" die passiert zu verstehen.
 
Zuletzt bearbeitet:

Ähnliche Java Themen

Neue Themen


Oben