2012-03-12 21 views
6

平和はあなたにあります!JAVAFXチャートを使用してチャートに複数の軸を描画する方法

JavaFXグラフAPIを使用して複数軸を描画する方法は?

次は、リアルタイムのデータと履歴データがプロットされている巨大なソフトウェアのトレンドのイメージです。 2つのペンが登録されており、各ペンの別々の軸が同じ傾向で定義されています。

Trends with multiple axis in IntelliMax

私はJavaFXの2.0チャートに正確にこれを実行しようとしています。私は次のようであるリアルタイムチャートをプロットすることができました:

JavaFX 2.0 Line Chart;two NumberSeries with random points plotted

私はJavaFXのを使用して、複数の軸を中心に研究しているとthisリンクを見つけることができるが、私は、これは使用してJavaFXの古いバージョンのだと思いますFXML。しかし、私はこれを達成するためにJavaFXの正規のクラスを使用しています。

HELP!

+2

リンク先の記事は全くのJavaFXに関するものではありません。それはAdobe Flexのコードです。 –

+0

これは大変です! = X ...しかし、問題は続く...助けてください –

+1

これはもっと注意が必要です。 – GGrec

答えて

5

StackPaneに2つのグラフを配置します。

chartcss lookupsを使用して、軸とラベルを翻訳(translate-xとtranslate-yを使用)して、下の図とは独立して読むことができます。上のグラフのデータプロットを下のグラフの上に重ねるようにしておきます。どのデータプロットがどのシリーズに属しているかが分かりやすいように、色と凡例を変更する(またはグラフや線を組み合わせるなど)。

上記実証されている技術のいくつか

:への答えで

layered

+0

うーん...それは困難な仕事になるだろうが、私たちの場合はおそらく解決策になるだろう。 ありがとうございますjewelsea =) –

+0

私はこれが古かったと知っていますが、とにかくスタックされたチャートがmouseeventsを待ち受けるようにしています。現時点では、最後に追加されたチャートのみがイベントを受け取ります。バックグラウンドは透過的ですが、何らかのクリップのように思われます。 – BAR

+0

@ user417896誰かがあなたの追加の質問に答えることができます:[JavaFXは、透明なノードから子供へのMouseEventsを渡します](http://stackoverflow.com/questions/16876083/javafx-pass-mouseeventsthrough-transparent-node-to-children) – jewelsea

7

Hereあなたは私の解決策を見つけることができます - MultipleAxesLineChart。それは一般的なものではなく、私のニーズに合っていますが、StackPaneを使ってどのように行うことができるのかをよく知ることができると思います。

MultipleAxesLineChart

+0

こんにちは、Maciej、 すごく嬉しいです! 私はあなたのソリューションを使用し、右軸のアクションを設定したいと思います。なぜ私はそれを左だけにすることができますか? –

+0

@JuliaGrabovskaあなたは行動によって何を意味しますか? –

+0

"yAxis.setOnMousePressed"たとえば、 –

0
public class MultipleAxesLineChart extends StackPane { 

private final LineChart baseChart; 
private final ObservableList<LineChart> backgroundCharts = FXCollections.observableArrayList(); 
private final Map<LineChart, Color> chartColorMap = new HashMap<>(); 

private final double yAxisWidth = 60; 
private final AnchorPane detailsWindow; 

private final double yAxisSeparation = 20; 
private double strokeWidth = 0.3; 

public MultipleAxesLineChart(LineChart baseChart, Color lineColor) { 
    this(baseChart, lineColor, null); 
} 

public MultipleAxesLineChart(LineChart baseChart, Color lineColor, Double strokeWidth) { 
    if (strokeWidth != null) { 
     this.strokeWidth = strokeWidth; 
    } 
    this.baseChart = baseChart; 

    chartColorMap.put(baseChart, lineColor); 

    styleBaseChart(baseChart); 
    styleChartLine(baseChart, lineColor); 
    setFixedAxisWidth(baseChart); 

    setAlignment(Pos.CENTER_LEFT); 

    backgroundCharts.addListener((Observable observable) -> rebuildChart()); 

    detailsWindow = new AnchorPane(); 
    bindMouseEvents(baseChart, this.strokeWidth); 

    rebuildChart(); 
} 

private void bindMouseEvents(LineChart baseChart, Double strokeWidth) { 
    final DetailsPopup detailsPopup = new DetailsPopup(); 
    getChildren().add(detailsWindow); 
    detailsWindow.getChildren().add(detailsPopup); 
    detailsWindow.prefHeightProperty().bind(heightProperty()); 
    detailsWindow.prefWidthProperty().bind(widthProperty()); 
    detailsWindow.setMouseTransparent(true); 

    setOnMouseMoved(null); 
    setMouseTransparent(false); 

    final Axis xAxis = baseChart.getXAxis(); 
    final Axis yAxis = baseChart.getYAxis(); 

    final Line xLine = new Line(); 
    final Line yLine = new Line(); 
    yLine.setFill(Color.GRAY); 
    xLine.setFill(Color.GRAY); 
    yLine.setStrokeWidth(strokeWidth/2); 
    xLine.setStrokeWidth(strokeWidth/2); 
    xLine.setVisible(false); 
    yLine.setVisible(false); 

    final Node chartBackground = baseChart.lookup(".chart-plot-background"); 
    for (Node n: chartBackground.getParent().getChildrenUnmodifiable()) { 
     if (n != chartBackground && n != xAxis && n != yAxis) { 
      n.setMouseTransparent(true); 
     } 
    } 
    chartBackground.setCursor(Cursor.CROSSHAIR); 
    chartBackground.setOnMouseEntered((event) -> { 
     chartBackground.getOnMouseMoved().handle(event); 
     detailsPopup.setVisible(true); 
     xLine.setVisible(true); 
     yLine.setVisible(true); 
     detailsWindow.getChildren().addAll(xLine, yLine); 
    }); 
    chartBackground.setOnMouseExited((event) -> { 
     detailsPopup.setVisible(false); 
     xLine.setVisible(false); 
     yLine.setVisible(false); 
     detailsWindow.getChildren().removeAll(xLine, yLine); 
    }); 
    chartBackground.setOnMouseMoved(event -> { 
     double x = event.getX() + chartBackground.getLayoutX(); 
     double y = event.getY() + chartBackground.getLayoutY(); 

     xLine.setStartX(10); 
     xLine.setEndX(detailsWindow.getWidth()-10); 
     xLine.setStartY(y+5); 
     xLine.setEndY(y+5); 

     yLine.setStartX(x+5); 
     yLine.setEndX(x+5); 
     yLine.setStartY(10); 
     yLine.setEndY(detailsWindow.getHeight()-10); 

     detailsPopup.showChartDescrpition(event); 

     if (y + detailsPopup.getHeight() + 10 < getHeight()) { 
      AnchorPane.setTopAnchor(detailsPopup, y+10); 
     } else { 
      AnchorPane.setTopAnchor(detailsPopup, y-10-detailsPopup.getHeight()); 
     } 

     if (x + detailsPopup.getWidth() + 10 < getWidth()) { 
      AnchorPane.setLeftAnchor(detailsPopup, x+10); 
     } else { 
      AnchorPane.setLeftAnchor(detailsPopup, x-10-detailsPopup.getWidth()); 
     } 
    }); 
} 

private void styleBaseChart(LineChart baseChart) { 
    baseChart.setCreateSymbols(false); 
    baseChart.setLegendVisible(false); 
    baseChart.getXAxis().setAutoRanging(false); 
    baseChart.getXAxis().setAnimated(false); 
    baseChart.getYAxis().setAnimated(false); 
} 

private void setFixedAxisWidth(LineChart chart) { 
    chart.getYAxis().setPrefWidth(yAxisWidth); 
    chart.getYAxis().setMaxWidth(yAxisWidth); 
} 

private void rebuildChart() { 
    getChildren().clear(); 

    getChildren().add(resizeBaseChart(baseChart)); 
    for (LineChart lineChart : backgroundCharts) { 
     getChildren().add(resizeBackgroundChart(lineChart)); 
    } 
    getChildren().add(detailsWindow); 
} 

private Node resizeBaseChart(LineChart lineChart) { 
    HBox hBox = new HBox(lineChart); 
    hBox.setAlignment(Pos.CENTER_LEFT); 
    hBox.prefHeightProperty().bind(heightProperty()); 
    hBox.prefWidthProperty().bind(widthProperty()); 

    lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backgroundCharts.size())); 
    lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backgroundCharts.size())); 
    lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backgroundCharts.size())); 

    return lineChart; 
} 

private Node resizeBackgroundChart(LineChart lineChart) { 
    HBox hBox = new HBox(lineChart); 
    hBox.setAlignment(Pos.CENTER_LEFT); 
    hBox.prefHeightProperty().bind(heightProperty()); 
    hBox.prefWidthProperty().bind(widthProperty()); 
    hBox.setMouseTransparent(true); 

    lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backgroundCharts.size())); 
    lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backgroundCharts.size())); 
    lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backgroundCharts.size())); 

    lineChart.translateXProperty().bind(baseChart.getYAxis().widthProperty()); 
    lineChart.getYAxis().setTranslateX((yAxisWidth + yAxisSeparation) * backgroundCharts.indexOf(lineChart)); 

    return hBox; 
} 

public void addSeries(XYChart.Series series, Color lineColor) { 
    NumberAxis yAxis = new NumberAxis(); 
    NumberAxis xAxis = new NumberAxis(); 

    // style x-axis 
    xAxis.setAutoRanging(false); 
    xAxis.setVisible(false); 
    xAxis.setOpacity(0.0); // somehow the upper setVisible does not work 
    xAxis.lowerBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).lowerBoundProperty()); 
    xAxis.upperBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).upperBoundProperty()); 
    xAxis.tickUnitProperty().bind(((NumberAxis) baseChart.getXAxis()).tickUnitProperty()); 

    // style y-axis 
    yAxis.setSide(Side.RIGHT); 
    yAxis.setLabel(series.getName()); 

    // create chart 
    LineChart lineChart = new LineChart(xAxis, yAxis); 
    lineChart.setAnimated(false); 
    lineChart.setLegendVisible(false); 
    lineChart.getData().add(series); 

    styleBackgroundChart(lineChart, lineColor); 
    setFixedAxisWidth(lineChart); 

    chartColorMap.put(lineChart, lineColor); 
    backgroundCharts.add(lineChart); 
} 

private void styleBackgroundChart(LineChart lineChart, Color lineColor) { 
    styleChartLine(lineChart, lineColor); 

    Node contentBackground = lineChart.lookup(".chart-content").lookup(".chart-plot-background"); 
    contentBackground.setStyle("-fx-background-color: transparent;"); 

    lineChart.setVerticalZeroLineVisible(false); 
    lineChart.setHorizontalZeroLineVisible(false); 
    lineChart.setVerticalGridLinesVisible(false); 
    lineChart.setHorizontalGridLinesVisible(false); 
    lineChart.setCreateSymbols(false); 
} 

private String toRGBCode(Color color) { 
    return String.format("#%02X%02X%02X", 
      (int) (color.getRed() * 255), 
      (int) (color.getGreen() * 255), 
      (int) (color.getBlue() * 255)); 
} 

private void styleChartLine(LineChart chart, Color lineColor) { 
    chart.getYAxis().lookup(".axis-label").setStyle("-fx-text-fill: " + toRGBCode(lineColor) + "; -fx-font-weight: bold;"); 
    Node seriesLine = chart.lookup(".chart-series-line"); 
    seriesLine.setStyle("-fx-stroke: " + toRGBCode(lineColor) + "; -fx-stroke-width: " + strokeWidth + ";"); 
} 

public Node getLegend() { 
    HBox hBox = new HBox(); 

    final CheckBox baseChartCheckBox = new CheckBox(baseChart.getYAxis().getLabel()); 
    baseChartCheckBox.setSelected(true); 
    baseChartCheckBox.setStyle("-fx-text-fill: " + toRGBCode(chartColorMap.get(baseChart)) + "; -fx-font-weight: bold;"); 
    baseChartCheckBox.setDisable(true); 
    baseChartCheckBox.getStyleClass().add("readonly-checkbox"); 
    baseChartCheckBox.setOnAction(event -> baseChartCheckBox.setSelected(true)); 
    hBox.getChildren().add(baseChartCheckBox); 

    for (final LineChart lineChart : backgroundCharts) { 
     CheckBox checkBox = new CheckBox(lineChart.getYAxis().getLabel()); 
     checkBox.setStyle("-fx-text-fill: " + toRGBCode(chartColorMap.get(lineChart)) + "; -fx-font-weight: bold"); 
     checkBox.setSelected(true); 
     checkBox.setOnAction(event -> { 
      if (backgroundCharts.contains(lineChart)) { 
       backgroundCharts.remove(lineChart); 
      } else { 
       backgroundCharts.add(lineChart); 
      } 
     }); 
     hBox.getChildren().add(checkBox); 
    } 

    hBox.setAlignment(Pos.CENTER); 
    hBox.setSpacing(20); 
    hBox.setStyle("-fx-padding: 0 10 20 10"); 

    return hBox; 
} 

private class DetailsPopup extends VBox { 

    private DetailsPopup() { 
     setStyle("-fx-border-width: 1px; -fx-padding: 5 5 5 5px; -fx-border-color: gray; -fx-background-color: whitesmoke;"); 
     setVisible(false); 
    } 

    public void showChartDescrpition(MouseEvent event) { 
     getChildren().clear(); 

     Long xValueLong = Math.round((double)baseChart.getXAxis().getValueForDisplay(event.getX())); 

     HBox baseChartPopupRow = buildPopupRow(event, xValueLong, baseChart); 
     if (baseChartPopupRow != null) { 
      getChildren().add(baseChartPopupRow); 
     } 

     for (LineChart lineChart : backgroundCharts) { 
      HBox popupRow = buildPopupRow(event, xValueLong, lineChart); 
      if (popupRow == null) continue; 

      getChildren().add(popupRow); 
     } 
    } 

    private HBox buildPopupRow(MouseEvent event, Long xValueLong, LineChart lineChart) { 
     Label seriesName = new Label(lineChart.getYAxis().getLabel()); 
     seriesName.setTextFill(chartColorMap.get(lineChart)); 

     Number yValueForChart = getYValueForX(lineChart, xValueLong.intValue()); 
     if (yValueForChart == null) { 
      return null; 
     } 
     Number yValueLower = Math.round(normalizeYValue(lineChart, event.getY() - 10)); 
     Number yValueUpper = Math.round(normalizeYValue(lineChart, event.getY() + 10)); 
     Number yValueUnderMouse = Math.round((double) lineChart.getYAxis().getValueForDisplay(event.getY())); 

     // make series name bold when mouse is near given chart's line 
     if (isMouseNearLine(yValueForChart, yValueUnderMouse, Math.abs(yValueLower.doubleValue()-yValueUpper.doubleValue()))) { 
      seriesName.setStyle("-fx-font-weight: bold"); 
     } 

     HBox popupRow = new HBox(10, seriesName, new Label("["+yValueForChart+"]")); 
     return popupRow; 
    } 

    private double normalizeYValue(LineChart lineChart, double value) { 
     Double val = (Double) lineChart.getYAxis().getValueForDisplay(value); 
     if (val == null) { 
      return 0; 
     } else { 
      return val; 
     } 
    } 

    private boolean isMouseNearLine(Number realYValue, Number yValueUnderMouse, Double tolerance) { 
     return (Math.abs(yValueUnderMouse.doubleValue() - realYValue.doubleValue()) < tolerance); 
    } 

    public Number getYValueForX(LineChart chart, Number xValue) { 
     List<XYChart.Data> dataList = ((List<XYChart.Data>)((XYChart.Series)chart.getData().get(0)).getData()); 
     for (XYChart.Data data : dataList) { 
      if (data.getXValue().equals(xValue)) { 
       return (Number)data.getYValue(); 
      } 
     } 
     return null; 
    } 
} 
} 

MultipleAxisMainChart:

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.chart.LineChart; 
import javafx.scene.chart.NumberAxis; 
import javafx.scene.chart.XYChart; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.paint.Color; 
import javafx.stage.Stage; 

import java.util.function.Function; 

public class MultipleAxesLineChartMain extends Application { 

    public static final int X_DATA_COUNT = 3600; 

    @Override 
    public void start(Stage primaryStage) throws Exception{ 
     NumberAxis xAxis = new NumberAxis(0, X_DATA_COUNT, 200); 
     NumberAxis yAxis = new NumberAxis(); 
     yAxis.setLabel("Series 1"); 

     LineChart baseChart = new LineChart(xAxis, yAxis); 
     baseChart.getData().add(prepareSeries("Series 1", (x) -> (double)x)); 

     MultipleAxesLineChart chart = new MultipleAxesLineChart(baseChart, Color.RED); 
     chart.addSeries(prepareSeries("Series 2", (x) -> (double)x*x),Color.BLUE); 
     chart.addSeries(prepareSeries("Series 3", (x) -> (double)-x*x),Color.GREEN); 
     chart.addSeries(prepareSeries("Series 4", (x) -> ((double) (x-250))*x),Color.DARKCYAN); 
     chart.addSeries(prepareSeries("Series 5", (x) -> ((double)(x+100)*(x-200))),Color.BROWN); 

     primaryStage.setTitle("MultipleAxesLineChart"); 

     BorderPane borderPane = new BorderPane(); 
     borderPane.setCenter(chart); 
     borderPane.setBottom(chart.getLegend()); 

     Scene scene = new Scene(borderPane, 1024, 600); 
     scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm()); 

     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    private XYChart.Series<Number, Number> prepareSeries(String name, Function<Integer, Double> function) { 
     XYChart.Series<Number, Number> series = new XYChart.Series<>(); 
     series.setName(name); 
     for (int i = 0; i < X_DATA_COUNT; i++) { 
      series.getData().add(new XYChart.Data<>(i, function.apply(i))); 
     } 
     return series; 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 
関連する問題