我正在使用Java 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();
}

最佳答案

正如MadProgrammer指出的那样,您可以使用SwingWorker来促进这种行为。但是,我提供了一个小示例(并非特定于您的程序),演示了这种委派给后台线程并更新事件分派线程(EDT)上的GUI如何工作。

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

该示例包括两个类,GuiWorker是所有线程处理发生的位置,而ExampleFrame使用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)方法(入口点)将使用ExampleFrame在EDT上构造一个新的SwingUtilities.invokeLater(Runnable)实例。

为简单起见,一切都在构造函数中执行。使用名为JLabel的单个output设置并显示GUI,该GuiWorker最初具有文本“ Initial”,并使用i进行某些后台任务。在这种情况下,它将执行while循环的10次迭代,将JLabel输出到控制台(每次迭代增加1)。每次迭代都会在500ms的后台线程上短暂暂停。完成此操作后,称为outputGuiWorker将更新为“已完成”。

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更新需要在后台任务执行期间提供数据,则可以在将postExecute()实现为匿名类时始终引入类成员字段,否则可以通过GuiWorker进行访问。或者,可以对backgroundProcess()进行修改以允许postExecute()返回一些数据,然后将其作为参数传递给。

09-26 23:25