I am trying to create a timer that will delay my GUI from sorting the bars representing random integers. This will make the sorting visible and in a style similar to those seen on YouTube. I read online that Swing Timers are recommended for GUI's so I went with one. If you can't already tell, this is my second project so if my code looks bad, please refrain from sending me death threats.
Anyways, when running my code and pressing the button that sorts the graph, the graph is immediately sorted despite having a timer programmed to delay frame.repaint() calls each time. Again, please go easy on me. This is my second time asking questions on StackOverflow, and I've seen vicious lashes at people who forgot a semicolon. Let me know if I programmed my Swing timer wrong, or if it's something else entirely
Thank you
Main Program
import java.util.Random;
import java.util.Scanner;
public class SortVisualizerShell {
public static int[] unsortedArray = arrayGenerator();
public static String requestedSort;
public static int[] arrayGenerator() {
int $N = 500;
int[] array = new int[$N];
Random rand = new Random();
for (int i = 0; i < $N; i ) {
int random_num = rand.nextInt($N);
array[i] = random_num;
}
return array;
}
public static void selectionSort(int[] array, int n) {
for (int i = 0; i < (n - 1); i ) {
int min = i;
for (int j = (i 1); j < n; j ) {
if (array[min] > array[j]) {
min = j;
}
}
int key = array[min];
while (min > i) {
array[min] = array[min - 1];
min = min - 1;
}
array[i] = key;
}
}
public static void bubbleSort(int[] array, int n) {
boolean swapped;
int i, j, temp;
for (i = 0; i < (n - 1); i ) {
swapped = false;
for (j = 0; j < ((n - 1) - 1); j ) {
if (array[j] > array[j 1]) {
temp = array[j];
array[j] = array[j 1];
array[j 1] = temp;
swapped = true;
}
}
if (swapped == false) {
break;
}
}
}
public static void insertionSort(int[] array) {
int n = array.length;
for (int i = 1; i < n; i ) {
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
array[j 1] = array[j];
j = j - 1;
}
array[j 1] = key;
}
}
public static void quickSort(int[] array, int left, int right) {
if (left < right) {
int pivot = partition(array, left, right);
quickSort(array, left, pivot - 1);
quickSort(array, pivot 1, right);
}
}
public static int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j ) {
if (array[j] < pivot) {
i ;
swap(array, i, j);
}
}
swap(array, i 1, high);
return (i 1);
}
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void mergeSort(int[] array, int len) {
if (len < 2) {
return;
}
int middle = len / 2;
int[] left_arr = new int[middle];
int[] right_arr = new int[len - middle];
int k = 0;
for (int i = 0; i < len; i ) {
if (i < middle) {
left_arr[i] = array[i];
}
else {
right_arr[k] = array[i];
k = k 1;
}
}
mergeSort(left_arr, middle);
mergeSort(right_arr, len - middle);
merge(left_arr, right_arr, array, middle, (len - middle));
}
public static void merge(int[] left_arr, int[] right_arr, int[] array, int left_side,
int right_side) {
int i = 0;
int left = 0;
int right = 0;
while (left < left_side && right < right_side) {
if (left_arr[left] < right_arr[right]) {
array[i ] = left_arr[left ];
}
else {
array[i ] = right_arr[right ];
}
}
while (left < left_side) {
array[i ] = left_arr[left ];
}
while (right < right_side) {
array[i ] = right_arr[right ];
}
}
public static void userInputFrame(String requestedSort, int[] array) {
if (requestedSort.equals("merge sort")) {
mergeSort(array, array.length);
}
else if (requestedSort.equals("quick sort")) {
quickSort(array, 0, array.length - 1);
}
else if (requestedSort.equals("insertion sort")) {
insertionSort(array);
}
else if (requestedSort.equals("bubble sort")) {
bubbleSort(array, array.length);
}
else if (requestedSort.equals("selection sort")) {
selectionSort(array, array.length);
}
}
public static void activateSort(String requestedSort, int[] unsortedArray) {
userInputFrame(requestedSort, unsortedArray);
}
public static int[] unsort(int[] sortedArray) {
int[] unsortedArray = arrayGenerator();
return unsortedArray;
}
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
System.out.println("Welcome to my sort visualizer");
System.out.println("");
System.out.println("Which sort would you like to see in action? ");
requestedSort = myScanner.nextLine();
requestedSort = requestedSort.toLowerCase();
Drawer.createGUI();
myScanner.close();
}
}
GUI Program
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Drawer extends JPanel {
public static SortVisualizerShell sort;
public static int[] y = SortVisualizerShell.unsortedArray;
public static String requestedSort;
public static Timer timer;
public static int delay = 1000;
public static void createGUI() {
JFrame frame = new JFrame();
JButton sortButton = new JButton("Sort");
JButton unsortButton = new JButton("Randomize Array");
JPanel panel = new JPanel();
Drawer draw = new Drawer();
requestedSort = SortVisualizerShell.requestedSort;
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.add(Box.createRigidArea(new Dimension(0, 5)));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(draw);
panel.add(sortButton);
panel.add(unsortButton);
frame.add(panel);
frame.getContentPane().setBackground(Color.BLACK);
frame.setVisible(true);
frame.pack();
frame.setSize(550, 650);
sortButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SortVisualizerShell.activateSort(requestedSort, y);
frame.repaint();
}
});
unsortButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
y = SortVisualizerShell.unsort(y);
frame.repaint();
}
});
Timer timer = new Timer(1000, sortButton.getAction());
timer.setInitialDelay(1000);
timer.setDelay(1000);
timer.setRepeats(true);
timer.start();
}
public void paintComponent(Graphics g) {
for (int i = 0; i < y.length; i ) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(i, 0, 5, y[i]);
g2d.setColor(Color.WHITE);
g2d.fillRect(i, 0, 5, y[i]);
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
}
});
}
}
CodePudding user response:
First, some points to note regarding the code in your question.
You shouldn't mix console application with GUI application.
I would add aJComboBox
for choosing the desired sort method.In the below line of your code,
sortButton.getAction()
returns null.
Timer timer = new Timer(1000, sortButton.getAction());
Hence your timer essentially does nothing.
- Usually the first line of an overridden
paintComponent
method should be
super.paintComponent(g);
Refer to Performing Custom Painting lesson of Creating a GUI With Swing trail in Oracle's Java tutorials.
Now I believe that you want to perform a repaint after each step of the chosen sort algorithm in order to manifest the visualization of the sort algorithm. In the below code, each sort algorithm is a separate class and each class implements a sortStep
method which is declared in interface IntsSort
.
I defined an enum for the different sorting algorithms.
After clicking on the Sort button, a [Swing] timer is launched that invokes the sortStep
method of the selected sorting algorithm every second. The timer is stopped when the sortStep
method returns true indicating that the sort has completed.
As an added visual aid, I set the cursor to a wait cursor while the sort is executing and display a JOptionPane informing the user that the sort has terminated.
Note that the below code is missing the classes that implement merge sort and quick sort. Hopefully you will be able to write those yourself based on the below code.
The IntsSort
interface:
/**
* Sorts array of {@code int}.
*/
public interface IntsSort {
/**
* Performs a single, sorting step.
*
* @return <tt>true</tt> if there are no more sorting steps required indicating that the sort
* has completed.
*/
public boolean sortStep();
}
The bubble sort implementation of IntsSort
:
public class BublSort implements IntsSort {
private int[] array;
private int i;
public BublSort(int[] arr) {
array = arr;
i = -1;
}
@Override
public boolean sortStep() {
boolean swapped = false;
i ;
if (i < array.length - 1) {
int temp;
for (int j = 0; j < ((array.length - 1) - 1); j ) {
if (array[j] > array[j 1]) {
temp = array[j];
array[j] = array[j 1];
array[j 1] = temp;
swapped = true;
}
}
}
return !swapped;
}
}
The insertion sort implementation of IntsSort
:
public class InsrtSrt implements IntsSort {
private int[] array;
private int i;
public InsrtSrt(int[] arr) {
array = arr;
i = -1;
}
public boolean sortStep() {
boolean stopSorting;
i ;
int n = array.length;
if (i < n) {
stopSorting = false;
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
array[j 1] = array[j];
j = j - 1;
}
array[j 1] = key;
}
else {
stopSorting = true;
}
return stopSorting;
}
}
The selection sort implementation of interface IntsSort
:
public class SlctnSrt implements IntsSort {
private int[] array;
private int i;
public SlctnSrt(int[] arr) {
array = arr;
i = -1;
}
@Override
public boolean sortStep() {
boolean stopSorting;
i ;
if (i < array.length - 1) {
stopSorting = false;
int min = i;
for (int j = (i 1); j < array.length; j ) {
if (array[min] > array[j]) {
min = j;
}
}
int key = array[min];
while (min > i) {
array[min] = array[min - 1];
min = min - 1;
}
array[i] = key;
}
else {
stopSorting = true;
}
return stopSorting;
}
}
The Swing GUI application:
Note that there is no ActionListener
for randomizeButton
since I am under the impression that you already know how to do that part.
Also, the ActionListener
for sortButton
uses a method reference.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
public class SortDraw extends JPanel implements Runnable {
private JComboBox<SortExec.SortMethod> sortAlgorithmsCombo;
private JFrame frame;
private Timer timer;
public SortDraw() {
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setBackground(Color.darkGray);
setOpaque(false);
}
@Override
public void run() {
createAndDisplayGui();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int[] y = SortExec.getArray();
if (y != null) {
for (int i = 0; i < y.length; i ) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(i, 0, 5, y[i]);
g2d.setColor(Color.WHITE);
g2d.fillRect(i, 0, 5, y[i]);
}
}
}
private void createAndDisplayGui() {
frame = new JFrame("Algos");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTopPanel(), BorderLayout.PAGE_START);
frame.add(this, BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.setSize(550, 650);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
JButton sortButton = new JButton("Sort");
sortButton.setMnemonic(KeyEvent.VK_S);
sortButton.addActionListener(this::launchSort);
buttonsPanel.add(sortButton);
JButton randomizeButton = new JButton("Randomize");
buttonsPanel.add(randomizeButton);
return buttonsPanel;
}
private JPanel createTopPanel() {
JPanel topPanel = new JPanel();
JLabel label = new JLabel("Sort Algorithm");
topPanel.add(label);
sortAlgorithmsCombo = new JComboBox<>(SortExec.SortMethod.values());
topPanel.add(sortAlgorithmsCombo);
return topPanel;
}
private void launchSort(ActionEvent event) {
SortExec.SortMethod requestedSort = (SortExec.SortMethod) sortAlgorithmsCombo.getSelectedItem();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
SortExec.initSort(requestedSort);
}
catch (RuntimeException xRuntime) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
throw xRuntime;
}
timer = new Timer(1000, this::performSort);
timer.setInitialDelay(0);
timer.start();
}
private void performSort(ActionEvent event) {
boolean stopSorting = SortExec.performSort();
repaint();
if (stopSorting) {
timer.stop();
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showMessageDialog(frame,
"Sorting completed.",
"Complete",
JOptionPane.INFORMATION_MESSAGE);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new SortDraw());
}
}
And finally, the SortExec
utility class (including the sort algorithms enum
):
import java.util.Random;
public class SortExec {
public enum SortMethod {
BUBBLE, INSERTION, MERGE, QUICK, SELECTION;
@Override
public String toString() {
String str;
switch (this) {
case BUBBLE:
str = "bubble sort";
break;
case INSERTION:
str = "insertion sort";
break;
case MERGE:
str = "merge sort";
break;
case QUICK:
str = "quick sort";
break;
case SELECTION:
str = "selection sort";
break;
default:
str = "unknown: " this.ordinal();
}
return str;
}
}
private static int[] arr;
private static IntsSort sorter;
public static int[] getArray() {
return arr;
}
public static void initSort(SortMethod sortMethod) {
arr = arrayGenerator();
sorter = null;
switch (sortMethod) {
case BUBBLE:
sorter = new BublSort(arr);
break;
case INSERTION:
sorter = new InsrtSrt(arr);
break;
case SELECTION:
sorter = new SlctnSrt(arr);
break;
default:
throw new RuntimeException(sortMethod.toString());
}
}
public static boolean performSort() {
return sorter.sortStep();
}
private static int[] arrayGenerator() {
int $N = 500;
int[] array = new int[$N];
Random rand = new Random();
for (int i = 0; i < $N; i ) {
int random_num = rand.nextInt($N);
array[i] = random_num;
}
return array;
}
}