Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JFreeChart MouseListener doesn't resolve chart elements

Tags:

jfreechart

I've got stacked bar chart where I want to be able to select individual bars in the stack. But ChartMouseListener doesn't resolve ChartMouseEvent into corresponding ChartEntity. Here's the listener snippet :

    public void chartMouseClicked(ChartMouseEvent event){
        ChartEntity entity = event.getEntity();
        if(entity != null && (entity instanceof XYItemEntity) ){
            XYItemEntity item = (XYItemEntity)entity;
            renderer.select(item.getSeriesIndex(), item.getItem());
            return;
        }

        // deselect
        renderer.select(-1,-1);
    }

The problem is that event.getEntity() returns null when I am obviously clicking on some of the bars. Note that NOT all the bars fail. The further I go to the right end of the chart, the more obvious is the shift in coordinates. Snap shot below showing that selected bar actually appears when clicking outside of it. I am using JFreeChart within SWT composite. Can anyone confirm that this is a buggy behavior or is there a workaround?

enter image description here

Below is complete sscce, after you run it and click on bars - it will show up pinky. Then re-size the window and try to select bars - it will miss. And I think the miss is the function of the new size.

import java.awt.Color;
import java.awt.Paint;
import java.util.Random;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.xy.TableXYDataset;
import org.jfree.experimental.chart.swt.ChartComposite;

public class StackedChartSwt {
    private StackedRenderer renderer;
    private Color[] colors = new Color[]{
            new Color(230,240,255), 
            new Color(240,255,240), 
            new Color(255,255,255), 
            new Color(255,255,240), 
            new Color(255,240,240), 
            new Color(240,240,240)
    };

    public StackedChartSwt(){
        Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());

        JFreeChart chart = createStackedChart(createStackedDataset());
        ChartComposite chartComposite = new ChartComposite(shell, SWT.NONE, chart, false, false, false, false, false);
        chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
        chartComposite.setRangeZoomable(false);
        chartComposite.setMenu(null);
        chartComposite.addChartMouseListener(new ThisMouseListener());
        shell.setSize(800, 600);
        shell.open();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
              display.sleep();
            }
          }

          display.dispose();        
    }

    private JFreeChart createStackedChart(TableXYDataset tablexydataset) {
        DateAxis dateaxis = new DateAxis();
        dateaxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
        NumberAxis numberaxis = new NumberAxis("Event counts");
        renderer = new StackedRenderer();
        XYPlot plot = new XYPlot(tablexydataset, dateaxis, numberaxis, renderer);
        plot.setBackgroundPaint(Color.white);
        plot.setDomainGridlinePaint(Color.lightGray);
        plot.setDomainGridlinesVisible(true);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.lightGray);

        JFreeChart chart = new JFreeChart(null, plot);
        chart.setBackgroundPaint(Color.white);
        chart.setBorderVisible(false);
        chart.setBorderPaint(null);

        return chart;
    }

    class StackedRenderer extends StackedXYBarRenderer{
        int selectedRow=-1, selectedCol=-1;
        public StackedRenderer(){
            setDrawBarOutline(true);
            setBarPainter(new StandardXYBarPainter());
            setShadowVisible(false);
            setSeriesPaint(0, Color.blue);
            setMargin(0.2);
        }

        public void select(int row, int col){
            selectedRow = row;
            selectedCol = col;
            notifyListeners(new RendererChangeEvent(this));
        }

        @Override
        public Paint getItemPaint(final int row, final int col){
            if(row == selectedRow && col == selectedCol)
                return Color.pink;
            return colors[row];
        }
    } 

    class ThisMouseListener implements ChartMouseListener{
        public void chartMouseMoved(ChartMouseEvent event){
        }

        public void chartMouseClicked(ChartMouseEvent event){
            ChartEntity entity = event.getEntity();
            if(entity != null && (entity instanceof XYItemEntity) ){
                XYItemEntity item = (XYItemEntity)entity;
                renderer.select(item.getSeriesIndex(), item.getItem());
                return;
            }

            // deselect
            renderer.select(-1,-1);
        }
    }

    private TableXYDataset createStackedDataset(){
        Random random = new Random(0);
        TimeTableXYDataset ds = new TimeTableXYDataset();
        Day day = new Day();
        for( int i = 0; i < 24; i++ ){
            Hour hour = new Hour( i, day );
            ds.add(hour, random.nextInt( 20 ), "A");
            ds.add(hour, random.nextInt( 20 ), "B");
            ds.add(hour, random.nextInt( 20 ), "C");
            ds.add(hour, random.nextInt( 20 ), "D");
            ds.add(hour, random.nextInt( 20 ), "E");
            ds.add(hour, random.nextInt( 20 ), "F");
        }

        return ds;
    }

    public static void main(String[] args){
        new StackedChartSwt();
    }
}
like image 600
Dima Avatar asked Feb 23 '23 12:02

Dima


1 Answers

Your exemplary renderer has the correct geometry when run from Swing, as shown below. I'm unsure why things are awry with SWT, but this result may narrow the search.

enter image description here

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Paint;
import java.util.Random;
import javax.swing.JFrame;
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.DateTickMarkPosition;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.xy.TableXYDataset;

public class StackedChartSwing {

    private ChartPanel panel;
    private StackedRenderer renderer;
    private Color[] colors = new Color[]{
        new Color(230, 240, 255),
        new Color(240, 255, 240),
        new Color(255, 255, 255),
        new Color(255, 255, 240),
        new Color(255, 240, 240),
        new Color(240, 240, 240)
    };

    public StackedChartSwing() {
        JFreeChart chart = createStackedChart(createStackedDataset());
        panel = new ChartPanel(chart);
        panel.addChartMouseListener(new ThisMouseListener());
    }

    private JFreeChart createStackedChart(TableXYDataset tablexydataset) {
        DateAxis dateaxis = new DateAxis();
        dateaxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
        NumberAxis numberaxis = new NumberAxis("Event counts");
        renderer = new StackedRenderer();
        XYPlot plot = new XYPlot(tablexydataset, dateaxis, numberaxis, renderer);
        plot.setBackgroundPaint(Color.white);
        plot.setDomainGridlinePaint(Color.lightGray);
        plot.setDomainGridlinesVisible(true);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.lightGray);

        JFreeChart chart = new JFreeChart(null, plot);
        chart.setBackgroundPaint(Color.white);
        chart.setBorderVisible(false);
        chart.setBorderPaint(null);

        return chart;
    }

    class StackedRenderer extends StackedXYBarRenderer {

        int selectedRow = -1, selectedCol = -1;

        public StackedRenderer() {
            setDrawBarOutline(true);
            setBarPainter(new StandardXYBarPainter());
            setShadowVisible(false);
            setSeriesPaint(0, Color.blue);
            setMargin(0.2);
        }

        public void select(int row, int col) {
            selectedRow = row;
            selectedCol = col;
            notifyListeners(new RendererChangeEvent(this));
        }

        @Override
        public Paint getItemPaint(final int row, final int col) {
            if (row == selectedRow && col == selectedCol) {
                return Color.pink;
            }
            return colors[row];
        }
    }

    class ThisMouseListener implements ChartMouseListener {

        @Override
        public void chartMouseMoved(ChartMouseEvent event) {
        }

        @Override
        public void chartMouseClicked(ChartMouseEvent event) {
            ChartEntity entity = event.getEntity();
            if (entity != null && (entity instanceof XYItemEntity)) {
                XYItemEntity item = (XYItemEntity) entity;
                renderer.select(item.getSeriesIndex(), item.getItem());
                return;
            }
            // deselect
            renderer.select(-1, -1);
        }
    }

    private TableXYDataset createStackedDataset() {
        Random random = new Random(0);
        TimeTableXYDataset ds = new TimeTableXYDataset();
        Day day = new Day();
        for (int i = 0; i < 24; i++) {
            Hour hour = new Hour(i, day);
            ds.add(hour, random.nextInt(20), "A");
            ds.add(hour, random.nextInt(20), "B");
            ds.add(hour, random.nextInt(20), "C");
            ds.add(hour, random.nextInt(20), "D");
            ds.add(hour, random.nextInt(20), "E");
            ds.add(hour, random.nextInt(20), "F");
        }

        return ds;
    }

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panel);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new StackedChartSwing().display();
            }
        });
    }
}
like image 58
trashgod Avatar answered Apr 10 '23 00:04

trashgod