Update transparancy of jFreeChart in real time wit

2019-07-16 01:17发布

问题:

I would like to ask a follow up question to this Q&A question (How can I update a JFreeChart's appearance after it's been made visible?) from @trashgod.

I am trying to give the user a JSlider to control the transparency of the graphs. I added a JSlider and a ChangeListener to repaint whenever there is a JSlider change.

slider.addChangeListener(new ChangeListener() {
    @Override
    public void stateChanged(ChangeEvent e) {
        chartPanel.repaint();
    }
});

However, the transparency is not updated until the user "clicks" the graph with the mouse. Why isn't this working?

Here's the SSCCE (with jFreeChart library):

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.AttributedString;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import javax.swing.JFrame;

import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicButtonListener;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.labels.PieToolTipGenerator;
import org.jfree.chart.plot.MultiplePiePlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.util.TableOrder;

/**
 * This example is similar to {@link MultiplePieChartDemo1}, but slices the
 * dataset by column rather than by row.
 */
public class MultiplePieChart extends JPanel {

    /**
     * Creates a sample dataset.
     *
     * @return A sample dataset.
     */
    private static CategoryDataset createDataset() {
        BigInteger topSources_quan = new BigInteger("100");
        BigInteger notTopSources_quan = new BigInteger("10");
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        dataset.addValue(topSources_quan, "Included Sources", "Sales Quantity");
        dataset.addValue(notTopSources_quan, "Not Included Sources", "Sales Quantity");

        BigDecimal topSources_total = new BigDecimal("1450.34");
        BigDecimal notTopSources_total = new BigDecimal("99.78");
        dataset.addValue(topSources_total, "Included Sources", "Sales Total");
        dataset.addValue(notTopSources_total, "Not Included Sources", "Sales Total");

        return dataset;
    }

    /**
     * Creates a sample chart with the given dataset.
     *
     * @param dataset  the dataset.
     *
     * @return A sample chart.
     */
    private static JFreeChart createChart(CategoryDataset dataset, Dimension size, String chartTitle, boolean includeLegend, final JSlider slider) {
        JFreeChart chart = ChartFactory.createMultiplePieChart(
            chartTitle,  // chart title
            dataset,               // dataset
            TableOrder.BY_COLUMN,
            includeLegend,                  // include legend
            true,
            false
        );
        chart.setBackgroundPaint(new GradientPaint(0, 0, new Color(240,240,240), 0, ((int)size.getHeight() / 2), new Color(200, 200, 200), true));
        TextTitle title = new TextTitle(chartTitle == null? "" : chartTitle, new Font("Tahoma", Font.BOLD, 16));
        chart.setTitle(title);

        if(includeLegend) {
            LegendTitle legend = chart.getLegend();
            legend.setBackgroundPaint(new Color(0, 0, 0, 0));
            legend.setItemFont(new Font("Tahoma", Font.PLAIN, 12));
            legend.setBorder(0, 0, 0, 0);
        }

        MultiplePiePlot plot = (MultiplePiePlot) chart.getPlot();
        JFreeChart stamper = plot.getPieChart();

        if(dataset.getColumnCount() == 1) {
            stamper.getTitle().setFont(new Font("Tahoma", Font.PLAIN, 0));
        } else {
            stamper.getTitle().setFont(new Font("Tahoma", Font.PLAIN, 12));
        }
        stamper.setBackgroundPaint(new GradientPaint(0, 0, new Color(240,240,240), 0, ((int)size.getHeight() / 2), new Color(200, 200, 200), true));

        plot.setOutlineStroke(null);
        plot.setBackgroundAlpha(.25f);

        JFreeChart subchart = plot.getPieChart();
        final PiePlot p = (PiePlot) subchart.getPlot();

        p.setBackgroundAlpha(0f);
        p.setOutlineStroke(null);
        p.setSectionPaint("Included Sources", new Color(0,100,255));
        p.setSectionPaint("Not Included Sources", new Color(255, 50, 50));
        p.setStartAngle(0);
        p.setLabelFont(new Font("Tahoma", Font.PLAIN, 9));
        p.setForegroundAlpha(.6f);

        if(slider != null) {
            slider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    float value = (float)slider.getValue() / (float)100;
                    System.out.println("Setting foreground alpha:" + value);
                    p.setForegroundAlpha(value);

                }
            });
        }
        p.setMaximumLabelWidth(0.20);

        p.setLabelGenerator(null);
        PieToolTipGenerator cttg = new PieToolTipGenerator() {

            @Override
            public String generateToolTip(PieDataset dataset, Comparable key) {
                Number val = dataset.getValue(key);
                if(val instanceof BigDecimal) {
                    DecimalFormat df = new DecimalFormat("$ #,##0.00");
                    return df.format(val.floatValue());
                } else if(val instanceof BigInteger) {
                    DecimalFormat df = new DecimalFormat("#,##0");
                    return df.format(val.intValue());
                } else
                    return val.toString();
            }


        };
        p.setToolTipGenerator(cttg);

        return chart;
    }

    /**
     * Creates a panel for the demo (used by SuperDemo.java).
     *
     * @return A panel.
     */
    public static JPanel createPanel(final JFrame parentFrame, CategoryDataset dataset, Dimension size, String chartTitle, boolean includeLegend, JSlider slider) {
        JFreeChart chart = createChart(dataset, size, chartTitle, includeLegend, slider);
        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setMouseWheelEnabled(true);
        if(size != null)
            chartPanel.setPreferredSize(size);

        if(slider != null) {
            slider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    chartPanel.repaint();
                }
            });
        }
        return chartPanel;
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JSlider slider = new JSlider();
        JFrame sliderFrame = new JFrame();
        sliderFrame.add(slider);
        sliderFrame.pack();
        JPanel panel = createPanel(frame, createDataset(), new Dimension(800, 500), "Sales", true, slider);
        frame.add(panel);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
        sliderFrame.setLocation(frame.getLocationOnScreen().x + frame.getWidth() - slider.getWidth(), frame.getLocationOnScreen().y + frame.getHeight());
        sliderFrame.setVisible(true);
        sliderFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    }

    static class CustomLabelGenerator implements PieSectionLabelGenerator {
        public String generateSectionLabel(PieDataset dataset, Comparable key) {

            if(dataset.getValue(key) instanceof BigInteger) {
                NumberFormat nf = new DecimalFormat("#,##0");
                return nf.format(((BigInteger)dataset.getValue(key)).intValue());
            } else if (dataset.getValue(key) instanceof BigDecimal) {
                NumberFormat nf = new DecimalFormat("$ #,##0.00");
                return nf.format(((BigDecimal)dataset.getValue(key)).doubleValue());
            } else {
                return dataset.getValue(key).toString();
            }
        }

        public AttributedString generateAttributedSectionLabel(
                PieDataset dataset, Comparable key) {
            return null;
        }

    }
}

回答1:

The critical issue is to notify the chart that you've modified "the pie chart that is used to draw the individual pie plots."

chart.fireChartChanged(); 

Simplified SSCCE:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.MultiplePiePlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.util.TableOrder;

/**
 * @see http://stackoverflow.com/a/17370398/230513
 * This example is similar to {@link MultiplePieChartDemo1}, but slices the
 * dataset by column rather than by row.
 */
public class MultiplePieChart extends JPanel {

    private JSlider slider = new JSlider();

    private CategoryDataset createDataset() {
        BigInteger topSources = new BigInteger("100");
        BigInteger notTopSources = new BigInteger("10");
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        dataset.addValue(topSources, "Included Sources", "Sales Quantity");
        dataset.addValue(notTopSources, "Not Included Sources", "Sales Quantity");

        BigDecimal topSourcesTotal = new BigDecimal("1450.34");
        BigDecimal notTopSourcesTotal = new BigDecimal("99.78");
        dataset.addValue(topSourcesTotal, "Included Sources", "Sales Total");
        dataset.addValue(notTopSourcesTotal, "Not Included Sources", "Sales Total");

        return dataset;
    }

    private JFreeChart createChart() {
        final JFreeChart chart = ChartFactory.createMultiplePieChart(
            "Sales", createDataset(), TableOrder.BY_COLUMN, true, true, false);
        MultiplePiePlot plot = (MultiplePiePlot) chart.getPlot();
        JFreeChart subchart = plot.getPieChart();
        final PiePlot p = (PiePlot) subchart.getPlot();
        p.setForegroundAlpha(0.5f);
        slider.setValue((int) (p.getForegroundAlpha() * 100));
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                float value = (float) slider.getValue() / (float) 100;
                p.setForegroundAlpha(value);
                chart.fireChartChanged();
            }
        });
        return chart;
    }

    public JPanel createChartPanel() {
        JFreeChart chart = createChart();
        final ChartPanel chartPanel = new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(800, 400);
            }
        };
        return chartPanel;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                MultiplePieChart mpc = new MultiplePieChart();
                JFrame frame = new JFrame();
                frame.add(mpc.createChartPanel());
                frame.add(mpc.slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}