1

When running my BubbleSort method I want a panel to update showing exactly what is happening, however the panel freezes until the method has fniished when using Thread.sleep.

I've heard about using Swing timers and I know how to use them but do not know how I could implement them 'mid-method' to pause and continue.

import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;

@SuppressWarnings("serial")
public class BubbleSort extends JPanel implements ActionListener
{  
    int swaps;
    int maxswaps;
    int[] array;
    JLabel arrayLabel;
    JLabel tipLabel = new JLabel();;

    private void bubbleSort(int[] array)
    {  
        maxswaps = (int) Math.pow(array.length,2);
        int tempnum = 0;  
        for(int i=0; i < array.length; i++)
        {  
            for(int num=1; num < (array.length-i); num++)
            {  
                if(array[num-1] > array[num])
                {  
                    tempnum = array[num-1];  
                    array[num-1] = array[num];  
                    array[num] = tempnum;  
                    tipLabel.setText("Swapping " + tempnum + " and " + array[num-1]);
                    arrayLabel.setText(Arrays.toString(array));
                    this.repaint();
                    swaps++;
                    try
                    {
                        Thread.sleep(500);
                        // this is where I want it to sleep         
                    }
                    catch (InterruptedException e)
                    {

                    }

                } 
            }  
        }  
    }  



    public static void moveToMiddle(JFrame frame)
    {
        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        int screenWidth = gd.getDisplayMode().getWidth();
        int screenHeight = gd.getDisplayMode().getHeight();
        frame.setBounds((screenWidth/2) - (frame.getWidth()/2), (screenHeight/2) - (frame.getHeight()/2), frame.getWidth(), frame.getHeight());
    }

    public static void main(String[] args)
    {  
        JPanel panel = new BubbleSort();
        panel.setLayout(null);
        panel.setBackground(Color.WHITE);
        panel.setFocusable(true);

        JFrame frame = new JFrame("Bubble Sort");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);        
        frame.setSize(800, 500);
        frame.setContentPane(panel);
        frame.setVisible(true);
        moveToMiddle(frame);
    }  

    public BubbleSort()
    {
        int arrayLength = 12;
        int maxNumber = 100;
        int minNumber = 0;

        array = new int[arrayLength]; 
        Random r = new Random();
        for(int i=0; i < array.length; i++)
        {
            int num = r.nextInt(maxNumber) + minNumber;
            array[i] = num;
        }

        arrayLabel = new JLabel(Arrays.toString(array));
        arrayLabel.setFont(new Font("Bahnschrift", Font.PLAIN, 35));
        arrayLabel.setBounds(100, 100, 600, 100);

        tipLabel.setBounds(100 , 250 , 600, 100);
        tipLabel.setFont(new Font("Bahnschrift", Font.PLAIN, 35));

        tipLabel.setVerticalAlignment(SwingConstants.CENTER);
        tipLabel.setHorizontalAlignment(SwingConstants.CENTER);

        arrayLabel.setVerticalAlignment(SwingConstants.CENTER);
        arrayLabel.setHorizontalAlignment(SwingConstants.CENTER);

        JButton startButton = new JButton("Start");
        startButton.addActionListener(this);
        startButton.setBounds(0,0,50,50);

        this.add(arrayLabel);   this.add(tipLabel); this.add(startButton);
    }

    @Override
    public void actionPerformed(ActionEvent arg0)
    {
        bubbleSort(array);
    }
}  

I want the panel to update whilst the method is running but using it remains blank until the method has finished.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Edd
  • 136
  • 9

3 Answers3

2

You are placing Thread.sleep(500); directly on UI thread. Instead create new thread for bubble sort. So if you put sleep on thread other than UI thread it will not freeze your UI.

Joy
  • 394
  • 2
  • 9
2

Change the ActionListner so the sorting is done on a separate thread:

@Override
public void actionPerformed(ActionEvent arg0)
{
    new Thread(()->bubbleSort(array)).start();
}

To update UI using Swing thread use : SwingUtilities.invokeLater(()->repaint()); instead of this.repaint();

c0der
  • 18,467
  • 6
  • 33
  • 65
  • Repaint marks the component as dirty, it doesn’t paints it directly, thus you can call it safely from any thread. – Snowy_1803 Feb 14 '19 at 20:35
  • @Snowy_1803 Thanks for the note. I see the `reapint` issue discussed [here](https://stackoverflow.com/questions/20905985/is-the-swing-repaint-method-still-safe-to-use-outside-the-edt-in-java-7) and the conclusion is that it is not thread safe. – c0der Feb 15 '19 at 04:55
1

You are doing the sorting AND the sleeping in EDT

   @Override
    public void actionPerformed(ActionEvent arg0)
    {
        bubbleSort(array);
    }

This is so called "GUI" thread and actionPerformed is done by it. EDT is responsible for drawing and responsiveness of the UI. You are chocking it, thus freeze. Use SwingWorker to do computation (and sleeping) and publish intermediate results so the GUI may reflect the changes.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99