java - 如何在 Java Swing 中的 while 循环迭代后等待
问题描述
我正在使用 Kotlin 和 Java Swing 编写路径查找器可视化工具,并且我有这个广度优先搜索功能:
fun bfs(): MutableList<Node>? {
Grid.resetNodes()
val queue: Queue<Node> = LinkedList()
queue.add(Grid.start!!)
Grid.start!!.state = State.IN_QUEUE
while (queue.isNotEmpty()) {
val current = queue.poll()
//println(current)
current.state = State.CLOSE
if (current == Grid.end!!) break
Grid.getNodeNeighbours(current).forEach { node ->
if (node.state == State.OPEN) {
node.parent = current
queue.add(node)
node.state = State.IN_QUEUE
}
}
GridPanel.repaint()
}
return getPath()
}
在 while 循环的每次迭代之后,我想重新绘制 Grid 并等待几秒钟以使算法视图变慢一点。我尝试使用 Swing Timers,但我无法让它工作。由于 Java Swing,我也不能使用 'Thread.sleep()'。
解决方案
Swing
是一个单线程库。所有的绘画任务都由EDT执行。
在 EDT 上运行长进程(如 BFS)使其忙碌,因此它不会更新 gui(gui 变得无响应,冻结)。
在不同的线程上运行长进程,但请记住,所有Swing
gui 更新都需要由 EDT 完成。
不同的线程是您可以应用延迟以减慢进程以获得更好的可视化的地方。
执行此类任务的好工具是SwingWorker
.
以下是演示基本结构的单文件mre(将整个代码复制粘贴到 AlgoVisualitation.java 并运行):
import java.awt.*;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class SwingAlgoVisualization {
private Cell[][] cells;
private static final int SIZE = 3, GAP = 2;
JPanel view() {
JPanel gridPanel = new JPanel();
gridPanel.setLayout(new GridLayout(SIZE, SIZE, GAP, GAP));
gridPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP,GAP, GAP));
cells = new Cell[SIZE][SIZE];
for(int row=0; row <cells.length; row++) {
for(int col=0; col<cells[row].length; col++) {
Cell cell = new Cell();
cells[row][col] = cell;
gridPanel.add(cell.getComponent());
}
}
return gridPanel;
}
void solve() {
//run long process on a SwingWorker
new LongTask().execute();
}
//use swing worker to perform long task off the EDT
class LongTask extends SwingWorker<Void,int[]> {
private static final long DELAY = 30;
private final Random rand = new Random();
@Override
public Void doInBackground() {
myLongComplexAlgo();
return null;
}
@Override
//runs on EDT
protected void process(List<int[]> results) {
for(int[] result : results){
cells[result[0]][result[1]].updateValue(result[2]);
}
}
void myLongComplexAlgo() { //simulates long process that repeatedly updates gui
while (true){ //todo add stop mechanism
//calculate(apply random value to random cell) and publish results
int row = rand.nextInt(cells.length);
int col = rand.nextInt(cells[0].length);
int value = RandomValue.randomDigit();
publish(new int[]{row,col,value});
try {
Thread.sleep(DELAY); //visualization delay
} catch (InterruptedException ex) { ex.printStackTrace();}
}
}
}
public static void main(String[] args) {
SwingAlgoVisualization av = new SwingAlgoVisualization();
JFrame jFrame = new JFrame("Random Value Change");
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setLocationRelativeTo(null);
jFrame.add(av.view());
jFrame.pack();
jFrame.setVisible(true);
av.solve();
}
}
class Cell {
private static int CELL_SIZE =100, BORDER = 1, FONT_SIZE = 20;
private final JLabel label;
Cell() {
label = new JLabel();
label.setFont(new Font("Calibri", Font.BOLD, FONT_SIZE));
updateValue(RandomValue.randomDigit());
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setBorder(BorderFactory.createLineBorder(Color.BLUE, BORDER));
label.setPreferredSize(new Dimension(CELL_SIZE , CELL_SIZE));
label.setOpaque(true);
}
void updateValue(int value){
label.setText(String.valueOf(value));
}
JComponent getComponent(){
return label;
}
}
class RandomValue{
private static Random rand = new Random();
public static int randomDigit(){
return rand.nextInt(10);
}
}
(在线运行)
推荐阅读
- youtube-data-api - YouTube 数据 API V3 配额限制为 0
- c# - 带有“cloud-builds”主题的 PubSub 通常会产生未确认的消息
- puppeteer - Puppeteer - 缺少 Unicode 图标
- jquery - 收到错误 net::ERR_CERT_AUTHORITY_INVALID
- reactjs - RTK 查询重置
- jenkins - Jenkins 默认负载平衡行为
- python - Bot 不会回应 Emoji Reaction
- javascript - Snowpack 3.8 找不到源文件夹,以不同的根目录运行开发服务器
- c# - C# 如何获取下载文件名 - Selenium(chrome)
- javascript - 根据下拉列表中选择的值动态设置单选按钮组的标签