Search code examples
javaswingjfreechart

How to create "please wait" Swing dialog while component paints


Still relatively new to Swing but after a couple of hours of search, I couldn't find the answer online, hence this post (sorry if already answered and I overlooked it).

I'm using JFreeChart in a Swing application. Some charts are relatively heavy (180k data points) and the JFreeChart's ChartPanel needs ~6 seconds to do its first paintComponent().

Hence I would like to show a "Please wait" message in a dialog while the component paints (no need to show progress with a SwingWorker). I tried to override the paintComponent method but unfortunately the message never appears on screen (I guess the thread directly goes into painting the chart, without taking the time to paint the dialog).

My code looks like this:

public class CustomizedChartPanel extends ChartPanel{

private static final long serialVersionUID = 1L;
private JDialog dialog = null;
boolean isPainted = false;

public CustomizedChartPanel(JFreeChart chart) { super(chart); }

@Override
public void paintComponent(Graphics g) {
    //At first paint (which can be lengthy for large charts), show "please wait" message
    if (! isPainted){
        dialog = new JDialog();
        dialog.setUndecorated(true);
        JPanel panel = new JPanel();
        panel.add(new JLabel("Please wait"));
        dialog.add(panel);
        dialog.pack();
        GuiHelper.centerDialog(dialog); //Custom code to center the dialog on the screen
        dialog.setVisible(true);
        dialog.repaint();
    }

    super.paintComponent(g);

    if (! isPainted){
        isPainted = true;
        dialog.dispose();
            super.repaint();
        }
}
}

Any pointer on how to solve this / best practices would be very much appreciated!

Thanks, Thomas


UPDATE:

Thanks for the hints & debate: very helpful.

I started implementing the suggested solution with the invokeLater() as I'm fearing that the JLayer solution won't work since it's also running on the EDT.

Unfortunately I'm having a null pointer exception when the paintComponent() is called by the invokeLater().

My code looks like this:

    @Override
public void paintComponent(Graphics graph) {
    //At first paint (which can be lengthy for large charts), show "please wait" message
    if (! isPainted){
        isPainted = true;
        dialog = new JDialog();
        dialog.setUndecorated(true);
        JPanel panel = new JPanel();
        panel.add(new JLabel("Please wait"));
        panel.add(new JLabel("Please wait !!!!!!!!!!!!!!!!!!!!!!!!!!!!!"));
        dialog.add(panel);
        dialog.pack();
        GuiHelper.centerDialog(dialog); //Custom code to center the dialog on the screen
        dialog.setVisible(true);
        dialog.repaint();
        RunnableRepaintCaller r = new RunnableRepaintCaller(this, graph, dialog);
        SwingUtilities.invokeLater(r);
    }
    else super.paintComponent(graph); //NULL POINTER EXCEPTION HERE (invoked by runnable class)
}

And the runnable class is:

public class RunnableRepaintCaller implements Runnable{
private ChartPanel target;
private Graphics g;
private JDialog dialog;

public RunnableRepaintCaller(ChartPanel target, Graphics g, JDialog dialog){
    this.target = target;
    this.g = g;
    this.dialog = dialog;
}

@Override
public void run() {
    System.out.println(g);
    target.paintComponent(g);
    dialog.dispose();
}
}

Again, any pointer would be much appreciated !

Thomas


Solution

  • Here is an example and/but it uses SwingWorker. You should seriously consider using this because if somehow the OS invalidates your frame and the loading of your JFreeChart is done on the EDT (Event Dispatching Thread), then your GUI will look frozen.

    It also allows you to give better user feedback while you are processing the data. (sorry if the code is a bit long but most of the interesting code is in the initUI and the SwingWorker).

    Note: instead of the dialog, you could use the JLayer (if you use Java 7), but this was unnecessary in my example.

    The code is highly inspired from http://www.vogella.com/articles/JFreeChart/article.html

    /**
     * This code was directly taken from: http://www.vogella.com/articles/JFreeChart/article.html
     * All credits goes to him for this code.
     * 
     * Thanks to him.
     */
    
    import java.util.List;
    
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.SwingWorker;
    
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.plot.PiePlot3D;
    import org.jfree.data.general.DefaultPieDataset;
    import org.jfree.data.general.PieDataset;
    import org.jfree.util.Rotation;
    
    public class PieChart extends JFrame {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    initUI();
                }
            });
        }
    
        protected static void initUI() {
            // First we create the frame and make it visible
            final PieChart demo = new PieChart("Comparison");
            demo.setSize(500, 270);
            demo.setVisible(true);
            // Then we display the dialog on that frame
            final JDialog dialog = new JDialog(demo);
            dialog.setUndecorated(true);
            JPanel panel = new JPanel();
            final JLabel label = new JLabel("Please wait...");
            panel.add(label);
            dialog.add(panel);
            dialog.pack();
            // Public method to center the dialog after calling pack()
            dialog.setLocationRelativeTo(demo);
    
            // allowing the frame and the dialog to be displayed and, later, refreshed
            SwingWorker<JFreeChart, String> worker = new SwingWorker<JFreeChart, String>() {
    
                @Override
                protected JFreeChart doInBackground() throws Exception {
                    publish("Loading dataset");
                    // simulating the loading of the Dataset
                    try {
                        System.out.println("Loading dataset");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // This will create the dataset 
                    PieDataset dataset = demo.createDataset();
                    publish("Loading JFreeChart");
                    // simulating the loading of the JFreeChart
                    try {
                        System.out.println("Loading JFreeChart");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // based on the dataset we create the chart
                    JFreeChart chart = demo.createChart(dataset, "Which operating system are you using?");
                    // we put the chart into a panel
                    return chart;
                }
    
                @Override
                protected void process(List<String> chunks) {
                    label.setText(chunks.get(0));
                    dialog.pack();
                    dialog.setLocationRelativeTo(demo);
                    dialog.repaint();
                }
    
                @Override
                protected void done() {
                    try {
                        // Retrieve the created chart and put it in a ChartPanel
                        ChartPanel chartPanel = new ChartPanel(this.get());
                        // add it to our frame
                        demo.setContentPane(chartPanel);
                        // Dispose the dialog.
                        dialog.dispose();
                        // We revalidate to trigger the layout
                        demo.revalidate();
                        // Repaint, just to be sure
                        demo.repaint();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
            };
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                         worker.execute();
                }
            });
            dialog.setVisible(true);
        }
    
        public PieChart(String applicationTitle) {
            super(applicationTitle);
        }
    
        /** * Creates a sample dataset */
    
        private PieDataset createDataset() {
            DefaultPieDataset result = new DefaultPieDataset();
            result.setValue("Linux", 29);
            result.setValue("Mac", 20);
            result.setValue("Windows", 51);
            return result;
    
        }
    
        /** * Creates a chart */
    
        private JFreeChart createChart(PieDataset dataset, String title) {
    
            JFreeChart chart = ChartFactory.createPieChart3D(title, // chart title
                    dataset, // data
                    true, // include legend
                    true, false);
            PiePlot3D plot = (PiePlot3D) chart.getPlot();
            plot.setStartAngle(290);
            plot.setDirection(Rotation.CLOCKWISE);
            plot.setForegroundAlpha(0.5f);
            return chart;
    
        }
    
    }