在调用时进行Swing重绘

问题描述:

我正在使用Java Swing以递归方式显示正在解决的问题。在调用时进行Swing重绘

每当有问题解决,我想重新绘制我的GUI(如果它通过queenSafe方法后解决)。

但是我在这里有一个问题。

我知道事件处理程序和图形是由同一个线程控制的,所以我不能只是告诉线程在设置标志时更新GUI来休眠。所以创建一个新线程是最好的?但不知道如何实施它。截至目前,我试图用一个标志来告诉其他线程重新绘制。但这似乎给了我一个无限循环。有什么建议么?

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

我不是100%确定你要达到的目标,或者更明确地说明为什么你需要一个线程来睡觉和事件的预期流程。但是,调用'Thread.run()'不会调度一个新的线程被创建,然后在该线程上运行该任务。相反,它只是在调用'Thread.run()'方法的线程上运行。这就是为什么你的程序基本上锁定了。你应该使用'Thread.start()',同时重写'run()'方法或者通过将'Runnable'实现传递给'Thread(Runnable)'构造函数(仍然使用Thread.start() )。 –

+2

另外,请注意,Swing不是线程安全的,在EDT上下文之外,不应对UI或其依赖的数据进行任何更改。请考虑使用“SwingWorker”,它提供了将更新呼叫同步到EDT的功能 – MadProgrammer

由于MadProgrammer指出,你可以使用一个SwingWorker以方便这种行为。但是,我提供了一个小例子(不是特定于您的程序),演示了如何使用该委派来后台线程和更新事件分派线程(EDT)上的GUI可以工作。

请注意,这只是您可以采用的一种方法。

该示例包括两个类GuiWorker,这是所有线程处理发生的地方,ExampleFrame使用GuiWorker来提供示例。

GuiWorker

这是一个抽象类定义了执行过程中,运行在正确的线程的培训相关的任务。它有两个抽象方法backgroundProcess()postExecute()必须实现。

backgroundProcess()不会在EDT上运行,而是后台线程。这在postExecute()

之前运行postExecute()将在EDT上运行,并且是在后台任务完成后GUI应该执行的地方。

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

这只是一个小(给人印象不深!)GUI带有标签。此处还定义了main(String[] args)方法以减少此示例中类的数量。

main(String args)方法(入口点)将利用SwingUtilities.invokeLater(Runnable)

为了简单起见一切在构造函数中执行构造上EDT新ExampleFrame实例。设置和显示带有单JLabel的GUI称为output最初具有文本“初始”,以及使用GuiWorker做一些后台任务。在这种情况下,它会执行10次while循环,输出i到控制台(每次迭代增加1)。每次迭代在500ms的后台线程上暂停一段时间。一旦完成,名为outputJLabel将更新为'已完成'。

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 
    } 

} 

如果后台任务的执行过程中的数据是必需的GUI更新实施GuiWorker作为一个匿名类或其他然后会被postExecute()可以访问时,你总是可以引入班级成员字段。或者GuiWorker可以返工以允许backgroundProcess()返回一些数据,然后将其作为参数传递给postExecute()