Sleep or wait time on every call to method from main in Swing class

I coded a Swing class where in the main there is only one call to a method solve(). This method implements a while loop and uses another method in almost every round, this method is called paint() and changes the value of only one JPanel inside of a 9x9 GridLayout, whose reference is stored in a 2D array.

I need a timer inisde paint() so before the panel is modified, 2 seconds pass. It does this the first time is called, but after the 2 seconds it does every round of the loop without waiting. The goal is to watch 2 seconds pass before any panel is modified. I tried using javax Timer. Thread.sleep() doesn't work in a Swing class apparently.

import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Stack;

public class SudokuApp {

    private JFrame frame;
    int i = 9;
    int j = 9;
    JPanel[][] board = new JPanel[i][j];
    JButton nextButton = new JButton("Resolver");
    Box[][] sudoku = new Box[9][9];
    Box newCurrent = new Box();

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    SudokuApp window = new SudokuApp();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public SudokuApp() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setTitle("Sudoku Solver");
        frame.setBounds(100, 100, 510, 555);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        frame.getContentPane().add(panel, BorderLayout.CENTER);
        panel.setLayout(new GridLayout(i, j));

        //Initiate board

        for (int m = 0; m < i; m++) {
            for (int n = 0; n < j; n++) {
                board[m][n] = new JPanel();
                JTextField textField = new JTextField(1);
                textField.setFont(new Font("Lucida Grande", Font.BOLD, 25));
                board[m][n].add(textField);
                panel.add(board[m][n]);
            }
        }

        Box last = new Box();
        for (int m = 0; m < this.i; m++) {
            for (int n = 0; n < this.j; n++) {
                sudoku[m][n] = new Box(m, n);
                if (m != 0 || n != 0) sudoku[m][n].back = last;
                last = sudoku[m][n];
            }
        }

        //Solve button action listener

        nextButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
            if (validBoard()) {
                nextButton.setEnabled(false);
                solve();
            }
            else JOptionPane.showMessageDialog(null, "Condiciones iniciales inválidas.");
        } });
        frame.getContentPane().add(nextButton, BorderLayout.SOUTH);
    }

    private boolean validBoard() {
        for (int m = 0; m < i; m++) {
            for (int n = 0; n < j; n++) {
                JTextField current = (JTextField) board[m][n].getComponent(0);
                String text = current.getText();
                if (text.length() == 1 && !text.contains("0")) {
                    try {
                        int number = Integer.parseInt(text);
                        if (!add(number, m, n)) return false;
                        current.setEditable(false);
                    } catch (NumberFormatException e) {
                        return false;
                    }
                } else {
                    current.setText("");
                    current.setEditable(false);
                }
            }
        }
        return true;
    }

    private void solve() {
        int i = 0;
        int j = 0;
        int newI = i + 1;

        while (i < 9) {
            while (j < 9) {
                Box current = sudoku[i][j];

                if (current.number == 0) {
                    HashSet<Integer> possibles = new HashSet<>();
                    HashSet<Integer> takenNumbers = getTakenNumbers(i, j);
                    for (int k = 1; k <= 9; k++) if (!takenNumbers.contains(k)) possibles.add(k);

                    if (possibles.isEmpty()) {
                        current.number = 0;
                        erase(i, j);
                        tryOther(current.back);

                        i = newCurrent.i;
                        newI = i + 1;
                        j = newCurrent.j + 1;
                    } else {
                        for (Integer p : possibles) current.possibles.push(p);
                        current.number = (int) current.possibles.pop();

                        paint(current.number, i, j);

                        j++;
                        newI = i + 1;
                    }
                } else {
                    j++;
                    newI = i + 1;
                }
            }
            i = newI;
            j = 0;
        }
    }

    private void tryOther(Box box) {
        if (box.possibles.empty()) {
            if (box.back != null) {
                if (!box.initial) {
                    box.number = 0;
                    erase(box.i, box.j);
                }
                tryOther(box.back);
            }
            else newCurrent = null;
        } else {
            if (getTakenNumbers(box.i, box.j).contains(box.possibles.peek())) {
                box.possibles.pop();
                tryOther(box);
            }
            newCurrent = box;
            box.number = (int) box.possibles.pop();
            paint(box.number, box.i, box.j);
        }
    }

    private boolean add(int a, int i, int j) {
        if (getTakenNumbers(i, j).contains(a)) return false;
        sudoku[i][j].number = a;
        sudoku[i][j].initial = true;
        return true;
    }

    private void paint(int a, int i, int j) {
        JPanel panel = board[i][j];
        JTextField jTextField = (JTextField) board[i][j].getComponent(0);
        jTextField.setFont(new Font("Lucida Grande", Font.PLAIN, 25));
        jTextField.setForeground(Color.gray);

        ActionListener task = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("T");
            }
        };
        Timer timer = new Timer(3000 , task);
        timer.setRepeats(false);
        timer.start();

        jTextField.setText("" + a);
        panel.revalidate();
        panel.repaint();
    }

    private void erase(int i, int j) {
        JTextField jTextField = (JTextField) board[i][j].getComponent(0);
        jTextField.setText("");
        board[i][j].revalidate();
        board[i][j].repaint();
    }

    private HashSet<Integer> getTakenNumbers(int i, int j) {
        HashSet<Integer> takenNumbers = new HashSet<>();

        for (int k = 0; k < 9; k++) if (sudoku[i][k].number != 0 && k != j) takenNumbers.add(sudoku[i][k].number);
        for (int k = 0; k < 9; k++) if (sudoku[k][j].number != 0 && k != i) takenNumbers.add(sudoku[k][j].number);

        int bigBoxRow = 0;
        int bigBoxColumn = 0;
        if (i <= 5 && i > 2) bigBoxRow = 3;
        if (i <= 8 && i > 5) bigBoxRow = 6;
        if (j <= 5 && j > 2) bigBoxColumn = 3;
        if (j <= 8 && j > 5) bigBoxColumn = 6;

        for (int k = bigBoxRow; k < bigBoxRow + 3; k++) for (int l = bigBoxColumn; l <  bigBoxColumn + 3; l++) if (sudoku[k][l].number != 0 && k != i && l != j) takenNumbers.add(sudoku[k][l].number);

        return takenNumbers;
    }
}



import java.util.Stack;
public class Box {

    public int number = 0;
    public int i, j;
    public Stack possibles = new Stack();
    public Box back;
    public boolean initial = false;

    public Box(int i, int j) {
        this.i = i;
        this.j = j;
    }

    public Box() {}
}

Answers


You need to change the structure of your program. Specifically, you will want to get rid of the nested while loops within your solve method and instead replace it with a single Swing Timer. That Timer will repeat 9 * 9 times, and will have a counter field inside of it, and inside of it you will want to do the code that you currently do within your nested while loops.

It could look something like...

    // TIMER_DELAY could be 2000 if you want a 2 second delay
    new Timer(TIMER_DELAY, new ActionListener() {
        private int i = 0;
        private int j = 0; 

        @Override
        public void actionPerformed(ActionEvent e) {
            // MAX_ROWS is 9
            if (i == MAX_ROWS) {
                // we've iterated through the whole thing
                ((Timer) e.getSource()).stop();
            }
            if (j == MAX_ROWS) {
                // we've gone through all the columns
                i++;    // the next row
                j = 0;  // start at column 0
            }
            // TODO the code in the nested while loop.

            j++;
        }
    }).start();

For an example of a functioning program that places text in fields with a delay (but doesn't solve anything):

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class SudokuMCVE extends JPanel {
    private static final int CLUSTER = 3;
    private static final int MAX_ROWS = 9;
    private static final float FIELD_PTS = 32f;
    private static final int GAP = 3;
    private static final Color BG = Color.BLACK;
    private static final Color SOLVED_BG = Color.LIGHT_GRAY;
    public static final int TIMER_DELAY = 2 * 1000;
    private JTextField[][] fieldGrid = new JTextField[MAX_ROWS][MAX_ROWS];

    public SudokuMCVE() {
        JPanel mainPanel = new JPanel(new GridLayout(CLUSTER, CLUSTER));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
        mainPanel.setBackground(BG);
        JPanel[][] panels = new JPanel[CLUSTER][CLUSTER];
        for (int i = 0; i < panels.length; i++) {
            for (int j = 0; j < panels[i].length; j++) {
                panels[i][j] = new JPanel(new GridLayout(CLUSTER, CLUSTER, 1, 1));
                panels[i][j].setBackground(BG);
                panels[i][j].setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
                mainPanel.add(panels[i][j]);
            }
        }

        for (int row = 0; row < fieldGrid.length; row++) {
            for (int col = 0; col < fieldGrid[row].length; col++) {
                fieldGrid[row][col] = createField(row, col);
                int i = row / 3;
                int j = col / 3;
                panels[i][j].add(fieldGrid[row][col]);
            }
        }

        setLayout(new BorderLayout());
        add(mainPanel, BorderLayout.CENTER);
        add(new JButton(new SolveAction("Solve")), BorderLayout.PAGE_END);
    }

    private JTextField createField(int row, int col) {
        JTextField field = new JTextField(2);
        field.setHorizontalAlignment(JTextField.CENTER);
        field.setFont(field.getFont().deriveFont(Font.BOLD, FIELD_PTS));

        return field;
    }

    private class SolveAction extends AbstractAction {

        public SolveAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            new Timer(TIMER_DELAY, new ActionListener() {
                private int i = 0;
                private int j = 0;

                @Override
                public void actionPerformed(ActionEvent e) {
                    // MAX_ROWS is 9
                    if (i == MAX_ROWS) {
                        ((Timer) e.getSource()).stop();
                    }
                    if (j == MAX_ROWS) {
                        i++;
                        j = 0;
                    }
                    int number = (int) (MAX_ROWS * Math.random()) + 1;
                    fieldGrid[i][j].setBackground(SOLVED_BG);
                    fieldGrid[i][j].setText(String.valueOf(number));

                    j++;
                }
            }).start();
        }
    }

    private static void createAndShowGui() {
        SudokuMCVE mainPanel = new SudokuMCVE();

        JFrame frame = new JFrame("SudokuMCVE");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            createAndShowGui();
        });
    }
}

Need Your Help

Android WebView not loading local html string correctly

android html android-webview

I'm working on a project where I'm receiving some html string which I should show in WebView. The problem is that the html string contains only body part, without any tags like &lt;html&gt;, &lt;he...