0

I'm programming a path finder visualizer with Kotlin and Java Swing and I have this Breadth-First Search function:

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

After each iteration of the while loop I want to redraw the Grid and wait a few seconds to make the algorithm view a little slower. I tried using Swing Timers, but I couldn't get it to work. I can't use 'Thread.sleep()' too because of Java Swing.

4
  • Use coroutines and just insert a delay? Commented Aug 23, 2021 at 22:23
  • 1
    You can't just insert an algorithm into a Java Swing application. You have to rewrite the algorithm so one step of the process at a time happens in a javax.swing.Timer ActionListener. Commented Aug 23, 2021 at 23:16
  • Swing is a single Thread library. All painting tasks are executed by the (EDT). Running long processes (such BFS) on the EDT keeps it busy, so it does not do update the gui (gui freezes). Run the long process on a different thread, preferably by using a SwingWorker, and there is also when you apply a "wait". See an example here Commented Aug 24, 2021 at 5:14
  • Another example Commented Aug 24, 2021 at 5:21

1 Answer 1

1

Swing is a single Thread library. All painting tasks are executed by the EDT.
Running long processes (such BFS) on the EDT keeps it busy, so it does not do update the gui (gui becomes unresponsive, freezes).
Run the long process on a different thread, but remember that all Swing gui updates needs to be done by the EDT.
The different thread is where you can apply a delay for slowing down the process for better visualization.
A good tool to perform such tasks is a SwingWorker.
The following is a single-file mre (copy-paste the entire code to AlgoVisualitation.java and run) demonstrating the basic structure:

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

(Run it online)


enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.