JfreeChart限制显示的图例项数量

发布于 2024-12-26 08:07:57 字数 3246 浏览 1 评论 0原文

我负责维护一个可以使用 JFreeChart 绘制图表的应用程序。 该应用程序是用 Eclipse-RCP 和 SWT 编写的,并使用 ChartComposite 来显示图表。

ChartComposite 已被部分覆盖,以便根据选择自定义上下文菜单:

@Override
    public void createPartControl(Composite parent) {
    super.createPartControl(parent);

    chart = createChart(timeSeriesDataset);

    chartComposite = new MyChartComposite(this, parent, SWT.NONE, chart, true);
    chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

    selectionProvider = new GenericObjectSelectionProvider();
    getSite().setSelectionProvider(selectionProvider);

    // add information to the status line:
    selectionProvider.addSelectionChangedListener(statusLineListener);

    addDropSupport();// add D'n D support for dropping TimeSeries 

}


protected JFreeChart createChart(TimeSeriesCollection ptimeSeriesDataset) {

        JFreeChart vChart = ChartFactory.createTimeSeriesChart(null, "time", "values", ptimeSeriesDataset, true,
                false, false);
        vChart.setBackgroundPaint(Color.white);

        XYPlot plot = vChart.getXYPlot();
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);
        // plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
        plot.setDomainCrosshairVisible(true);
        plot.setRangeCrosshairVisible(true);

        plot.setRenderer(new /*OptimisedXYLineAndShapeRenderer()*/ StandardXYItemRendererFast());
        XYItemRenderer renderer = plot.getRenderer();
        renderer.setBaseToolTipGenerator(new MyXYSeriesToolTipGenerator());
        renderer.setBaseItemLabelGenerator(new MyXYSeriesItemLabelGenerator());
        renderer.setLegendItemLabelGenerator(new MyXYSeriesLegendItemLabelGenerator());
        if (renderer instanceof XYLineAndShapeRenderer) {
            XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) renderer;
            r.setBaseShapesVisible(false);
            r.setBaseShapesFilled(true);
        }

        SimpleDateFormat dateFormat = getDateFormatAbscissa();
        if (dateFormat != null){
            DateAxis axis = (DateAxis) plot.getDomainAxis();
            axis.setDateFormatOverride(dateFormat);
        }   

        return vChart;
    }

我的问题是,当向图表(TimeSeriesChart)添加太多变量时,标题会占用太多空间,并且图形会从视图中消失:

包含 2 个系列的复合图表

ChartComposite 许多系列

我尝试创建一个 ScrollComposite 在 ChartComposite 中滚动,结果好一点;但它只能在图表再次消失之前在标题中添加更多项目:

ScrolledComposite scrollableChart = new ScrolledComposite(parent, SWT.BORDER|SWT.V_SCROLL);

chartComposite = new MyChartComposite(this, scrollableChart, SWT.NONE, chart, true);
//chartComposite = new MyChartComposite(this, parent, SWT.NONE, chart, true);
//chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

scrollableChart.setContent(chartComposite);
scrollableChart.setExpandVertical(true);
scrollableChart.setExpandHorizontal(true);
scrollableChart.setMinSize(ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH, ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH);

我的问题是:如何向 ChartComposite 提供真正的滚动条,以便在图表上绘制许多系列时保留图表?

I'm in charge of maintaining an application which can draw graphs using JFreeChart.
The application is written in Eclipse-RCP and SWT and use a ChartComposite to display the charts.

The ChartComposite has been partially overridden in order to customize contextual menus depending on the selection:

@Override
    public void createPartControl(Composite parent) {
    super.createPartControl(parent);

    chart = createChart(timeSeriesDataset);

    chartComposite = new MyChartComposite(this, parent, SWT.NONE, chart, true);
    chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

    selectionProvider = new GenericObjectSelectionProvider();
    getSite().setSelectionProvider(selectionProvider);

    // add information to the status line:
    selectionProvider.addSelectionChangedListener(statusLineListener);

    addDropSupport();// add D'n D support for dropping TimeSeries 

}


protected JFreeChart createChart(TimeSeriesCollection ptimeSeriesDataset) {

        JFreeChart vChart = ChartFactory.createTimeSeriesChart(null, "time", "values", ptimeSeriesDataset, true,
                false, false);
        vChart.setBackgroundPaint(Color.white);

        XYPlot plot = vChart.getXYPlot();
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);
        // plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
        plot.setDomainCrosshairVisible(true);
        plot.setRangeCrosshairVisible(true);

        plot.setRenderer(new /*OptimisedXYLineAndShapeRenderer()*/ StandardXYItemRendererFast());
        XYItemRenderer renderer = plot.getRenderer();
        renderer.setBaseToolTipGenerator(new MyXYSeriesToolTipGenerator());
        renderer.setBaseItemLabelGenerator(new MyXYSeriesItemLabelGenerator());
        renderer.setLegendItemLabelGenerator(new MyXYSeriesLegendItemLabelGenerator());
        if (renderer instanceof XYLineAndShapeRenderer) {
            XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) renderer;
            r.setBaseShapesVisible(false);
            r.setBaseShapesFilled(true);
        }

        SimpleDateFormat dateFormat = getDateFormatAbscissa();
        if (dateFormat != null){
            DateAxis axis = (DateAxis) plot.getDomainAxis();
            axis.setDateFormatOverride(dateFormat);
        }   

        return vChart;
    }

My problem is that when too many variables are added to the chart (a TimeSeriesChart) the caption takes too much space and the graph disappears from the view:

ChartComposite with 2 series

ChartComposite many series

I tried to create a ScrollComposite to scroll in the ChartComposite and the result is a little better; but it only makes it possible to add more items in the caption before the graph disappears again:

ScrolledComposite scrollableChart = new ScrolledComposite(parent, SWT.BORDER|SWT.V_SCROLL);

chartComposite = new MyChartComposite(this, scrollableChart, SWT.NONE, chart, true);
//chartComposite = new MyChartComposite(this, parent, SWT.NONE, chart, true);
//chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

scrollableChart.setContent(chartComposite);
scrollableChart.setExpandVertical(true);
scrollableChart.setExpandHorizontal(true);
scrollableChart.setMinSize(ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH, ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH);

My question is: How to provide a real scrollbar to the ChartComposite in order to keep the graph when many series are plotted on the graph?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜宝宝 2025-01-02 08:07:57

我能够通过使用 FormData 使用 ChartComosite 和 Slider 对象将滑块同步到 SWT 中的 XYSeries。每次我移动滑块时,我都会捕获该事件并根据我的需要自行更新图表。

我的用例可能与您的不同,但值得此处查看我的答案。

如果您对该答案中描述的我的实施有疑问,请随时询问详细信息

I was able to sync a slider to the XYSeries in SWT using a ChartComosite and Slider objects through the use of FormData. And every time I move the slider I capture that event and update the chart myself according to my needs.

My use case may be different than yours, but it's worth to take a look to my answer here.

If you have questions regarding my implementation, described in that answer, feel free to ask for details

小镇女孩 2025-01-02 08:07:57

经过几次不成功的尝试后,我决定限制图表上显示的 LegendItem 数量。

为了更改在 LegendTitle 中显示的图例项,我使用了滑块。最困难的部分是在使用滑块时重新创建新的 LegendTitle;我不确定我的解决方案是最佳的还是优雅的,但至少它是有效的。

Result

为了实现这一点,我需要监听系列创建 (ChartChangeEventType.DATASET_UPDATED) 来刷新LegendTitle 并设置滑块值。

所以这是代码:

public class MyExampleChartComposite extends ChartComposite {
    // snip
    /**
     * A slider to choose the legend items to display
     */
     private Slider legendSlider;
     /**
     * Number of legend items to display on the chart
     */
     private final static int NUMBER_OF_LEGENDITEMS_TO_DISPLAY = 10;

     private void createPartControl(Composite parent, int style) {
         JFreeChart chart = createChart();
         setChart(chart);
         legendSlider = new Slider(parent, SWT.NONE|SWT.H_SCROLL);
         legendSlider.addSelectionListener(new SelectionListener() {
             @Override
             public void widgetSelected(SelectionEvent arg0) {
                      refreshLegend();
             }
         });

     private JFreeChart createChart() {
         chart.addChangeListener(new ChartChangeListener() {    
             @Override
             public void chartChanged(ChartChangeEvent e) {
                  if (e.getType().equals(ChartChangeEventType.DATASET_UPDATED)) {
                      refreshLegend();
                  }
             }
         });
    }

    /**
     * Refresh the the LegendItems and the slider,
     * according to slider selection.
     */
    public void refreshLegend() {
        // Display LegendItems according to the selection
        // display the 10 nearest legend item (current selected element included)
        int begin = legendSlider.getSelection() - (NUMBER_OF_LEGENDITEMS_TO_DISPLAY/2);
        int end = legendSlider.getSelection() + (NUMBER_OF_LEGENDITEMS_TO_DISPLAY/2 -1);
        int seriesEndIndex = Math.max(getSeriesCount()-1, 0);
        // if begin is less than 0
        // set it to 0, and increase end to display 10 items
        if (begin < 0) {
            begin = 0; 
            end = NUMBER_OF_LEGENDITEMS_TO_DISPLAY - 1;
        }
        // if end is greater than the number of series plotted
        // set it to the max possible value and increase begin to
        // display 10 items
        if  (end > seriesEndIndex) {
            end = seriesEndIndex;  
            begin = seriesEndIndex - (NUMBER_OF_LEGENDITEMS_TO_DISPLAY - 1);
        }
        end = Math.min(seriesEndIndex, end);
        begin = Math.max(begin, 0);
        // Refresh only if begin != end
        if (end != begin) {
            refreshLegendItems(begin, end);
            refreshLegendSlider();
        } else {
            // in this case no more series are plotted on the chart
            // clear legend
            getChart().clearSubtitles();
        }
    }
    /**
     * Refresh the LegendTitle.
     * Display only LegendItems between beginIndex and toIndex,
     * to preserve space for the chart.
     * @param beginIndex index of the {@link LegendItemCollection} used as the beginning of the new {@link LegendTitle}
     * @param endIndex index of the {@link LegendItemCollection} used as the end of the new {@link LegendTitle}
     */
    private void refreshLegendItems(int beginIndex, int endIndex) {
        // Last 10 items
        final LegendItemCollection result = new LegendItemCollection();
        // get the renderer to retrieve legend items
        XYPlot plot = getChart().getXYPlot();
        XYItemRenderer renderer = plot.getRenderer();
        // Number of series displayed on the chart 
        // Construct the legend
        for (int i = beginIndex; i <= endIndex; i++) {
            LegendItem item = renderer.getLegendItem(0, i);
            result.add(item);
        }
        // Because the only way to create a new LegendTitle is to
        // create a LegendItemSource first
        LegendItemSource source = new LegendItemSource() {
            LegendItemCollection lic = new LegendItemCollection();
            {lic.addAll(result);}
            public LegendItemCollection getLegendItems() {  
                return lic;
            }
        };
        // clear previous legend title
        getChart().clearSubtitles();
        // Create the new LegendTitle and set its position
        LegendTitle legend = new LegendTitle(source);
        legend.setHorizontalAlignment(HorizontalAlignment.CENTER);
        legend.setVerticalAlignment(VerticalAlignment.CENTER);
        legend.setPosition(RectangleEdge.BOTTOM);
        legend.setBorder(1.0, 1.0, 1.0, 1.0);
        legend.setVisible(true);
        // Add the LegendTitle to the graph
        getChart().addLegend(legend);
    }

    /**
     * Set values of the slider according to the number of series
     * plotted on the graph
     */
    private void refreshLegendSlider() {
        int max = getSeriesCount() -1;
        int selection = Math.min(legendSlider.getSelection(), max);
        legendSlider.setValues(selection, 0, max, 1, 1, 1);
    }
}

After several unsuccessful tries, I decided to limit the number of LegendItem shown on the chart.

To change the legend items to display in the LegendTitle I used a slider. The most difficult part was to recreate the a new LegendTitle when using the slider; I am not sure that my solution is optimal or elegant but at least it is working.

Result

To make this possible I needed to listen to series creation (ChartChangeEventType.DATASET_UPDATED) to refresh the LegendTitle and set slider values.

So here is the code:

public class MyExampleChartComposite extends ChartComposite {
    // snip
    /**
     * A slider to choose the legend items to display
     */
     private Slider legendSlider;
     /**
     * Number of legend items to display on the chart
     */
     private final static int NUMBER_OF_LEGENDITEMS_TO_DISPLAY = 10;

     private void createPartControl(Composite parent, int style) {
         JFreeChart chart = createChart();
         setChart(chart);
         legendSlider = new Slider(parent, SWT.NONE|SWT.H_SCROLL);
         legendSlider.addSelectionListener(new SelectionListener() {
             @Override
             public void widgetSelected(SelectionEvent arg0) {
                      refreshLegend();
             }
         });

     private JFreeChart createChart() {
         chart.addChangeListener(new ChartChangeListener() {    
             @Override
             public void chartChanged(ChartChangeEvent e) {
                  if (e.getType().equals(ChartChangeEventType.DATASET_UPDATED)) {
                      refreshLegend();
                  }
             }
         });
    }

    /**
     * Refresh the the LegendItems and the slider,
     * according to slider selection.
     */
    public void refreshLegend() {
        // Display LegendItems according to the selection
        // display the 10 nearest legend item (current selected element included)
        int begin = legendSlider.getSelection() - (NUMBER_OF_LEGENDITEMS_TO_DISPLAY/2);
        int end = legendSlider.getSelection() + (NUMBER_OF_LEGENDITEMS_TO_DISPLAY/2 -1);
        int seriesEndIndex = Math.max(getSeriesCount()-1, 0);
        // if begin is less than 0
        // set it to 0, and increase end to display 10 items
        if (begin < 0) {
            begin = 0; 
            end = NUMBER_OF_LEGENDITEMS_TO_DISPLAY - 1;
        }
        // if end is greater than the number of series plotted
        // set it to the max possible value and increase begin to
        // display 10 items
        if  (end > seriesEndIndex) {
            end = seriesEndIndex;  
            begin = seriesEndIndex - (NUMBER_OF_LEGENDITEMS_TO_DISPLAY - 1);
        }
        end = Math.min(seriesEndIndex, end);
        begin = Math.max(begin, 0);
        // Refresh only if begin != end
        if (end != begin) {
            refreshLegendItems(begin, end);
            refreshLegendSlider();
        } else {
            // in this case no more series are plotted on the chart
            // clear legend
            getChart().clearSubtitles();
        }
    }
    /**
     * Refresh the LegendTitle.
     * Display only LegendItems between beginIndex and toIndex,
     * to preserve space for the chart.
     * @param beginIndex index of the {@link LegendItemCollection} used as the beginning of the new {@link LegendTitle}
     * @param endIndex index of the {@link LegendItemCollection} used as the end of the new {@link LegendTitle}
     */
    private void refreshLegendItems(int beginIndex, int endIndex) {
        // Last 10 items
        final LegendItemCollection result = new LegendItemCollection();
        // get the renderer to retrieve legend items
        XYPlot plot = getChart().getXYPlot();
        XYItemRenderer renderer = plot.getRenderer();
        // Number of series displayed on the chart 
        // Construct the legend
        for (int i = beginIndex; i <= endIndex; i++) {
            LegendItem item = renderer.getLegendItem(0, i);
            result.add(item);
        }
        // Because the only way to create a new LegendTitle is to
        // create a LegendItemSource first
        LegendItemSource source = new LegendItemSource() {
            LegendItemCollection lic = new LegendItemCollection();
            {lic.addAll(result);}
            public LegendItemCollection getLegendItems() {  
                return lic;
            }
        };
        // clear previous legend title
        getChart().clearSubtitles();
        // Create the new LegendTitle and set its position
        LegendTitle legend = new LegendTitle(source);
        legend.setHorizontalAlignment(HorizontalAlignment.CENTER);
        legend.setVerticalAlignment(VerticalAlignment.CENTER);
        legend.setPosition(RectangleEdge.BOTTOM);
        legend.setBorder(1.0, 1.0, 1.0, 1.0);
        legend.setVisible(true);
        // Add the LegendTitle to the graph
        getChart().addLegend(legend);
    }

    /**
     * Set values of the slider according to the number of series
     * plotted on the graph
     */
    private void refreshLegendSlider() {
        int max = getSeriesCount() -1;
        int selection = Math.min(legendSlider.getSelection(), max);
        legendSlider.setValues(selection, 0, max, 1, 1, 1);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文