Auf Benutzereingabe/Fensterschließen warten

Was übersehe ich hier? Oder gibt es eine andere Möglichkeit dafür?

	private List<Double> getLinePrices( ) {
		List<Double> list = new ArrayList<>();
		Object lock = new Object();
		while (true) {
			JSlider s1 = new JSlider(-50, +50, 0);
			JFormattedTextField t1 = new JFormattedTextField(123.0);
			JFormattedTextField t2 = new JFormattedTextField(0.0);
			JFrame f1 = new JFrame();
			f1.setLayout(new GridLayout(3, 1));
			f1.add(s1);
			f1.add(t1);
			f1.add(t2);
			f1.pack();
			f1.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
			f1.setVisible(true);
			Thread t = new Thread() {
				@Override
				public void run() {
					synchronized (lock) {
						while (f1.isVisible()) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						System.out.println("Working now");
					}
				}
			};
			t.start();
			f1.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					synchronized (lock) {
						f1.setVisible(false);
						lock.notify();
					}
				}
			});
			try {
				t.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			double d1 = (double) t1.getValue();
			f1.dispose();
			list.add(d1);
			if (d1 == d1) { // temporär
				break;
			}
		}
		return list;
	}

(Den Slider muss ich noch verdrahten.)
Es wird nichts angezeigt. getLinePrices rufe ich innerhalb eines ActionListeners/EDT auf, liegt es vielleicht daran? Workaround?

Konnte das durch noch einen zusätzlichen Thread lösen; bin aber nicht sicher, ob das so „gut“ ist.

		b_graph_l.addActionListener(e -> {
			int selectedRow = table.getSelectedRow();
			if (selectedRow != -1) {
				int selectedRowConv = table.getRowSorter().convertRowIndexToModel(selectedRow);
				MyAsset a = model.getAsset(selectedRow);
				new Thread(() -> {
					List<Double> linePrices = getLinePrices(a);
					Collections.sort(linePrices);
					double[] ds = new double[linePrices.size()];
					for (int i = 0; i < ds.length; i++) {
						ds[i] = linePrices.get(i);
					}
					model.drawChart(selectedRowConv, ds);
				}).start();
			}
		});


	private volatile boolean yield = false;

	private List<Double> getLinePrices(MyAsset a) {
		List<Double> list = new ArrayList<>();
		Object lock = new Object();
		double d1 = a.actualPrice;
		do {
			JSlider s1 = new JSlider(-50, +50, 0);
			JTextField t1 = new JTextField(a.actualPrice + "");
			JTextField t2 = new JTextField(1.0 + "");
			JFrame f1 = new JFrame();
			f1.setLayout(new GridLayout(3, 1));
			f1.add(s1);
			f1.add(t1);
			f1.add(t2);
			f1.pack();
			f1.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
			f1.setVisible(true);
			s1.addChangeListener(src -> {
				if (yield) {
					return;
				}
//				if (!s1.getValueIsAdjusting()) {
				double val_a = s1.getValue() / 1000.0 + 1.0;
				double val_b = a.actualPrice * val_a;
				double val_c = val_b / a.actualPrice;
				SwingUtilities.invokeLater(() -> {
					yield = true;
					t1.setText(val_b + "");
					t2.setText(val_c + "");
					yield = false;
				});
//				}
			});
			t1.getDocument().addDocumentListener(new DocumentListener() {
				@Override
				public void changedUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void removeUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void insertUpdate(DocumentEvent e) {
					update();
				}

				public void update() {
					if (yield) {
						return;
					}
					try {
						double val_b = Double.parseDouble(t1.getText());
						double val_c = val_b / a.actualPrice;
						int val_a = (int) Math.round((val_c - 1.0) * 1000.0);
						SwingUtilities.invokeLater(() -> {
							yield = true;
							s1.setValue(val_a);
							t2.setText(val_c + "");
							yield = false;
						});
					} catch (NullPointerException | NumberFormatException igno) {
					}
				}
			});
			t2.getDocument().addDocumentListener(new DocumentListener() {
				@Override
				public void changedUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void removeUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void insertUpdate(DocumentEvent e) {
					update();
				}

				public void update() {
					if (yield) {
						return;
					}
					try {
						double val_c = Double.parseDouble(t2.getText());
						double val_b = a.actualPrice * val_c;
						int val_a = (int) Math.round((val_c - 1.0) * 1000.0);
						SwingUtilities.invokeLater(() -> {
							yield = true;
							s1.setValue(val_a);
							t1.setText(val_b + "");
							yield = false;
						});
					} catch (NullPointerException | NumberFormatException igno) {
					}
				}
			});
			Thread t = new Thread() {
				@Override
				public void run() {
					synchronized (lock) {
						while (f1.isVisible()) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				}
			};
			t.start();
			f1.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					synchronized (lock) {
						f1.setVisible(false);
						lock.notify();
					}
				}
			});
			try {
				t.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			try {
				d1 = Double.parseDouble(t1.getText());
			} catch (NullPointerException | NumberFormatException igno) {
			}
			f1.dispose();
			list.add(d1);
		} while (d1 != a.actualPrice);
		return list;
	}

Sieht nach Spaghetti aus, oder?

Ja (obligatorische mindestens 6 Zeichen)

Ok, das sollte man natürlich in eine eigene Klasse auslagern. Das ist da unschön. Aber sonst wüsste ich nicht, wie es anders, also kürzer, ginge.

Zusammenfassung
	private volatile boolean yield = false;

	private List<Double> getLinePrices(final double ref) {
		List<Double> list = new ArrayList<>();
		Object lock = new Object();
		double d1 = ref;
		do {
			JSlider s1 = new JSlider(-50, +50, 0);
			JTextField t1 = new JTextField(ref + "");
			JTextField t2 = new JTextField(1.0 + "");
			JFrame f1 = new JFrame();
			f1.setLayout(new GridLayout(3, 1));
			f1.add(s1);
			f1.add(t1);
			f1.add(t2);
			f1.pack();
			f1.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
			f1.setVisible(true);
			s1.addChangeListener(src -> {
				if (yield) {
					return;
				}
//				if (!s1.getValueIsAdjusting()) {
				double val_a = s1.getValue() / 1000.0 + 1.0;
				double val_b = ref * val_a;
				double val_c = val_b / ref;
				SwingUtilities.invokeLater(() -> {
					yield = true;
					t1.setText(val_b + "");
					t2.setText(val_c + "");
					yield = false;
				});
//				}
			});
			t1.getDocument().addDocumentListener(new DocumentListener() {
				@Override
				public void changedUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void removeUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void insertUpdate(DocumentEvent e) {
					update();
				}

				public void update() {
					if (yield) {
						return;
					}
					try {
						double val_b = Double.parseDouble(t1.getText());
						double val_c = val_b / ref;
						int val_a = (int) Math.round((val_c - 1.0) * 1000.0);
						SwingUtilities.invokeLater(() -> {
							yield = true;
							s1.setValue(val_a);
							t2.setText(val_c + "");
							yield = false;
						});
					} catch (NullPointerException | NumberFormatException igno) {
					}
				}
			});
			t2.getDocument().addDocumentListener(new DocumentListener() {
				@Override
				public void changedUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void removeUpdate(DocumentEvent e) {
					update();
				}

				@Override
				public void insertUpdate(DocumentEvent e) {
					update();
				}

				public void update() {
					if (yield) {
						return;
					}
					try {
						double val_c = Double.parseDouble(t2.getText());
						double val_b = ref * val_c;
						int val_a = (int) Math.round((val_c - 1.0) * 1000.0);
						SwingUtilities.invokeLater(() -> {
							yield = true;
							s1.setValue(val_a);
							t1.setText(val_b + "");
							yield = false;
						});
					} catch (NullPointerException | NumberFormatException igno) {
					}
				}
			});
			Thread t = new Thread() {
				@Override
				public void run() {
					synchronized (lock) {
						while (f1.isVisible()) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				}
			};
			t.start();
			f1.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					synchronized (lock) {
						f1.setVisible(false);
						lock.notify();
					}
				}
			});
			try {
				t.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			try {
				d1 = Double.parseDouble(t1.getText());
			} catch (NullPointerException | NumberFormatException igno) {
			}
			f1.dispose();
			list.add(d1);
		} while (d1 != ref);
		return list;
	}

Es wäre wirklich begrüßenswert, wenn uns nicht mit diesem Müll belästigen würdest. Swing kann so schön sein. Was du daraus machst, ist traurig. Aber… um zumindest noch Konstruktivität zu heucheln: Vermutlich suchst du einen (modalen) JDialog.

Nein, nicht direkt. Modal würde den Rest der Anwendung sperren, das Warten auf den modalen Dialog wäre aber in ähnlicher Weise unübersichtlich.

Es geht mir auch nicht darum, Swing anzuprangern, das steht mir gar nicht zu. Ich suche hier einfach nur nach einer eleganten Möglichkeit für das beschriebene Problem.

Du hast kein Problem beschrieben. Und das ist Teil des Problems.

Meine Güte, ist das anstrengend… :roll_eyes:

Ich möchte in einen Graphen beliebig viele Linien einzeichnen können. Zum Beispiel bei 1.5, 1.45 und 1.55.

Dafür möchte ich ein Fenster haben, um die Werte eingeben zu können. Ich möchte also beliebig viele double Werte eingeben können. Die Werte sollen auch über einen „Schieberegler“ eingegeben werden können.

Das UI muss aber auf diese Eingaben warten, ehe sie die Linien einzeichnen kann (logisch).

Also gibt es einen „Enter Data…“ button, der einen Dialog aufmacht, in dem der Regler ist, und wenn man den Dialog mit „OK“ beendet, wird der Wert übernommen, und bei „Cancel“ wird er nicht übernommen. So war sowas schon immer, und da gibt’s Mechanismen dafür. Ohne volatile. Sonst noch was?

Das volatile war überflüssig, stimmt.

Richtig. Hast du dazu einen Vorschlag?

Siehe Klassenname

package bytewelt;

import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class UseAModalDialogForEffsSake
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("Now what?");
        JTextField textField = new JTextField();

        button.addActionListener(e -> obtainSomeValueFor(textField));
        f.getContentPane().add(button, BorderLayout.NORTH);
        f.getContentPane().add(textField, BorderLayout.CENTER);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static void obtainSomeValueFor(JTextField textField)
    {
        JSlider slider = new JSlider(0, 100, 50);
        int n = JOptionPane.showConfirmDialog(
            null, slider, "For effs sake",
            JOptionPane.OK_CANCEL_OPTION);
        if (n == JOptionPane.OK_OPTION)
        {
            textField.setText(String.valueOf(slider.getValue()));
        }
        else
        {
            textField.setText("Cancelled");
        }
    }
}

Sehr schön. Es geht. :pray:

import java.awt.BorderLayout;
import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class UseAModalDialogForEffsSake {
	public static void main(String[] args) {
		SwingUtilities.invokeLater(() -> createAndShowGui());
	}

	private static void createAndShowGui() {
		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JButton button = new JButton("Now what?");
		JTextField textField = new JTextField();

		button.addActionListener(e -> obtainSomeValueFor(textField, 123.0));
		f.getContentPane().add(button, BorderLayout.NORTH);
		f.getContentPane().add(textField, BorderLayout.CENTER);
		f.pack();
		f.setLocationRelativeTo(null);
		f.setVisible(true);
	}

	private static boolean yield = false;

	private static void obtainSomeValueFor(JTextField textField, final double ref) {
		// the components
		JSlider s1 = new JSlider(-50, +50, 0);
		JTextField t1 = new JTextField(ref + "");
		JTextField t2 = new JTextField(1.0 + "");
		JPanel p1 = new JPanel();
		p1.setLayout(new GridLayout(3, 1));
		p1.add(s1);
		p1.add(t1);
		p1.add(t2);

		// the behavior
		s1.addChangeListener(src -> {
			if (yield) {
				return;
			}
//			if (!s1.getValueIsAdjusting()) {
			double val_a = s1.getValue() / 1000.0 + 1.0;
			double val_b = ref * val_a;
			double val_c = val_b / ref;
			SwingUtilities.invokeLater(() -> {
				yield = true;
				t1.setText(val_b + "");
				t2.setText(val_c + "");
				yield = false;
			});
//			}
		});
		t1.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void changedUpdate(DocumentEvent e) {
				update();
			}

			@Override
			public void removeUpdate(DocumentEvent e) {
				update();
			}

			@Override
			public void insertUpdate(DocumentEvent e) {
				update();
			}

			public void update() {
				if (yield) {
					return;
				}
				try {
					double val_b = Double.parseDouble(t1.getText());
					double val_c = val_b / ref;
					int val_a = (int) Math.round((val_c - 1.0) * 1000.0);
					SwingUtilities.invokeLater(() -> {
						yield = true;
						s1.setValue(val_a);
						t2.setText(val_c + "");
						yield = false;
					});
				} catch (NullPointerException | NumberFormatException igno) {
				}
			}
		});
		t2.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void changedUpdate(DocumentEvent e) {
				update();
			}

			@Override
			public void removeUpdate(DocumentEvent e) {
				update();
			}

			@Override
			public void insertUpdate(DocumentEvent e) {
				update();
			}

			public void update() {
				if (yield) {
					return;
				}
				try {
					double val_c = Double.parseDouble(t2.getText());
					double val_b = ref * val_c;
					int val_a = (int) Math.round((val_c - 1.0) * 1000.0);
					SwingUtilities.invokeLater(() -> {
						yield = true;
						s1.setValue(val_a);
						t1.setText(val_b + "");
						yield = false;
					});
				} catch (NullPointerException | NumberFormatException igno) {
				}
			}
		});

		// your stuff
		int n = JOptionPane.showConfirmDialog(null, p1, "For effs sake", JOptionPane.OK_CANCEL_OPTION);
		if (n == JOptionPane.OK_OPTION) {
			textField.setText(t1.getText());
		} else {
			textField.setText("Cancelled");
		}
	}
}

Hättest du zu private static boolean yield = false; noch eine Idee?

Sinn un Zweck: Zu einem Wert (hier 123.0) soll ein prozentualer Anteil (von -5 bis +5%) eingegeben werden können. Und die Werte sollen sofort sichtbar sein.

(Aber vielen Dank erst mal.)

„Guck mal, hier ist ganz viel zusammengeklöppelter code, mach’ den mal besser“. Nein. Stell eine passende Frage, aber richtig, und beschränk’ dich auf das Problem. Was auch immer du da mit dem yield zusammengepfuscht hast, es hat ja nichts mehr mit der Eingabe zu tun (die im Endeffekt mit einer Zeile erledigt war, auf die ich dich schon vorher hingewiesen hatte). Mann-oh-mann… :roll_eyes:

Doch, es gibt drei Möglichkeiten für die Eingabe und eine „Dreiecksbeziehung“: Wenn ich einen der drei Werte ändere, sollen sich auch die anderen beiden ändern, jedoch nicht der gerade geänderte Wert. (Ich weiß leider nicht genau, es besser zu beschreiben).

Da ist so ein boolean schon OK, von allem anderen mal abgesehen.

Aber ich verstehe die Verhaltensweise des EDTs noch nicht ganz. Ich rufe ja jede Änderung mit invokeLater auf. Eigentlich müsste dann doch „unmittelbar“ „währenddessen“ der DocumentListener der anderen Textfelder aufgerufen werden und es käme zu wechselseitigen Aktualisierungen. Aber anscheinend ist das nicht so. Kannst du mir vielleicht kurz erklären, warum/wie das funktioniert… und ob es immer funktioniert? Danke. Dann wäre das Thema auch schon beendet.

Ein anderes Thema wollte ich nicht aufmachen, weil ich denke, alles hängt hier i-wie miteinander zusammen. :confused:

Ich habe kein Interesse daran, auseinanderzuklamüsern, was du mit dem Code vielleicht erreichen wolltest.

Ok.

Ich ändere mal meinen Kommunikationsstil. Ich fühle mich mit meiner Frage gerade ein bisschen so (und das entnehme ich deiner Reaktion…), als ob ich etwas falsch gemacht hätte. Aber vielleicht ist das ja nur ein falscher subjektiver Eindruck von mir.

Oder anders: gestresst gerade?

Gerade? Immer.

Ich weiß nicht genau, wie oft ich es noch wiederholen muss oder werde, aber du postest irgendwelchen Code, der in so vieler Hinsicht schlecht ist, dass ich für die Kritikpukte eine Stichpunktliste erstellen müßte, (… hey, Moment mal, das hab’ ich doch schon ein paarmal! :thinking: … wie auch immer … ) und stellst nicht mal eine direkte Frage, sondern nur irgendwelches vages Gelaber, und erwartest, dass jemand deinen Murx liest, deine Ziele errät, erahnt, was die Frage ist, und ~„eine Lösung postet“. Ich könnte jetzt andeuten, welchen „Verlauf“ ich bei diesem Thread eigentlich erwarten würde (…hey, Moment mal, das hab’ ich doch schon ein paarmal! :thinking: … wie auch immer), aber ich weiß, dass es nichts bringen wird.

Ehm, der Code ist nicht schlecht. Mäßige mal deinen Ton.