java - jfreechart-fx 1.0.1 图表是否可以与 fxgraphics2d 构建和呈现的图像进行交互?

标签 java javafx jfreechart jfreechart-fx

我目前正在尝试弄清楚,jfreechart 拆分为 swing(1.5) 和 JavaFX (1.0.1) 是如何影响 JavaFX 部分的。据我(关于这个主题的知识非常有限)了解 jfree-fx 使用 fxgraphics2d 将其原始 Swing 组件(?)绘制到 FX-canvas 中以添加此进入 JavaFX 节点。

现在我的问题是,fxgraphics2d 对象是否仍然可以与之交互?我的意思是像工具提示和滚动以及类似的正常 jfreechart 提供的东西。由于我的知识和时间有限,我想知道,是否值得深入研究 jfree-fx(如果这些图表仍然可以与之交互),或者这些图表是否只是实际图表的图片而不能与之交互。然后我需要找到更好的解决方案。

我目前正在学习如何在我的 Java 应用程序中构建烛台图表。虽然我设法仅使用 JavaFX 构建了一个图表,但一旦绘制了数百个烛台,它的性能就非常糟糕。

然后我遇到了 jfreechart,我读到了它的性能远高于内部 JavaFX 图表可能性。所以今天我设法用 jfreechart-fx 构建了我的第一个图表,性能还不错。此外,我发现构建这些图表更加直观......但我不确定 jfree-fx 版本是否仅将图像或真实图表对象打印到节点。 (我在某处读到有关将图表转换为图像以提高绘图性能的内容...)

感谢您提供有关该主题的任何信息。

例如,这里是我的 JFreeChart 类,它已正确绘制,但我无法使用鼠标与图表进行任何交互。例如。我想使用鼠标滚轮放大/缩小,我想通过单击鼠标左键将图表向左/右平移。这就是为什么我担心我现在只看图像的原因。我通过谷歌找到的所有可行解决方案似乎都只针对 JFreeChart 而不是 JFreeChart-FX

package org.ezstrats.jfreeChart;

import javafx.collections.ObservableList;
import org.ezstrats.model.chartData.Candlestick;
import org.ezstrats.model.chartData.Chart;
import org.ezstrats.model.chartData.Exchange;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

public class JFreeCandlestickChart extends JPanel {

    private static final DateFormat READABLE_TIME_FORMAT = new SimpleDateFormat("kk:mm:ss");

    private OHLCSeries ohlcSeries;
    private TimeSeries volumeSeries;
    private JFreeChart candlestickChart;

    public JFreeCandlestickChart(String title) {
        ObservableList<Candlestick> candlesticks = Exchange.getCandlesticks();
        // Create new chart
        candlestickChart = createChart(title, candlesticks);
        // Create new chart panel
        final ChartPanel chartPanel = new ChartPanel(candlestickChart);
        chartPanel.setPreferredSize(new Dimension(832, 468));
        chartPanel.getChart().getXYPlot().getDomainAxis().setAutoRange(false);
        chartPanel.getChart().getXYPlot().getDomainAxis().setLowerBound(candlesticks.get(candlesticks.size() - 300).getTimestampOpen());
        chartPanel.getChart().getXYPlot().getDomainAxis().setUpperBound(candlesticks.get(candlesticks.size() - 1).getTimestampOpen());
        // Enable zooming - not workign?! ...
        chartPanel.setMouseZoomable(true);
        chartPanel.setMouseWheelEnabled(true);
        chartPanel.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                // process before
                super.mouseDragged(e);
                chartPanel.getChart().getXYPlot().getDomainAxis().configure();
                // process after
            }
        });


        add(chartPanel, BorderLayout.CENTER);
    }

    public JFreeChart createChart(String title, ObservableList<Candlestick> candlesticks){

        /**
         * 1st:
         * Creating candlestick subplot
         */
        // Create OHLCSeriesCollection as a price dataset for candlestick chart
        OHLCSeriesCollection candlestickDataset = new OHLCSeriesCollection();
        ohlcSeries = new OHLCSeries("Price");
        candlestickDataset.addSeries(ohlcSeries);

        // Create candlestick chart priceAxis
        NumberAxis priceAxis = new NumberAxis("Price");
        priceAxis.setAutoRangeIncludesZero(false);

        // Create candlestick chart renderer
        CandlestickRenderer candlestickRenderer = new CandlestickRenderer(CandlestickRenderer.WIDTHMETHOD_AVERAGE,
                false,
                new HighLowItemLabelGenerator(new SimpleDateFormat("kk:mm"), new DecimalFormat("0.00000000")));

        // Create candlestickSubplot
        XYPlot candlestickSubplot = new XYPlot(candlestickDataset, null, priceAxis, candlestickRenderer);
        candlestickSubplot.setBackgroundPaint(Color.white);


        /**
         * 2nd:
         * Creating volume subplot
         */
        // creates TimeSeriesCollection as a volume dataset for volume chart
        TimeSeriesCollection volumeDataset = new TimeSeriesCollection();
        volumeSeries = new TimeSeries("Volume");
        volumeDataset.addSeries(volumeSeries);

        // Create volume chart volumeAxis
        NumberAxis volumeAxis = new NumberAxis("Volume");
        volumeAxis.setAutoRangeIncludesZero(true);

        // Set to no decimal
        volumeAxis.setNumberFormatOverride(new DecimalFormat("0"));

        // Create volume chart renderer
        XYBarRenderer timeRenderer = new XYBarRenderer();
        timeRenderer.setShadowVisible(false);
        timeRenderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator("Volume--> Time={1} Size={2}",
                new SimpleDateFormat("kk:mm"), new DecimalFormat("0")));

        // Create volumeSubplot
        XYPlot volumeSubplot = new XYPlot(volumeDataset, null, volumeAxis, timeRenderer);
        volumeSubplot.setBackgroundPaint(Color.white);


        /**
         * 3rd:
         * Adding Candles to this chart
         **/
         for (Candlestick candle: candlesticks){
            addCandleToChart(candle.getTimestampOpen(),
                    candle.getPriceOpen(),
                    candle.getPriceHigh(),
                    candle.getPriceLow(),
                    candle.getPriceClose(),
                    candle.getVolumeQuote());
        }


        /**
         * 4th:
         * Create chart main plot with two subplots (candlestickSubplot,
         * volumeSubplot) and one common dateAxis
         */
        // Creating charts common dateAxis
        DateAxis dateAxis = new DateAxis("Time");
        dateAxis.setDateFormatOverride(new SimpleDateFormat("dd.mm.yy kk:mm"));
        //dateAxis.setRange();
        // reduce the default left/right margin from 0.05 to 0.02
        dateAxis.setLowerMargin(0.02);
        dateAxis.setUpperMargin(0.02);
        dateAxis.setLabelAngle(0);

        // Create mainPlot
        CombinedDomainXYPlot mainPlot = new CombinedDomainXYPlot(dateAxis);
        mainPlot.setGap(10.0);
        mainPlot.add(candlestickSubplot, 4);
        mainPlot.add(volumeSubplot, 1);
        mainPlot.setOrientation(PlotOrientation.VERTICAL);
        mainPlot.setDomainPannable(true);

        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, mainPlot, false);
        //chart.removeLegend();

        // Einbetten in JScrollPaenl??? um Scrollen zu ermöglichen...
//        ChartPanel chartPanel = new ChartPanel(chart);


        return chart;
    }


    /**
     * Fill series with data.
     *
     * @param c opentime
     * @param o openprice
     * @param h highprice
     * @param l lowprice
     * @param c closeprice
     * @param v volume
     */
    private void addCandleToChart(long time, double o, double h, double l, double c, double v) {
        // Add bar to the data. Let's repeat the same bar
        FixedMillisecond t = new FixedMillisecond(time);
        //READABLE_TIME_FORMAT.parse(String.valueOf(time)));
        ohlcSeries.add(t, o, h, l, c);
        volumeSeries.add(t, v);
    }


    public void setOhlcSeries(OHLCSeries ohlcSeries) {
        this.ohlcSeries = ohlcSeries;
    }
    public void setVolumeSeries(TimeSeries volumeSeries) {
        this.volumeSeries = volumeSeries;
    }
    public OHLCSeries getOhlcSeries() {
        return ohlcSeries;
    }
    public TimeSeries getVolumeSeries() {
        return volumeSeries;
    }
    public JFreeChart getCandlestickChart() {
        return candlestickChart;
    }
}

最佳答案

我没有详细研究 jFreeChart,但我认为它与内置 JavaFX 图表 API 之间的主要区别在于 jFreeChart 使用 Canvas 实现其实现,而内置图表使用场景图。粗略地说,虽然不完全是,它类似于 retained mode (scene graph) vs immediate mode (canvas) 的定义。 .

或许可以为 Canvas 渲染的图形添加交互性。除了基本的全图缩放和拖动操作之外,它在技术上可能具有挑战性。在 StackOverflow 答案的上下文中,实现或演示添加此类与 Canvas 渲染图形的交互性超出了我准备做的事情。

JFreeChart 包含一个interaction 包:

我建议您研究交互包,尝试使用它,看看它是否提供您需要的交互级别。

正如 Roger 在评论中提到的那样,通过将图表包装在 ChartViewer 中,您可以在 JFreeChartFX 图表上获得一些基本交互。使用 ChartViewer(JFreeChart myChart)

相关问题:

Canvas 与 SceneGraph 的旁白

包含有关 Canvas 如何工作的信息,以便您更好地了解这里发生的事情(请注意,此处的所有内容可能不是 100% 正确,但足够接近以帮助理解)。

从技术上讲,JavaFX 仅使用 SceneGraph 进行渲染。 canvas内部是如何实现的,据我了解,每个canvas都是scenegraph中的一个节点,自带一个绘制指令的命令队列。当您在 Canvas 上绘制时,它不会立即绘制,而是将绘图命令放入队列中,然后在某个时候,在下一个 60fps 绘图脉冲完成之前,它将这些渲染到图像缓冲区,然后中继到JavaFX 节点。一旦执行, Canvas 命令队列就会忘记旧命令,因此所有内容最终都以像素形式结束。您可以跟踪应用程序中的绘图命令,并根据需要重新发出它们以从头开始重新绘制 Canvas ,但 Canvas 对此无济于事。

JFreeChartFX 正在做的是提供一个适配器,使 JavaFX Canvas 看起来像一个 Swing 绘画表面,这样 JFreeChart 的繁重工作和内部引擎就可以用来发出所有的绘图命令,并且可以将这些命令渲染到JavaFX Canvas 或 Swing Canvas ,具体取决于所需的输出 UI 技术。

如果 JFreeChart 还为 JavaFX 事件而不是 Swing 事件提供了类似的适配器,并且如果 JFreeChart 已经有使用 Swing 事件进行交互的方法,那么它可能会使用类似的适配器或 Swing 事件的替换来为 JFreeChartFX 添加交互性机制。也许这就是上面链接的 interaction 包正在做的事情。

关于java - jfreechart-fx 1.0.1 图表是否可以与 fxgraphics2d 构建和呈现的图像进行交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57693217/

相关文章:

JavaFX如何将文本写入textArea中的新行?

java - 如何使用java中的jfreechart为散点图中的每个点分配不同的颜色?

java - 如何使用 JFreeChart 准备数据集以直方图的形式显示?

java - 如何为旋转的陀螺制作动画?

JavaFX KeyEvent 和重音字符

JavaFX 允许用户仅编辑文本的某些区域

java - 从 JFreeChart 饼图中删除标签

java - 如何在线性布局中以编程方式创建 TextView?

Windows 上带有通配符的 java 类路径 : Requires semi-colon at the end

java - 安装到 Maven 中的另一个本地存储库 [JAVA]