我正在使用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的后台线程上短暂暂停。完成此操作后,称为output
的GuiWorker
将更新为“已完成”。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()
返回一些数据,然后将其作为参数传递给。