Search code examples
javaswingjfreechartcardlayout

Adding ChartPanel to CardLayout


I have a pretty basic GUI organized with a GridBagLayout. The most complex portion is the bottom where West is populated with ScrollPane and the right is a panel with a CardLayout that has multiple ChartPanels so I can switch between a few graphs.

My issue comes when I start the program.

enter image description here

The resizing issue goes away after I resize the frame in any direction. I have confirmed the chartpanel is the issue because not adding this to the CardLayout panel fixes it. I create a blank ChartPanel and populate it later after some action is taken but this is what I've done:

public class Tester {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Chipmunk: Variant Data Collection Tool");
        JPanel hotspotPanel = new JPanel(new CardLayout());
        ChartPanel subHotspotPanel = new ChartPanel(null);
        JPanel indelHotspotPanel = new JPanel(new BorderLayout());
        JTextPane resultPane = new JTextPane();
        JPanel main = new JPanel(new GridBagLayout());
        JPanel header = new JPanel(new BorderLayout());

        header.setBackground(Color.WHITE);
        frame.setLayout(new BorderLayout());
        frame.setMinimumSize(new Dimension(875, 600));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setLocation(dim.width/2-frame.getSize().width/2, dim.height/2-frame.getSize().height/2);
        resultPane.setOpaque(false);
        resultPane.setEditable(false);
        GridBagConstraints c = new GridBagConstraints();
        DocumentFilter filter = new UppercaseDocumentFilter();
        JTextField geneField = new JTextField(10);
        ((AbstractDocument) geneField.getDocument()).setDocumentFilter(filter);
        geneField.setMinimumSize(geneField.getPreferredSize());
        JTextField proEffField = new JTextField(10);
        proEffField.setMinimumSize(proEffField.getPreferredSize());
        String[] mutTypes = { "missense", "nonsense", "frameshift", "nonframeshift"};
        JComboBox<String> mutTypeComboBox = new JComboBox<String>(mutTypes);
        JButton saveResultsButton = new JButton("Save to TSV");
        JPanel glass = (JPanel) frame.getGlassPane();
        JButton clearButton = new JButton("Clear");
        JButton cosmicButton = new JButton("To COSMIC");
        JButton dataButton = new JButton("Show Data");
        dataButton.setEnabled(false);
        JButton goButton = new JButton("GO");

        c.weightx = 1.0;c.gridx = 0;c.gridy = 0;c.anchor = GridBagConstraints.EAST;c.ipadx=5;c.ipady=5;
        main.add(new JLabel("Gene: "), c);
        c.gridx = 1;c.gridy = 0;c.anchor = GridBagConstraints.WEST;
        main.add(geneField, c);
        c.gridx = 0;c.gridy = 1;c.anchor = GridBagConstraints.EAST;
        main.add(new JLabel("Protein Effect: "), c);
        c.gridx = 1;c.gridy = 1;c.anchor = GridBagConstraints.WEST;
        main.add(proEffField, c);
        c.gridx =0;c.gridy = 2;c.anchor = GridBagConstraints.EAST;
        main.add(new JLabel("Mutation Type: "), c);
        c.gridx =1;c.gridy = 2;c.anchor = GridBagConstraints.WEST;
        main.add(mutTypeComboBox, c);
        c.gridx =0;c.gridy = 3;c.anchor = GridBagConstraints.WEST;
        main.add(saveResultsButton, c);
        c.gridx = 0;c.gridy = 3;c.anchor = GridBagConstraints.EAST;
        main.add(goButton, c);
        c.gridx = 1;c.gridy = 3;c.anchor = GridBagConstraints.WEST;
        main.add(clearButton,c);
        c.gridx = 0;c.gridy = 3;c.anchor = GridBagConstraints.CENTER;
        main.add(dataButton,c);
        c.gridx = 1;c.gridy = 3;c.anchor = GridBagConstraints.EAST;
        main.add(cosmicButton,c);
        c.gridx = 0; c.gridy =4;c.gridwidth =1; c.weightx = 1.0;c.weighty = 1.0; c.fill = GridBagConstraints.BOTH;
        JScrollPane scrollPane = new JScrollPane(resultPane);
        main.add(scrollPane, c);
        c.gridx = 1; c.gridy =4;c.gridwidth = 1; c.weightx = 1.0;c.weighty = 1.0; c.fill = GridBagConstraints.BOTH;

        hotspotPanel.add(subHotspotPanel, "SUBPANEL");
        hotspotPanel.add(indelHotspotPanel, "INDELPANEL");
        hotspotPanel.add(new JPanel(), "BLANK");
        main.add(hotspotPanel, c);
        frame.add(header, BorderLayout.NORTH);
        frame.add(main, BorderLayout.CENTER);

        frame.pack();
        frame.setVisible(true);
    }

}

Solution

  • Using this example, it's clear that a ChartPanel works correctly in a CardLayout. The example below overrides getPreferredSize(), as shown here, to establish an initial size for the ChartPanel. The use of GridLayout on each card allows the chart to fill the panel as the enclosing frame is resized.

    image

    import java.awt.BorderLayout;
    import java.awt.CardLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.util.Random;
    import javax.swing.AbstractAction;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.data.general.DefaultPieDataset;
    
    /**
     * @see https://stackoverflow.com/a/36392696/230513
     * @see https://stackoverflow.com/a/36243395/230513
     */
    public class CardPanel extends JPanel {
    
        private static final Random r = new Random();
        private static final JPanel cards = new JPanel(new CardLayout());
        private final String name;
    
        public CardPanel(String name) {
            super(new GridLayout());
            this.name = name;
            DefaultPieDataset pieDataset = new DefaultPieDataset();
            pieDataset.setValue("One", r.nextInt(10) + 10);
            pieDataset.setValue("Two", r.nextInt(20) + 10);
            pieDataset.setValue("Three", r.nextInt(30) + 10);
            JFreeChart chart = ChartFactory.createPieChart3D(
                "3D Pie Chart", pieDataset, true, true, true);
            chart.setTitle(name);
            this.add(new ChartPanel(chart) {
    
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(500, (int)(500 * 0.62));
                }
            });
        }
    
        @Override
        public String toString() {
            return name;
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    create();
                }
            });
        }
    
        private static void create() {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            for (int i = 1; i < 9; i++) {
                CardPanel p = new CardPanel("Chart " + String.valueOf(i));
                cards.add(p, p.toString());
            }
            JPanel control = new JPanel();
            control.add(new JButton(new AbstractAction("\u22b2Prev") {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    CardLayout cl = (CardLayout) cards.getLayout();
                    cl.previous(cards);
                }
            }));
            control.add(new JButton(new AbstractAction("Next\u22b3") {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    CardLayout cl = (CardLayout) cards.getLayout();
                    cl.next(cards);
                }
            }));
            f.add(cards, BorderLayout.CENTER);
            f.add(control, BorderLayout.SOUTH);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    }