Swing Problem mit Warten-Dialog

Diskutiere Problem mit Warten-Dialog im AWT, Swing, JavaFX & SWT Forum; Hallo, in meinem Programm gibt es einige Aktivitäten, die etwas länger dauern. Dies soll dem Anwender über einen nicht modalen Dialog angezeigt...

  1. kodela
    kodela Mitglied
    Hallo,

    in meinem Programm gibt es einige Aktivitäten, die etwas länger dauern. Dies soll dem Anwender über einen nicht modalen Dialog angezeigt werden, also "Ich mach jetzt das oder jenes, Bitte etwas warten".
    Den Dialog habe ich unter NetBeans mit dem GUI-Builder erstellt. Der Code sieht so aus:
    Code (Java):

    public class HinweisWarten extends JDialog {
        private static final long serialVersionUID = 1L;
        public HinweisWarten(java.awt.Frame parent) {
            super(parent, true);
            initComponents();
            setModal(false); // testhalber von mir eingesetzt
            setLocationRelativeTo(parent);
        }

        public void zeigeHinweis(String txt){
            labelText.setText(txt);
        }

        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                        
        private void initComponents() {
            labelText = new javax.swing.JLabel();
        // es folgt der generierte Code, den ich ausblende
    }
    Obwohl ich bei den Eigenschaften die Option "modal" nicht aktiviert habe, wird der Dialog modal ausgeführt. Das heißt, das Warten-Fenster wird angezeigt, aber damit wird das Programm, der Teil also, wegen dem der Anwender warten muss, nicht ausgeführt. Der Dialog verhält sich modal.

    Wenn ich unter den Eigenschaften für modalityType null festlege oder zusätzlich wie oben zu sehen in meinem Code setModal(false) hinzufüge, dann funktioniert das Verhalten des Dialoges einerseits so, wie es zu erwarten wäre, während der Dialog steht, kann der zeitaufwendige Teil abgearbeitet werden, aber der Hinweis-Text wird nicht angezeigt.

    Was muss gemacht werden, dass beides funktioniert, dass der Text angezeigt wird, der Dialog aber die Fortsetzung des Programms nicht verhindert?

    Danke vorweg,
    kodela
     
  2. Wenn du jetzt Java lernen möchtest, dann sichere dir hier unseren Java-Videokurs heute zum Sonderpreis (hier klicken!)
  3. kodela
    kodela Mitglied
    Ergänzung:
    Grundsätzlich wäre es wünschenswert, dass das Warten-Fenster modal ist, damit der Anwender keine Möglichkeit hat, den Prozess im Hintergrund zu stören. Bei einem Teil der Hintergrundprozesse, z.b. Prüfung auf eindeutige Lösbarkeit einer Aufgabe muss die aufrufende Methode auf das Ergebnis warten.
     
  4. mihe7
    mihe7 Bekanntes Mitglied
  5. kodela
    kodela Mitglied
    Hallo mihe7,

    danke für den interessanten Tipp, ich werde ihn mir vormerken, falls ich einmal so eine Fortschrittsanzeige brauche, aber für meine Fälle tut es ein ganz einfacher Hinweis, dass es einen Moment dauern kann, bis der Anwender weiter machen kann. Wie lange das ist, wäre in einigen Fällen in etwa berechenbar, in anderen überhaupt nicht, zum Beispiel, wenn ein extrem schweres Str8ts gelöst und anschließend geprüft werden muss, ob die gefundene Lösung eindeutig ist. Es ist auch nicht vorhersehbar, wann eine solche Situation auftritt und der Anwender weiß es ja auch nicht und ohne einen Hinweis ist er dann irritiert, dass da plötzlich überhaupt nichts geschieht.

    Mir wäre sehr geholfen, wenn ich wüsste, wie man es anstellt, dass während der Anzeige eines solchen Warte-Hinweises im Hintergrund das erledigt wird, weswegen der Hinweis ja kommt oder zumindest, als zweitbeste Lösung wenn der Dialog nicht modular ist, dass dann der Hinweis im Dialogfenster auch gezeigt wird. Im Augenblick kommt nur ein leeres Fenster.

    Ich habe übrigens gesehen, dass man von der von Dir verlinkten Seite noch eine ganze Menge anderer hoch interessanter Seiten erreichbar sind. Zu meinem speziellen Problem habe ich aber leider, zumindest bisher, nichts gefunden.

    Gruß, kodela
     
    Zuletzt bearbeitet: 15. März 2019
  6. mihe7
    mihe7 Bekanntes Mitglied
    Code (Java):

    import java.awt.BorderLayout;
    import java.awt.Window;
    import javax.swing.*;

    public class Test {

        private JLabel result;

        private class UnknownLengthTask extends SwingWorker<Integer, Void> {
            @Override
            public Integer doInBackground() {
                // lange Berechnung
                try { Thread.sleep(2000); } catch (InterruptedException ex) {}
                return (int)(Math.random()*Integer.MAX_VALUE);
            }

            @Override
            public void done() {
                try {
                    result.setText("Ergebnis: " + get());
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }

        public void calculate(Window parent) {
            JDialog dialog = new JDialog(parent, "Bitte warten");
            dialog.add(new JLabel("Dauert einen Moment"));
            dialog.pack();
            dialog.setModal(true);
         
            UnknownLengthTask task = new UnknownLengthTask();
            task.addPropertyChangeListener(e -> {
                if ("state".equals(e.getPropertyName())) {
                    if (SwingWorker.StateValue.DONE == e.getNewValue()) {
                        dialog.dispose();
                    }
                }
            });

            task.execute();
            dialog.setVisible(true);

            JOptionPane.showMessageDialog(parent, "Danke");
        }

        public void run() {
            result = new JLabel(" ");
            JPanel output = new JPanel();
            output.add(result);

            final JButton start = new JButton("Start");
            start.addActionListener(e -> calculate(SwingUtilities.getWindowAncestor(start)));
         
            JPanel input = new JPanel();
            input.add(start);

            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.add(input, BorderLayout.NORTH);
            frame.add(output, BorderLayout.SOUTH);
            frame.setSize(400, 200);
            frame.setVisible(true);
        }

        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> new Test().run());
        }
    }
     
  7. kodela
    kodela Mitglied
    Danke mihe7,

    ich habs schon mal ausprobiert, und Morgen werde ich das Beispiel dann auch studieren.

    Gruß, kodela
     
  8. kodela
    kodela Mitglied
    Hallo,

    @mihe7, ich habe Deinen Code, für den ich mich nochmal herzlich bedanken möchte, etwas überarbeitet, damit er besser zu meinen Verhältnissen passt. Er sieht jetzt so aus:
    Angepasster Code von mihe7 (open)

    Code (Java):
    import java.awt.BorderLayout;
    import java.awt.Window;
    import javax.swing.*;

    public class Test {

        private JLabel result;
        private javax.swing.JLabel labelText;

        private class UnknownLengthTask extends SwingWorker<Boolean, Void> {
            @Override
            public Boolean doInBackground() {
                // lange Berechnung (Eindeutigkeitsprüfung)
                try {
                    Thread.sleep(2000);
                }
                catch (InterruptedException ex) {
                }
                return (int)(Math.random()*Integer.MAX_VALUE) % 2 == 0;
            }

            @Override
            public void done() {
                try {
                    result.setText("Ergebnis: " + get());
                }
                catch (Exception ex) {
                }
            }
        }

        /**
         * Damit wird der Warten-Dialog erzeugt und angezeigt und
         * die im Hintergrund laufende Aktion angestoßen
         * @param parent
         */

        public void calculate(Window parent) {
            // zuerst den Warten-Dialog erzeugen
            JDialog dialog = new JDialog(parent);
            labelText = new javax.swing.JLabel();

            dialog.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
            dialog.setTitle("Hinweis");
    //        dialog.setName("dialogWarten");
            dialog.setResizable(false);

            labelText.setFont(new java.awt.Font("Tahoma", 1, 12));
            labelText.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
            labelText.setText("<html><center><b>Prüfe Str8ts auf Eindeutigkeit.<br><br>"
                    + "Bitte solange warten.</b></center></html>");
            labelText.setMaximumSize(new java.awt.Dimension(116, 45));

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(dialog.getContentPane());
            dialog.getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(labelText, javax.swing.GroupLayout.PREFERRED_SIZE,
                            233, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(20, 20, 20)
                    .addComponent(labelText,
                            javax.swing.GroupLayout.DEFAULT_SIZE, 76, Short.MAX_VALUE)
                    .addGap(24, 24, 24))
            );
            dialog.pack();
            dialog.setLocationRelativeTo(parent);
            dialog.setModal(true);
         
            // in meinem Fall die Eindeutigkeitsprüfung
            UnknownLengthTask task = new UnknownLengthTask();
            task.addPropertyChangeListener(e -> {
                if ("state".equals(e.getPropertyName())) {
                    if (SwingWorker.StateValue.DONE == e.getNewValue()) {
                        dialog.dispose();
                    }
                }
            });
            task.execute();
            dialog.setVisible(true);
        }

        /**
         * Damit werden die Voraussetzungen für den Test geschaffen
         */

        public void run() {
            result = new JLabel(" ");
            JPanel output = new JPanel();
            output.add(result);
            // zum Anstoßen des Testes
            final JButton start = new JButton("Start");
            start.addActionListener(
                    e -> calculate(SwingUtilities.getWindowAncestor(start)));
     
            JPanel input = new JPanel();
            input.add(start);

            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.add(input, BorderLayout.NORTH);
            frame.add(output, BorderLayout.SOUTH);
            frame.setSize(400, 200);
            frame.setLocation(600, 400);
            frame.setVisible(true);
        }

        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> new Test().run());
        }
    }

    Das könnte ich vermutlich so ähnlich in meinen Code integrieren. Allerdings sollte der Warten-Dialog auch für andere Aktionen verwendbar sein und deshalb habe ich dafür eine eigene Klasse erstellt. Die sieht so aus:
    Mein Warten-Dialog (open)

    Code (Java):
    import javax.swing.JDialog;

    /**
    * Gibt Warten-Dialog aus
    * @author Konrad
    */

    public class HinweisWarten extends JDialog {

        private static final long serialVersionUID = 1L;
        public HinweisWarten(java.awt.Frame parent) {
            super(parent, true);
            initComponents();
    //        setModal(false);
    //        damit verhindert der Dialog den Hintergrundorozess nicht,
    //        zeigt aber den Text nicht an
            setLocationRelativeTo(parent);
        }

        public void zeigeHinweis(String txt){
            labelText.setText(txt);
        }


        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                        
        private void initComponents() {

            labelText = new javax.swing.JLabel();

            setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
            setTitle("Hinweis");
            setName("dialogWarten"); // NOI18N
            setResizable(false);

            labelText.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N
            labelText.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
            labelText.setText("<html><center><b>Erzeuge Str8ts-Aufgaben.<br><br>Bitte solange warten.</b></center></html>");
            labelText.setMaximumSize(new java.awt.Dimension(116, 45));

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(labelText, javax.swing.GroupLayout.PREFERRED_SIZE, 233, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(20, 20, 20)
                    .addComponent(labelText, javax.swing.GroupLayout.DEFAULT_SIZE, 76, Short.MAX_VALUE)
                    .addGap(24, 24, 24))
            );

            labelText.getAccessibleContext().setAccessibleName("<html><center><b>Erzeuge Str8ts-Aufgaben.<br><br>Bitte solange warten.</b></center></html>");

            pack();
        }// </editor-fold>                      
        // Variables declaration - do not modify                  
        private javax.swing.JLabel labelText;
        // End of variables declaration                
    }
     

    Zum weiteren Verständnis hier noch der Code für die im Hintergrund auszuführende Eindeutigkeitsprüfung:
    Mein Code für Eindeutigkeitsprüfung (open)

    Code (Java):
    package str8ts;

    import static str8ts.GlobaleObjekte.LWF;
    import static str8ts.GlobaleObjekte.LWRT;

    /**
    * Überprüft Str8ts auf Eindeutigkeit
    * @author Konrad
    */

    public class EindeutigkeitChecken extends Thread
                                    implements Runnable, GlobaleObjekte {
        private LevelLoeser strLoeser;
        private final int[] aufgabe;
        private final int[] loesung;
     
        /**
         * Konstruktor
         * @param mf    Referenz auf Hauptklasse der Anwendung
         * @param hnw   Referenz auf Warten-Dialog
         * @param af    Bitcodierte Str8ts Aufgabe
         * @param ls    Bitcodierte Str8ts Lösung
         */

        EindeutigkeitChecken(Str8tsApp mf, HinweisWarten hnw, int[] af, int[] ls) {
            aufgabe = new int[81];
            kopieren(af, aufgabe);
            loesung = new int[81];
            kopieren(ls, loesung);      
            hnw.zeigeHinweis("<html><center><b>Eindeutigkeit wird überprüft."
                    + "<br><br>Bitte solange warten.</b></center></html>");
            hnw.setVisible(true);
        }
     
        @Override
        public void run() {
        }  
        /**
         * Überprüft das aktuelle Str8ts auf Eindeutigkeit.
         * @return true wenn eindeutig, ansonsten false
         */

        public boolean checkEindeutigkeit() {
            int afg[] = new int[81];
            int lsg[] = new int[81];
            kopieren(aufgabe, afg);
            for (int i = 0; i < 81; i++) {          
                int lw = loesung[i] & ~LWF;
                if (loesung[i] <= LWRT && loesung[i] > 9) {
                    for (int k = 1; k <= 9; k++) {
                        if (lw != k) {
                            kopieren(aufgabe, afg);
                            afg[i] = k | LWF;
                            bereinigeKListe(afg, k, i);                      
                            strLoeser = new LevelLoeser(null, afg, lsg, true);
                            int lev = strLoeser.loeseAufgabe();
                            if (lev > 0) {
                                return false;
                            }
                        }
                    }
                }
            }
            return true;
        }
     
        /**
         * Entfernt für eine Zelle alle durch einen gesetzten Wert unmöglichen
         * Kandidaten aus allen Zellen im Sichtbereich dieser Zelle.
         * @param strfld    zu bearbeitendes Str8tsfeld
         * @param knd       zu entfernender Kandidat
         * @param pos       Index der Zelle, welcher der Wert zugewiesen wurde
         */

        private void bereinigeKListe(int strfld[], int knd, int pos) {
            knd &= ~SZELLE;
            int z = (pos / 9) * 9;
            int s = pos % 9;
            // erst aus der Zeile entfernen
            for (int i = z; i <= z + 8; i++) {
                if (i == pos) {
                    continue;
                }
                if ((strfld[i] & LK_MASKE[knd]) == LK_MASKE[knd]) {
                    strfld[i] &= ~LK_MASKE[knd];
                    strfld[i] -= 1;
                }
            }
            // jetzt aus der Spalte entfernen
            for (int i = s; i < s + 73; i += 9) {
                if (i == pos) {
                    continue;
                }
                if ((strfld[i] & LK_MASKE[knd]) == LK_MASKE[knd]) {
                    strfld[i] &= ~LK_MASKE[knd];
                    strfld[i] -= 1;
                }
            }
        }
     
        /**
         * Kopiert die Werte und nicht nur die Referenzen für ein Str8tsfeld.
         * @param aufgabe   die zu kopierende Aufgabe
         * @param ziel      der Bereich, in den kopiert wird
         */

        @SuppressWarnings("ManualArrayToCollectionCopy")
        private void kopieren(int[]aufgabe, int ziel[]) {
            for (int i = 0; i < aufgabe.length; i++) {
                ziel[i] = aufgabe[i];
            }
        }      
    }
     

    @mihe7 oder jeder andere Interessierte: Das alles funktioniert bestens, nur die verdammte Anzeige des Warten-Dialoges. Im Augenblick habe ich auch keine Idee, wie ich das Konzept von mihe7 so umsetzen könnte, dass die Warten-Klasse allgemein anwendbar ist.

    Vielleicht kann mir jemand einen Denkanstoß geben, wie ich hier am besten vorgehen sollte.

    Danke schon einmal vorweg und Gruß,
    kodela
     
  9. mihe7
    mihe7 Bekanntes Mitglied
    Bei dem im Hintergrund auszuführenden Code handelt es sich um Anwendungslogik. Die hat nichts mit dem User Interface zu tun und dem entsprechend auch nichts davon zu wissen.

    Die Anwendungslogik wird von Deinem UI aus aufgerufen und an der Stelle kann ein Hinweis angezeigt werden. Du musst lediglich dafür sorgen, dass der Hinweis verschwindet/aktualisiert wird, nachdem die aufgerufene Logik vollständig abgearbeitet wurde. Das UI muss also mitbekommen, wenn eine Hintergrundaufgabe erledigt ist.

    Unter Swing bietet sich hierfür ein SwingWorker an. Der kann als Wrapper für die tatsächliche Anwendungslogik dienen. Alles, was Du dann tun musst, ist auf die "state"-Property lauschen, und den Hinweis schließen/aktualisieren.

    Im Fall oben bräuchtest Du in calculate nur einen HinweisDialog statt einem JDialog zu erzeugen.
     
  10. kodela
    kodela Mitglied
    Danke mihe7!
    Was Du zur Anwendungslogik schreibst, ist mir klar und ich habe von der Klasse, die den Prozess startet, ist nicht die UI, die Klasse für die Meldung bereits initialisiert. In meinem sehr ähnlichen Sudoku-Programm und auch im Str8ts-Programm funktioniert das ja grundsätzlich. Allerdings brauche ich in all den funktionierenden Fällen keinen Rückgabewert. Der macht mir jetzt erstmals Schwierigkeiten.

    Aber da hilft mir Dein Hinweis auf den SwingWorker offentlich weiter. Damit habe ich allerdings keine Erfahrung. Aber das kann man ja nachholen. Ich weiß nur, dass es die Aufgabe des SwingWorker's sein soll, eine Aufgabe im Hintergrund abzuarbeiten während der Anwender weiter arbeiten kann. Das darf bei mir nicht sein, im Gegenteil, das muss verhindert werden, denn nach der Feststellung der Eindeutigkeit muss ergebnisabhängig reagieren. Das ist ganz anders bei den bereits erwähnten anderen Fällen, wo der Warten-Hinweis zum Beispiel ausgegeben wird, wenn der Anwender z.B. 100 Str8ts ausdrucken will und zuvor der Drucker initialisiert wird oder eine große Anzahl von Str8ts aus einer Liste eingelesen werden soll. Das und ähnliche Aufgaben, auch die Eindeutigkeitsprüfung, laufen bei mir in eigenen Threads. Außer bei der Eindeutigkeitsprüfung wird aber nie auf ein auszuwertendes Ergebnis gewartet.

    Frage, was meinst Du mit "im Fall oben" und "HinweisDidalog statt JDialog"? Da habe ich momentan scheinbar ein Brett vor dem Kopf.

    Gruß, kodela
     
  11. mihe7
    mihe7 Bekanntes Mitglied
    Jein. Es geht erst einmal nur darum, dass der Event Dispatch Thread (EDT) des UIs nicht blockiert wird. Ansonsten würde z. B. Dein Fenster nicht neu gezeichnet, wenn Du beispielsweise kurz mal zu einer anderen Anwendung (z. B. Browser) und wieder zurück wechselst. Was der Benutzer in der Zeit mit Deiner Anwendung machen kann, hängt damit zwar zusammen, ist aber eine andere Frage.

    Der SwingWorker verwendet ebenfalls einen eigenen Thread. Der Vorteil besteht lediglich darin, dass er sich "EDT-konform" verhält (bestimmte Methoden werden intern mit invokeLater aufgerufen, so dass Du das nicht mehr machen musst) und gleich Methoden zum Informieren über den "Ausführungszustand" mitbringt.

    Korrigiere mich, wenn ich falsch liege: ich gehe davon aus, dass über das UI eine Aktion gestartet wird und diese eine Zeit lang dauert.
     
Passende Stellenanzeigen aus deiner Region:





Die Seite wird geladen...

Problem mit Warten-Dialog - Ähnliche Themen

Problem mit Hibernate c3p0
Problem mit Hibernate c3p0 im Forum Datenbankprogrammierung
Problem mit SOAP / javax.xml importieren
Problem mit SOAP / javax.xml importieren im Forum Java Basics - Anfänger-Themen
Problem mit Matheaufgabe / int first = mScanner.nextInt();
Problem mit Matheaufgabe / int first = mScanner.nextInt(); im Forum Java Basics - Anfänger-Themen
Eclipse EE und Java FX Autocomplete Probleme
Eclipse EE und Java FX Autocomplete Probleme im Forum IDEs und Tools
Problem mit Arraylist in Arraylist
Problem mit Arraylist in Arraylist im Forum Java Basics - Anfänger-Themen
Thema: Problem mit Warten-Dialog