首页 > 解决方案 > 如何在 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()'。

标签: javaswingkotlinsleepbreadth-first-search

解决方案


Swing是一个单线程库。所有的绘画任务都由EDT执行。
在 EDT 上运行长进程(如 BFS)使其忙碌,因此它不会更新 gui(gui 变得无响应,冻结)。
在不同的线程上运行长进程,但请记住,所有Swinggui 更新都需要由 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);
    }
}

在线运行)


在此处输入图像描述


推荐阅读