JPanel added but not displayed "in time"

I have a JFrame that displays JPanels depending on the click of MenuItem. It works, but now I need to call the method when the JPanel is added to the frame and it is displayed (because I use JFreeChart inside this panel, and I need to call chartPanel.repaint() when the JPanel is visible):

 this.getContentPane().add( myjpanel, BorderLayout.CENTER ); //this = JFrame this.validate(); myjpanel.methodCalledOnceDisplayed(); 

Does this seem normal? Is myjpanel really showing myjpanel ? It doesn't seem to be that way:

 public void methodCalledOnceDisplayed() { chartPanel.repaint() } 

This does not work ( chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0) throws an IndexOutOfBoundsException). This means that JPanel was not visible when calling repaint, I tested the following:

 public void methodCalledOnceDisplayed() { JOptionPane.showMessageDialog(null,"You should see myjpanel now"); chartPanel.repaint() } 

Now it works, I see myjpanel behind the warning, as expected, chartPanel is repainted and an Exception does not occur.

EDIT : SSCCE (requires jfreechart and jcommon: http://www.jfree.org/jfreechart/download.html )

  import java.awt.BorderLayout;
 import java.awt.EventQueue;
 import java.awt.Font;
 import javax.swing.JButton;
 import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.border.EmptyBorder;
 import org.jfree.chart.ChartMouseEvent;
 import org.jfree.chart.ChartMouseListener;
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.plot.CombinedDomainXYPlot;
 import org.jfree.chart.plot.PlotOrientation;
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.chart.ChartPanel;
 import org.jfree.data.time.TimeSeries;
 import org.jfree.data.time.TimeSeriesCollection;
 import org.jfree.data.xy.XYDataset;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;

 public class Window extends JFrame {
     private JPanel contentPane;

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

     public Window () {
         setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
         setBounds (100, 100, 700, 500);
         contentPane = new JPanel ();
         contentPane.setBorder (new EmptyBorder (5, 5, 5, 5));
         contentPane.setLayout (new BorderLayout (0, 0));
         setContentPane (contentPane);

         JButton clickme = new JButton ("Click me");
         clickme.addActionListener (new ActionListener () {
             public void actionPerformed (ActionEvent arg0) {
                 contentPane.removeAll ();
                 MyJPanel mypanel = new MyJPanel ();
                 contentPane.add (mypanel, BorderLayout.CENTER);
                 validate ();
                 mypanel.methodCalledOnceDisplayed ();
             }
         });
         contentPane.add (clickme, BorderLayout.NORTH);
         JPanel example = new JPanel ();
         example.add (new JLabel ("Example JPanel"));
         contentPane.add (example, BorderLayout.CENTER);
     }

 }

 class MyJPanel extends JPanel implements ChartMouseListener {
     private ChartPanel chartPanel;
     private JFreeChart chart;
     private XYPlot subplotTop;
     private XYPlot subplotBottom;
     private CombinedDomainXYPlot plot;

     public MyJPanel () {
         this.add (new JLabel ("This JPanel contains the chart"));
         createCombinedChart ();
         chartPanel = new ChartPanel (chart);
         chartPanel.addChartMouseListener (this);
         this.add (chartPanel);
     }

     private void createCombinedChart () {    
         plot = new CombinedDomainXYPlot ();
         plot.setGap (30);
         createSubplots ();
         plot.add (subplotTop, 4);
         plot.add (subplotBottom, 1);
         plot.setOrientation (PlotOrientation.VERTICAL);

         chart = new JFreeChart ("Title", new Font ("Arial", Font.BOLD, 20), plot, true);
     }

     private void createSubplots () {
         subplotTop = new XYPlot ();
         subplotBottom = new XYPlot ();

         subplotTop.setDataset (emptyDataset ("Empty 1"));
         subplotBottom.setDataset (emptyDataset ("Empty 2"));
     }

     private XYDataset emptyDataset (String title) {
         TimeSeries ts = new TimeSeries (title);
         TimeSeriesCollection tsc = new TimeSeriesCollection ();
         tsc.addSeries (ts);
         return tsc;
     }

     @Override
     public void chartMouseMoved (ChartMouseEvent e) {
         System.out.println ("Mouse moved!");
     }
     @Override
     public void chartMouseClicked (ChartMouseEvent arg0) {}

     public void methodCalledOnceDisplayed () {
         JOptionPane.showMessageDialog (null, "Magic!");  // try to comment this line and see the console
         chartPanel.repaint ();
         // now we can get chart areas
         this.chartPanel.getChartRenderingInfo (). getPlotInfo (). getSubplotInfo (0) .getDataArea ();
         this.chartPanel.getChartRenderingInfo (). getPlotInfo (). getSubplotInfo (1) .getDataArea ();
     }
 } 

See what happens with and without JOptionPane.

+3
source share
3 answers

Explaining why this is happening would be great.

You can get an idea of ​​the option below. Note

  • Swing GUI objects should only be created and processed into the event dispatch stream (EDT) for the reason suggested.

  • EDT continues to process events, as shown in the example, even when user interaction is limited to a modal dialog.

  • The repaint() call is not required when using the ChartPanel .

  • Prefer CardLayout or JTabbedPane to manually manipulate the container.

  • Instead of calling setPreferredSize() override getPreferredSize() , as discussed here .

Appendix: you deleted two lines ... that show the problem.

ChartRenderingInfo is dynamic data that does not exist until a chart is displayed. The modal dialog processes events, and the chart is updated in the background; without it, you can schedule your method by wrapping it in Runnable , suitable for invokeLater() :

 EventQueue.invokeLater(new Runnable() { @Override public void run() { myPanel.methodCalledOnceDisplayed(); } }); 

The best scheme is to access the ChartRenderingInfo in listeners, where you know that the data is valid, i.e. listeners implemented using ChartPanel .

image

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.border.EmptyBorder; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CombinedDomainXYPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.time.Day; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.XYDataset; /** * @see https://stackoverflow.com/a/14894894/230513 */ public class Test extends JFrame { private JPanel panel; public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { Test frame = new Test(); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public Test() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final MyJPanel myPanel = new MyJPanel(); panel = new JPanel() { @Override public Dimension getPreferredSize() { return myPanel.getPreferredSize(); } }; panel.setBorder(new EmptyBorder(5, 5, 5, 5)); panel.setLayout(new BorderLayout()); add(panel); myPanel.start(); JButton clickme = new JButton("Click me"); clickme.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { panel.removeAll(); panel.add(myPanel, BorderLayout.CENTER); validate(); EventQueue.invokeLater(new Runnable() { @Override public void run() { myPanel.methodCalledOnceDisplayed(); } }); } }); panel.add(clickme, BorderLayout.NORTH); JPanel example = new JPanel(); example.add(new JLabel("Example JPanel")); panel.add(example, BorderLayout.CENTER); } private static class MyJPanel extends JPanel { private static final Random r = new Random(); private ChartPanel chartPanel; private JFreeChart chart; private XYPlot subplotTop; private XYPlot subplotBottom; private CombinedDomainXYPlot plot; private Timer timer; private Day now = new Day(new Date()); public MyJPanel() { this.add(new JLabel("Chart panel")); createCombinedChart(); chartPanel = new ChartPanel(chart); this.add(chartPanel); timer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { update(subplotTop); update(subplotBottom); } }); timer.start(); } public void start() { timer.start(); } private void update(XYPlot plot) { TimeSeriesCollection t = (TimeSeriesCollection) plot.getDataset(); for (int i = 0; i < t.getSeriesCount(); i++) { TimeSeries s = t.getSeries(i); s.add(now, Math.abs(r.nextGaussian())); now = (Day) now.next(); } } private void createCombinedChart() { plot = new CombinedDomainXYPlot(); plot.setGap(30); createSubplots(); plot.add(subplotTop, 4); plot.add(subplotBottom, 1); plot.setOrientation(PlotOrientation.VERTICAL); chart = new JFreeChart("Title", JFreeChart.DEFAULT_TITLE_FONT, plot, true); plot.setDomainAxis(new DateAxis("Domain")); } private void createSubplots() { subplotTop = new XYPlot(); subplotBottom = new XYPlot(); subplotTop.setDataset(emptyDataset("Set 1")); subplotTop.setRenderer(new XYLineAndShapeRenderer()); subplotTop.setRangeAxis(new NumberAxis("Range")); subplotBottom.setDataset(emptyDataset("Set 2")); subplotBottom.setRenderer(new XYLineAndShapeRenderer()); subplotBottom.setRangeAxis(new NumberAxis("Range")); } private XYDataset emptyDataset(String title) { TimeSeriesCollection tsc = new TimeSeriesCollection(); TimeSeries ts = new TimeSeries(title); tsc.addSeries(ts); return tsc; } public void methodCalledOnceDisplayed() { PlotRenderingInfo plotInfo = this.chartPanel.getChartRenderingInfo().getPlotInfo(); for (int i = 0; i < plotInfo.getSubplotCount(); i++) { System.out.println(plotInfo.getSubplotInfo(i).getDataArea()); } JOptionPane.showMessageDialog(null, "Magic!"); } } } 

Addendum: Another iteration to illustrate the ChartMouseListener and clear some loose ends.

 import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import org.jfree.chart.ChartMouseEvent; import org.jfree.chart.ChartMouseListener; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.plot.CombinedDomainXYPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.time.Day; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.XYDataset; /** * @see https://stackoverflow.com/a/14894894/230513 */ public class Test { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { Test t = new Test(); } }); } public Test() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final MyJPanel myPanel = new MyJPanel(); f.add(myPanel, BorderLayout.CENTER); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); myPanel.start(); } private static class MyJPanel extends JPanel { private static final Random r = new Random(); private ChartPanel chartPanel; private JFreeChart chart; private XYPlot subplotTop; private XYPlot subplotBottom; private CombinedDomainXYPlot plot; private Timer timer; private Day now = new Day(new Date()); public MyJPanel() { createCombinedChart(); chartPanel = new ChartPanel(chart); this.add(chartPanel); timer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { update(subplotTop); update(subplotBottom); now = (Day) now.next(); } }); chartPanel.addChartMouseListener(new ChartMouseListener() { @Override public void chartMouseClicked(ChartMouseEvent e) { final ChartEntity entity = e.getEntity(); System.out.println(entity + " " + entity.getArea()); } @Override public void chartMouseMoved(ChartMouseEvent e) { } }); } public void start() { timer.start(); } private void update(XYPlot plot) { TimeSeriesCollection t = (TimeSeriesCollection) plot.getDataset(); for (int i = 0; i < t.getSeriesCount(); i++) { TimeSeries s = t.getSeries(i); s.add(now, Math.abs(r.nextGaussian())); } } private void createCombinedChart() { plot = new CombinedDomainXYPlot(); createSubplots(); plot.add(subplotTop, 4); plot.add(subplotBottom, 1); plot.setOrientation(PlotOrientation.VERTICAL); chart = new JFreeChart("Title", JFreeChart.DEFAULT_TITLE_FONT, plot, true); plot.setDomainAxis(new DateAxis("Domain")); } private void createSubplots() { subplotTop = new XYPlot(); subplotBottom = new XYPlot(); subplotTop.setDataset(emptyDataset("Set 1")); subplotTop.setRenderer(new XYLineAndShapeRenderer()); subplotTop.setRangeAxis(new NumberAxis("Range")); subplotBottom.setDataset(emptyDataset("Set 2")); subplotBottom.setRenderer(new XYLineAndShapeRenderer()); subplotBottom.setRangeAxis(new NumberAxis("Range")); } private XYDataset emptyDataset(String title) { TimeSeriesCollection tsc = new TimeSeriesCollection(); TimeSeries ts = new TimeSeries(title); tsc.addSeries(ts); return tsc; } } } 
+4
source

JPanel is not displayed until Thread.sleep is complete. What for? What am I doing wrong?

  • do not block Event Dispatch Thread , T hread.sleep(int) block EDT, show the Swing GUI before this delay expires, and all changes made during Thread.sleep(int) will not be visible on the screen, use Swing Timer , otherwise case all changes in the Swing GUI must be wrapped in invokeLater()

  • Swing is single-threaded, and all updates to the visible GUI (or from invokeLater ) must be done via EDT, more in Concurency in Swing

  • for better help, publish SSCCE soon , short, executable, compiled

+4
source

It seems to be resolved thanks to invokeLater:

  public void methodCalledOnceDisplayed () {
         SwingUtilities.invokeLater (new Runnable () {
             @Override
             public void run () {
                 chartPanel.repaint ();
                 chartPanel.getChartRenderingInfo (). getPlotInfo (). getSubplotInfo (0) .getDataArea ();
                 chartPanel.getChartRenderingInfo (). getPlotInfo (). getSubplotInfo (1) .getDataArea ();
             }
         });
     } 

Now there is no IndexOutOfBoundsException: Index: 0, Size: 0

an explanation of why this would be great

+1
source

All Articles