简体   繁体   English

随时随地挥杆重涂

[英]Swing repaint on call

I'm using Java Swing to show a problem being solved recursively. 我正在使用Java Swing来展示递归解决的问题。

Every time there is a problem that is solved I'd like to repaint my GUI (Solved after if it passes queenSafe method). 每次解决问题时,我queenSafe重绘我的GUI(如果它通过queenSafe方法,则可以解决)。

However I'm having an issue here. 但是我在这里有一个问题。

I understand that the Event handler and graphics is controlled by the same thread so I can't just tell the thread to sleep to update the GUI when the flag is set. 我知道事件处理程序和图形是由同一线程控制的,因此设置标志时我不能仅仅告诉线程休眠以更新GUI。 So creating a new thread is best? 那么创建一个新线程是最好的吗? but not so sure on how to implement it. 但不确定如何实施。 As of right now I'm trying to use a flag to tell the other thread to repaint. 截至目前,我正在尝试使用一个标志告诉另一个线程重新绘制。 But this seems to give me an infinite loop. 但这似乎给了我一个无限循环。 Any suggestions? 有什么建议么?

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();
}

As MadProgrammer pointed out you could use a SwingWorker to facilitate this sort of behavior. 正如MadProgrammer指出的那样,您可以使用SwingWorker来促进这种行为。 However, I've provide a small example (not specific to your program) demonstrating how this delegation to background threads and updating the GUI on the event dispatch thread (EDT) can work. 但是,我提供了一个小示例(并非特定于您的程序),演示了这种委派给后台线程并更新事件分派线程(EDT)上的GUI如何工作。

Note, this is just one method you could adopt. 请注意,这只是您可以采用的一种方法。

The example includes two classes, GuiWorker which is where all the thread handling occurs, and ExampleFrame which uses GuiWorker to provide an example. 该示例包括两个类,其中GuiWorker是所有线程处理发生的位置,而ExampleFrame使用GuiWorker提供示例。

GuiWorker GuiWorker

This is an abstract class which defines the execution process, running the relevent tasks on the correct thread. 这是一个抽象类,用于定义执行过程,并在正确的线程上运行相关的任务。 It has two abstract methods backgroundProcess() and postExecute() which must be implemented. 它有两个必须实现的抽象方法backgroundProcess()postExecute()

backgroundProcess() will not be run on the EDT, but a background thread. backgroundProcess()不会在EDT上运行,而是在后台线程上运行。 This is run before postExecute() 它在postExecute()之前运行

postExecute() will be run on the EDT and is where the GUI updates after the background task has finished should be carried out. 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 ExampleFrame

This is just a small (and unimpressive!) GUI with a label. 这只是一个带有标签的小(且令人印象深刻!)GUI。 The main(String[] args) method is also defined here to reduce the number of classes in this example. 在此示例中,还定义了main(String[] args)方法以减少类的数量。

The main(String args) method (entry point) will construct a new ExampleFrame instance on the EDT using SwingUtilities.invokeLater(Runnable) main(String args)方法(入口点)将使用SwingUtilities.invokeLater(Runnable)在EDT上构造一个新的ExampleFrame实例。

For simplicity everything is carried out in the constructor. 为简单起见,一切都在构造函数中执行。 Setting up and showing the GUI with a single JLabel called output which initially has the text ' Initial ', as well as using GuiWorker to do some background task. 使用单个名为output JLabel设置并显示GUI,该JLabel最初具有文本“ Initial ”,并使用GuiWorker进行一些后台任务。 In this case, it will perform 10 iterations of a while loop, outputting i to the console (which increases by 1 with each iteration). 在这种情况下,它将执行while循环的10次迭代,将i输出到控制台(每次迭代增加1)。 Each iteration has a short pause on the background thread of 500ms. 每次迭代都会在500ms的后台线程上短暂暂停。 Once this is finished, the JLabel called output will be updated to say ' Finished '. 完成此操作后,将调用JLabel output更新为“ 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
    }

}

If data during execution of the background task is required for the GUI update you can always introduce class member fields when implementing GuiWorker as an anonymous class or otherwise which would then be accessible by postExecute() . 如果GUI更新在执行后台任务期间需要数据,则可以在将GuiWorker实现为匿名类时始终引入类成员字段,否则可以通过postExecute()进行访问。 Alternatively GuiWorker can be reworked to allow backgroundProcess() to return some data which is then passed to postExecute() as a parameter. 另外,可以对GuiWorker进行重做以允许backgroundProcess()返回一些数据,然后将其作为参数传递给postExecute()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM