zr870
zr870

Reputation: 1165

Java multiple repaint() calls getting ignored

For my algorithms class, our assignment is to perform mergesort on an array and show what is happening as an animation. I have the code (in theory) working, but when I call repaint() many times rapidly (to animate the array) they are getting ignored. No animation is being shown, just the final array. There should be one '-' after every ' * ' in the console but that is not the case, there are many ' * ' (as there should be) but only one '-'. The ' * ' denotes when the repaint method should be called, and the '-' denotes when it actually is called.

package a2;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class GraphicalSort extends JFrame implements ActionListener {
    int[] data = new int[200];
    int[] helper = new int[200];
    JPanel panel = new JPanel(); //Panel to hold graphical display of array
    JPanel buttonsPanel = new JPanel();
    JButton mButton = new JButton("Mergesort");
    JButton sButton = new JButton("Scramble");

    //Constants to scale the width and height
    int barWidth = 8;
    int barHeight = 1;

    public GraphicalSort() {
        setLayout(new BorderLayout());
        mButton.addActionListener(this);
        sButton.addActionListener(this);
        buttonsPanel.add(sButton);
        buttonsPanel.add(mButton);
        for (int i = 0; i < data.length; i++) {
            data[i] = (int) (500 * Math.random() + 1);
            helper[i] = data[i];
        }
        setSize(barWidth * data.length, barHeight * 500 + buttonsPanel.getHeight());
        panel = new ArrayPanel();
        add(buttonsPanel, BorderLayout.NORTH);
        add(panel, BorderLayout.CENTER);

        repaint();
        validate();
    }

    public static void main(String[] args) {
        GraphicalSort gs = new GraphicalSort();
        gs.setTitle("Graphical Sort");
        gs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gs.setLocationRelativeTo(null);
        gs.setResizable(false);
        gs.setVisible(true);
    }

    @SuppressWarnings("serial")
    class ArrayPanel extends JPanel {
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.BLACK);
            System.out.println("-"); //when repaint is actually called
            int xPos = 0;
            for (int i = 0; i < data.length; i++) {
                g.fillRect(xPos, (barHeight * 500) - (barHeight * data[i]), barWidth, barHeight * data[i]);
                xPos += barWidth;
            }   
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == mButton) {
            mergesort(0, data.length - 1);
        } else if (e.getSource() == sButton) {
            Random r = new Random();
            for (int i = 0; i < data.length; i++) {
                int index = r.nextInt(data.length);
                int temp = data[i];
                data[i] = data[index];
                data[index] = temp;
                panel.repaint();
            }
        }
    }

    private void mergesort(int low, int high)  {
        // Check if low is smaller then high, if not then the array is sorted
        if (low < high) {
            // Get the index of the element which is in the middle
            int middle = (low + high) / 2;
            // Sort the left side of the array
            mergesort(low, middle);
            // Sort the right side of the array
            mergesort(middle + 1, high);
            // Combine them both
            merge(low, middle, high);
            System.out.println("*"); //When the repaint should be called
            panel.repaint();
        }
    }

    private void merge(int low, int middle, int high) {

        // Copy both parts into the helper array
        for (int i = low; i <= high; i++) {
            helper[i] = data[i];
        }       

        int i = low;
        int j = middle + 1;
        int k = low;
        // Copy the smallest values from either the left or the right side back
        // to the original array
        while (i <= middle && j <= high) {
            if (helper[i] <= helper[j]) {
                data[k] = helper[i];
                i++;
            } else {
                data[k] = helper[j];
                j++;
            }
            k++;
        }
        // Copy the rest of the left side of the array into the target array
        while (i <= middle) {
            data[k] = helper[i];
            k++;
            i++;
        }

    }
}    

Upvotes: 1

Views: 934

Answers (2)

Java42
Java42

Reputation: 7706

To see animation during "scramble", you can replace your action preformed method with the following. Optionally add code to limit scrambling to one scramble at a time.

public void actionPerformed(final ActionEvent e) {
    if (e.getSource() == mButton) {
        mergesort(0, data.length - 1);
    } else if (e.getSource() == sButton) {
        new Thread(new Runnable() {
            public void run() {
                final Random r = new Random();
                for (int i = 0; i < data.length; i++) {
                    try {Thread.sleep(10);} 
                    catch (final InterruptedException e) 
                    { e.printStackTrace(); }
                    final int index = r.nextInt(data.length);
                    final int temp = data[i];
                    data[i] = data[index];
                    data[index] = temp;
                    panel.repaint();
                }
            }
        }).start();
    }
}

Upvotes: 0

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Your merge sort is happening quicker than you think, and the repaints are being called so close together that they are getting "stacked" and when this happens, yes, they can get ignored. Even if they aren't being ignored, again the speed of the code would result in no visible change to the GUI, and even if the process did take a lot of time, the GUI would be locked since the Swing event thread would be too busy to paint.

The solution is to slow down your code, but not with Thread.sleep(...) which again would tie up the Swing event thread if not called in a background thread, but rather with a Swing Timer. This would allow the code to progress in a slower step-wise fashion, but without tying up the Swing event thread.

Upvotes: 1

Related Questions