0

I'm trying to change the values of bar chart depending on the selected item from the combobox list. The code below is a short example. The method for creating/updating the chart works well with button MouseListener or ActionPerformed, but it does not instantly change when I select an item from the combobox list.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
 
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
 
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.DefaultCategoryDataset;
 
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
 
public class MainTest extends JFrame {
     
    private JPanel contentPane;
    private JPanel panel;
    private JComboBox<String> comboBox;
    private static JPanel chartMainPanel;
     
    static String selectedItem = "Select";
     
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MainTest frame = new MainTest();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
     
    public MainTest() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 474, 409);
        contentPane = new JPanel();
        contentPane.setBackground(Color.WHITE);
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);
         
        panel = new JPanel();
        panel.setBounds(0, 0, 458, 370);
        contentPane.add(panel);
        panel.setLayout(null);
         
        comboBox = new JComboBox<String>();
        comboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (!comboBox.getSelectedItem().equals("Select")) {
                    selectedItem = comboBox.getSelectedItem().toString();
                }
                else {
                    selectedItem = "Select";
                }
                chart();
            }
        });
        comboBox.setFocusable(false);
        comboBox.setModel(new DefaultComboBoxModel<String>(new String[] {"Select", "First", "Second", "Third"}));
        comboBox.setBounds(10, 11, 146, 28);
        panel.add(comboBox);
         
        chartMainPanel = new JPanel();
        chartMainPanel.setBounds(10, 105, 438, 176);
        panel.add(chartMainPanel);
        chartMainPanel.setLayout(new BorderLayout(0, 0));
         
        JButton btnNewButton = new JButton("Refresh");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                chart();
            }
        });
        btnNewButton.setBounds(166, 11, 103, 28);
        panel.add(btnNewButton);
         
        chart();
    }
    public static void chart() {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        int a1 = 0;
        int a2 = 0;
        int a3 = 0;
         
        int b1 = 0;
        int b2 = 0;
        int b3 = 0;
         
        int c1 = 0;
        int c2 = 0;
        int c3 = 0;
         
        if (selectedItem.equals("First")) {
            a1 = 1;
            a2 = 1;
            a3 = 1;
             
            b1 = 1;
            b2 = 1;
            b3 = 1;
             
            c1 = 1;
            c2 = 1;
            c3 = 1;
        }
        else if (selectedItem.equals("Second")) {
            a1 = 2;
            a2 = 2;
            a3 = 2;
             
            b1 = 2;
            b2 = 2;
            b3 = 2;
             
            c1 = 2;
            c2 = 2;
            c3 = 2;
        }
        else if (selectedItem.equals("Third")) {
            a1 = 3;
            a2 = 3;
            a3 = 3;
             
            b1 = 3;
            b2 = 3;
            b3 = 3;
             
            c1 = 3;
            c2 = 3;
            c3 = 3;
        }
        else {
            a1 = 0;
            a2 = 0;
            a3 = 0;
             
            b1 = 0;
            b2 = 0;
            b3 = 0;
             
            c1 = 0;
            c2 = 0;
            c3 = 0;
        }
         
        dataset.setValue(a1, "part 1", "Q1");
        dataset.setValue(a2, "part 1", "Q2");
        dataset.setValue(a3, "part 1", "Q3");
         
        dataset.setValue(b1, "part 2", "Q1");
        dataset.setValue(b2, "part 2", "Q2");
        dataset.setValue(b3, "part 2", "Q3");
         
        dataset.setValue(c1, "part 3", "Q1");
        dataset.setValue(c2, "part 3", "Q2");
        dataset.setValue(c3, "part 3", "Q3");
         
        Font font = new Font("Tahoma", Font.PLAIN, 12);
        Font font2 = new Font("Tahoma", Font.PLAIN, 13);
         
        JFreeChart chart = ChartFactory.createBarChart("", "", "Rating", dataset, PlotOrientation.VERTICAL, false, false, false);
        CategoryPlot categoryPlot = chart.getCategoryPlot();
        categoryPlot.setBackgroundPaint(Color.WHITE);
        categoryPlot.setRangeGridlinePaint(Color.GRAY);
        categoryPlot.setOutlinePaint(Color.WHITE);
         
        categoryPlot.getDomainAxis().setLabelFont(font);
        categoryPlot.getDomainAxis().setTickLabelFont(font);
        categoryPlot.getDomainAxis().setTickLabelPaint(new Color(160, 163, 165));
         
        categoryPlot.getRangeAxis().setLabelFont(font2);
        categoryPlot.getRangeAxis().setTickLabelFont(font2);
        categoryPlot.getRangeAxis().setTickLabelPaint(new Color(160, 163, 165));
        categoryPlot.getRangeAxis().setRange(0, 3);
         
        ((BarRenderer) categoryPlot.getRenderer()).setBarPainter(new StandardBarPainter());
        BarRenderer renderer = (BarRenderer)chart.getCategoryPlot().getRenderer();
        renderer.setMaximumBarWidth(0.04);
        renderer.setSeriesPaint(0, new Color(102, 204, 255));
        renderer.setSeriesPaint(1, new Color(102, 204, 153));
         
        ChartPanel chartPanel = new ChartPanel(chart);
        chartMainPanel.add(chartPanel, BorderLayout.CENTER);
        chartPanel.setPopupMenu(null);
        chartPanel.removeAll();
        chartPanel.setMouseZoomable(false, false);
        chartPanel.revalidate();    
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Instead of re-creating the chart each time, simply update the data model; the listening chart view will update itself, for [example](https://stackoverflow.com/a/15311014/230513). If this is not a duplicate, please [edit] your question to include a shortened, [mre] that shows your revised approach. See also [*Initial Threads*](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod May 07 '22 at 12:35
  • This is a shortened example already I created to mimic the problem in my program , I don't understand how to update the dataset only, will it not require calling the chart method again? –  May 07 '22 at 12:45
  • My chart(); method updates the dataset depending on the selected item from combobox then revalidates the chart, the problem is that it doesn't revalidate after I selected an item from the combobox. –  May 07 '22 at 12:50
  • I tried creating a field for the dataset and creating an updateDataset() method but it won't update the chart unless I call the chart(); method again. –  May 07 '22 at 12:57
  • Your examples are difficult to follow, your coding structure is way too advance. –  May 07 '22 at 14:22
  • I'm sure your response is the right way of doing it but it's not really helpful for students who are trying to learn. A simple example would do and not some "look how good I am at programming" kind of example. I don't feel entitled to be given an example but I'm pretty sure you once were a beginner in programming. –  May 07 '22 at 14:40
  • 1
    *This is a shortened example already* No it isn't. Your question is about replacing a panel component on the frame. So JFreeChart should have nothing to do with the issue. If it works with one listener is should work with the other. Read the comment from your other cross posting: https://coderanch.com/t/751510/java/revalidate-update-jfreechart-JComboBox-actionperformed which explains how to create an [mre]. The only suggestion I can make is to wrap the ActionListener logic in a SwingUtilities.invoke later(...) in case there is some kind of timing issue between the two events. – camickr May 07 '22 at 15:31
  • 1
    Try eliminating the irrelevant, decorative elements in your example—fonts colors, etc.—and focus on the excerpted method in the example cited above; contrast the two approaches suggested [here](https://stackoverflow.com/a/50185046/230513); it looks like you want the second one, like [this](https://stackoverflow.com/a/6849654/230513). – trashgod May 07 '22 at 15:38
  • *but it's not really helpful for students who are trying to learn.* - yes it is helpful to understand the concept of "changing the data". This is how all Swing components work. The data is stored in a model. When the data in the model is changed, or a new model is created, the view will automatically update itself without creating any new view components. So if you understand this concept it will making your coding easier. This is how you learn programming, one concept at a time. The other example is not trying to show off coding skills, it is demonstrating a better way to use JFreeChart. – camickr May 07 '22 at 15:53
  • Thank you! it works now using this Thread th = new Thread() { public void run() { chart(); } };th.start(); –  May 07 '22 at 16:00
  • I apologize for my behavior there is no excuse. –  May 07 '22 at 16:14
  • 1
    Now you have [this problem](https://stackoverflow.com/a/7158505/230513); try this [example](https://stackoverflow.com/a/50867973/230513). – trashgod May 07 '22 at 16:16
  • 1
    No. Don't use a Thread. As I suggested in my comment, if there is some kind of timing issue you need to use SwingUtilities.invokeLater(). If you still have problems then post a proper [mre]. – camickr May 07 '22 at 20:17
  • I userstand thank you very much, sorry for being entitled and rude. –  May 08 '22 at 16:33
  • Instead of `addActionListener`, consider invoking `addItemListener` on the `JComboBox`. Your program appears to be correctly synchronized, but the `null` layout leads to multiple problems. Are you still interested in pursuing this question? – trashgod May 09 '22 at 23:11

1 Answers1

1

You don't need to replace the ChartPanel each time you want to see a new CategoryDataset; just the set the plot's new dataset like here. This example has a JComboBox<String> for selection and a Map<String, CategoryDataset> from which to choose:

combo.addItemListener((ItemEvent e) -> {
    var key = (String) e.getItem();
    plot.setDataset(map.get(key));
});

Bar Chart

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ItemEvent;
import java.util.HashMap;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;

public class ComboTest {

    private DefaultCategoryDataset createDataset(int i) {
        var set = new DefaultCategoryDataset();
        for (int j = 1; j < 4; j++) {
            set.addValue(i * 1, "Series 1", "Category " + j);
            set.addValue(i * 2, "Series 2", "Category " + j);
            set.addValue(i * 3, "Series 3", "Category " + j);
        }
        return set;
    }

    private void display() {
        var keys = new String[]{"Zero", "One", "Two", "Three", "Four"};
        var barChart = ChartFactory.createBarChart("", "Category", "Score",
            null, PlotOrientation.VERTICAL, true, true, false);
        var plot = (CategoryPlot) barChart.getPlot();
        var map = new HashMap<String, CategoryDataset>();
        var combo = new JComboBox<String>();
        for (int i = 0; i < keys.length; i++) {
            map.put(keys[i], createDataset(i));
            combo.addItem(keys[i]);
        }
        combo.addItemListener((ItemEvent e) -> {
            var key = (String) e.getItem();
            plot.setDataset(map.get(key));
        });
        combo.setSelectedIndex(keys.length / 2);
        var controlPanel = new JPanel();
        controlPanel.add(combo);
        var f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(controlPanel, BorderLayout.NORTH);
        f.add(new ChartPanel(barChart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(567, 345);
            }
        });
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new ComboTest()::display);
    }
}
Catalina Island
  • 7,027
  • 2
  • 23
  • 42