在调用时进行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();
}
由于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的后台线程上暂停一段时间。一旦完成,名为output
的JLabel
将更新为'已完成'。
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()
。
我不是100%确定你要达到的目标,或者更明确地说明为什么你需要一个线程来睡觉和事件的预期流程。但是,调用'Thread.run()'不会调度一个新的线程被创建,然后在该线程上运行该任务。相反,它只是在调用'Thread.run()'方法的线程上运行。这就是为什么你的程序基本上锁定了。你应该使用'Thread.start()',同时重写'run()'方法或者通过将'Runnable'实现传递给'Thread(Runnable)'构造函数(仍然使用Thread.start() )。 –
另外,请注意,Swing不是线程安全的,在EDT上下文之外,不应对UI或其依赖的数据进行任何更改。请考虑使用“SwingWorker”,它提供了将更新呼叫同步到EDT的功能 – MadProgrammer