2017-10-12 4 views
0

Ich verwende Java Swing, um ein Problem zu zeigen, das rekursiv gelöst wird.Swing Repaint bei Anruf

Jedes Mal, wenn ein Problem gelöst ist, möchte ich meine GUI neu streichen (Gelöst nach, wenn es queenSafe Methode passiert).

Allerdings habe ich ein Problem hier.

Ich verstehe, dass der Event-Handler und Grafiken von demselben Thread gesteuert wird, so kann ich nicht nur den Thread in den Ruhezustand sagen, um die GUI zu aktualisieren, wenn das Flag gesetzt ist. Also ist es am besten, einen neuen Thread zu erstellen? aber nicht so sicher, wie man es implementiert. Im Moment versuche ich eine Flagge zu verwenden, um dem anderen Thread zu sagen, dass er neu streichen soll. Aber das scheint mir eine unendliche Schleife zu geben. Irgendwelche Vorschläge?

public void queensProblem(){ 
    this.guiUpdate(); 
    boolean solved = solveQueenBlindSearch(0); 
    if(!solved){ 
     JOptionPane.showMessageDialog(gui, "Sorry, but this is not solvable"); 
    }   
} 
boolean solveQueenBlindSearch(int col) 
{ 
    if (col >= cb.queens) 
     return true; 

    for (int row = 0; row < cb.columns; row++) 
    { 
     if (queenSafe(row, col)) 
     { 
      cb.board[row][col].piece = new Queen(); 
      this.flag = true; 
      if (solveQueenBlindSearch(col + 1) == true) 
       return true; 
      cb.board[row][col].piece = null; 
     } 
    } 
    return false; 
} 
void guiUpdate() { 
    new Thread() { 
     public void run() { 
      while(true){ 
       if(flag){ 
        mainLayout.removeAll(); 
        mainLayout.add(searches); 
        JPanel newChessboard = cb.drawChessboard(); 
        mainLayout.add(newChessboard); 
        mainLayout.repaint(); 
        flag = false; 
       } 
      } 
     }   
    }.run(); 
} 
+1

Ich bin nicht 100% sicher, was Sie erreichen möchten, oder genauer gesagt, warum Sie einen Thread zum Schlafen benötigen und den beabsichtigten Ablauf von Ereignissen. Der Aufruf von 'Thread.run()' plant jedoch nicht, dass ein neuer Thread erstellt wird, und führt dann die Task für diesen Thread aus. Stattdessen wird nur der Thread ausgeführt, von dem die 'Thread.run()' -Methode aufgerufen wurde. Deshalb schließt Ihr Programm im Wesentlichen. Sie sollten stattdessen 'Thread.start()' verwenden, während Sie immer noch die 'run() 'Methode überschreiben oder eine' Runnable'-Implementierung an den 'Thread (Runnable)' -Konstruktor übergeben (und immer noch 'Thread.start()' verwenden). –

+2

Beachten Sie auch, dass Swing nicht threadsicher ist. Sie sollten keine Änderungen an der Benutzeroberfläche oder den Daten vornehmen, auf die sie basieren, und zwar außerhalb des EDT-Kontextes. Verwenden Sie stattdessen einen 'SwingWorker', der Funktionen zum Synchronisieren von Aktualisierungsaufrufen mit dem EDT bietet – MadProgrammer

Antwort

1

Wie MadProgrammer wies darauf hin, Sie SwingWorker verwenden könnte diese Art von Verhalten zu erleichtern. Ich habe jedoch ein kleines Beispiel (nicht speziell für Ihr Programm) vorgestellt, das demonstriert, wie diese Delegierung an Hintergrundthreads und die Aktualisierung der GUI für den Ereignisversand-Thread (EDT) funktionieren kann.

Hinweis, dies ist nur eine Methode, die Sie übernehmen könnten.

Das Beispiel enthält zwei Klassen, GuiWorker wo die ganze Thread-Behandlung auftritt, und ExampleFrame, die GuiWorker verwendet, um ein Beispiel zu geben.

GuiWorker

Dies ist eine abstrakte Klasse, die den Ausführungsprozess definiert, auf dem richtigen Thread die relevent Aufgaben ausgeführt wird. Es hat zwei abstrakte Methoden backgroundProcess() und postExecute(), die implementiert werden müssen.

backgroundProcess() wird nicht auf dem EDT, sondern ein Hintergrund-Thread ausgeführt werden. Dies wird ausgeführt, bevor postExecute()

postExecute() auf dem EDT ausgeführt wird und wo die GUI aktualisiert wird, nachdem die Hintergrundaufgabe beendet wurde, sollte ausgeführt werden.

import javax.swing.SwingUtilities; 

public abstract class GuiWorker { 

    public abstract void backgroundProcess(); // method called on background thread 

    public abstract void postExecute(); // method called on EDT 

    public void execute() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       System.out.println("Running backgroundProcess() on EDT: " + SwingUtilities.isEventDispatchThread()); 

       // Execute backgroundProcess() on this background thread 
       backgroundProcess(); 

       // When backgroundProcess() pops, run postExecute() on the EDT 
       System.out.println("End of background process."); 
       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         System.out.println("Running postExecute() on EDT: " + SwingUtilities.isEventDispatchThread()); 
         postExecute(); 
        } 
       }); 
      } 
     }).start(); // start background thread 
    } 

} 

ExampleFrame

Dies ist nur eine kleine (und unscheinbar!) GUI mit einem Label. Die Methode main(String[] args) ist hier ebenfalls definiert, um die Anzahl der Klassen in diesem Beispiel zu reduzieren.

Der main(String args) Methode (Eintrittspunkt) wird eine neue Instanz auf dem ExampleFrame EDT konstruiert SwingUtilities.invokeLater(Runnable)

Einfachheit halber alles verwendet, ist in dem Konstruktor durchgeführt. Einrichten und zeigt die GUI mit einem einzigen JLabeloutput genannt, die zunächst den Text ‚Initial‘ sowie mit GuiWorker hat einige Hintergrundaufgabe zu tun. In diesem Fall führt es 10 Wiederholungen einer while-Schleife aus und gibt i an die Konsole aus (die sich bei jeder Iteration um 1 erhöht). Jede Wiederholung hat eine kurze Pause im Hintergrund-Thread von 500ms. Sobald dies abgeschlossen ist, die so genannte JLabeloutput zu sagen aktualisiert werden ‚ Finished‘.

import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.SwingUtilities; 

public class ExampleFrame extends JFrame { 

    private JLabel output = new JLabel("Initial"); 

    public static void main(String[] args) { 
     // Construct and show a new JFrame (ExampleFrame) on the EDT 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new ExampleFrame(); 
      } 
     }); 
    } 

    public ExampleFrame() { 
     System.out.println("Running ExampleFrame() constructor on EDT: " + SwingUtilities.isEventDispatchThread()); 

     // Setup GUI 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     add(output); 
     pack(); 
     setVisible(true); 

     // Implement the abstract methods of GuiWorker and invoke execute() to run 
     new GuiWorker() { 

      @Override 
      public void backgroundProcess() { 
       // To be run on a background thread 

       int i = 0; 
       // iterate 10 times, sleeping for 500 ms 
       // printing i to the console 
       while (i < 10) { 
        try { 
         Thread.sleep(500); 
        } catch (InterruptedException ex) { 
         ex.printStackTrace(); 
        } 
        System.out.println(i); 
        i++; 
       } 
      } 

      @Override 
      public void postExecute() { 
       // when the backgroundProcess has finished 
       // update the output JLabel on the EDT 
       output.setText("Finished"); 
      } 
     }.execute(); // invoke execute to start the worker 
    } 

} 

Wenn Daten während der Ausführung der Hintergrundaufgabe für das GUI-Update erforderlich ist, Sie immer Klassenmitgliederfelder einführen können, wenn GuiWorker als eine anonyme Klasse Umsetzung oder auf andere Weise, die dann von postExecute() zugänglich wären. Alternativ kann GuiWorker nachbearbeitet werden, damit backgroundProcess() einige Daten zurückgeben kann, die dann als Parameter an postExecute() übergeben werden.