All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fair_acc.sample.chart.ErrorDataSetRendererStylingSample Maven / Gradle / Ivy

Go to download

Small sample applications to showcase the features of the chart-fx library.

The newest version!
package io.fair_acc.sample.chart;

import static io.fair_acc.dataset.DataSet.DIM_X;

import java.util.*;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.fair_acc.chartfx.XYChart;
import io.fair_acc.chartfx.axes.spi.DefaultNumericAxis;
import io.fair_acc.chartfx.marker.DefaultMarker;
import io.fair_acc.chartfx.plugins.*;
import io.fair_acc.chartfx.renderer.ErrorStyle;
import io.fair_acc.chartfx.renderer.LineStyle;
import io.fair_acc.chartfx.renderer.datareduction.DefaultDataReducer;
import io.fair_acc.chartfx.renderer.spi.ErrorDataSetRenderer;
import io.fair_acc.chartfx.ui.ProfilerInfoBox;
import io.fair_acc.chartfx.ui.ProfilerInfoBox.DebugLevel;
import io.fair_acc.dataset.DataSetError;
import io.fair_acc.dataset.spi.DefaultErrorDataSet;
import io.fair_acc.dataset.testdata.spi.*;
import io.fair_acc.dataset.utils.ProcessingProfiler;

public class ErrorDataSetRendererStylingSample extends ChartSample {
    private static final String STOP_TIMER = "stop timer";
    private static final String START_TIMER = "start timer";
    private static final Logger LOGGER = LoggerFactory.getLogger(RollingBufferSample.class);
    private static final int DEBUG_UPDATE_RATE = 1000;
    private static final int UPDATE_DELAY = 1000; // [ms]
    private static final int UPDATE_PERIOD = 100; // [ms]
    private static final double N_MAX_SAMPLES = 10_000;
    private final Random rnd = new Random();
    private DataSetType dataSetType = DataSetType.RANDOM_WALK;
    private boolean dataSetIncludeNaNs = false;
    private int nSamples = 400;
    private Timer timer;
    private ComboBox errorTypeCombo;
    private TextField dataSetStyle;

    private void generateData(final XYChart chart) {
        long startTime = ProcessingProfiler.getTimeStamp();
        final List dataSet = new ArrayList<>();
        switch (dataSetType) {
        case OUTLIER:
            dataSet.add(new SingleOutlierFunction("function with single outlier", nSamples));
            break;
        case STEP:
            dataSet.add(new RandomStepFunction("random step function", nSamples));
            break;
        case SINC:
            dataSet.add(new SincFunction("sinc function", nSamples));
            break;
        case GAUSS:
            dataSet.add(new GaussFunction("gauss function", nSamples));
            break;
        case SINE:
            dataSet.add(new SineFunction("sine function", nSamples));
            break;
        case COSINE:
            dataSet.add(new CosineFunction("cosine function", nSamples));
            break;
        case MIX_TRIGONOMETRIC:
            dataSet.add(new SineFunction("dyn. sine function", nSamples, true));
            dataSet.add(new CosineFunction("dyn. cosine function", nSamples, true));
            break;
        case ERROR_TYPE_TEST:
            dataSet.add(new ErrorTestDataSet(nSamples, errorTypeCombo.getValue()));
            break;
        case RANDOM_WALK:
        default:
            dataSet.add(new RandomWalkFunction("random walk data", nSamples));
            break;
        }

        final List dataSetsWithNaN;
        if (dataSetIncludeNaNs) {
            dataSetsWithNaN = dataSet.stream() //
                                      .map(ds -> {
                                          final DefaultErrorDataSet newDs = new DefaultErrorDataSet(ds);
                                          for (int i = Math.max(Math.min(nSamples / 10, 500), 2); i >= 0; i--) { // how many gaps to produce
                                              final int index = rnd.nextInt(nSamples - i);
                                              for (int j = i; j >= 0; j--) { // produce gaps with 1 to n consecutive NaNs
                                                  newDs.set(index, ds.get(DIM_X, index + j), Double.NaN);
                                              }
                                          }
                                          return newDs;
                                      })
                                      .collect(Collectors.toList());
        } else {
            dataSetsWithNaN = dataSet;
        }

        dataSetsWithNaN.forEach(dataSetError -> dataSetError.setStyle(dataSetStyle.getText()));

        Platform.runLater(() -> {
            chart.getRenderers().get(0).getDatasets().setAll(dataSetsWithNaN);
            chart.invalidate();
        });
        startTime = ProcessingProfiler.getTimeDiff(startTime, "adding data into DataSet");
    }

    private ParameterTab getAxisTab(final String name, final XYChart chart, final DefaultNumericAxis axis) {
        final ParameterTab pane = new ParameterTab(name);

        final CheckBox animated = new CheckBox();
        animated.selectedProperty().bindBidirectional(axis.animatedProperty());
        animated.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Amimated: ", animated);

        final CheckBox autoranging = new CheckBox();
        autoranging.selectedProperty().bindBidirectional(axis.autoRangingProperty());
        autoranging.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Auto-Ranging: ", autoranging);

        final CheckBox autogrowranging = new CheckBox();
        autogrowranging.selectedProperty().bindBidirectional(axis.autoGrowRangingProperty());
        autogrowranging.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Auto-Grow-Ranging: ", autogrowranging);

        final Spinner upperRange = new Spinner<>(-N_MAX_SAMPLES, +N_MAX_SAMPLES, axis.getMin(), 0.1);
        upperRange.valueProperty().addListener((ch, old, value) -> {
            axis.setMax(value.doubleValue());
            chart.invalidate();
        });
        pane.addToParameterPane("   Upper Bound): ", upperRange);

        final Spinner lowerRange = new Spinner<>(-N_MAX_SAMPLES, +N_MAX_SAMPLES, axis.getMin(), 0.1);
        lowerRange.valueProperty().addListener((ch, old, value) -> {
            axis.setMin(value.doubleValue());
            chart.invalidate();
        });
        pane.addToParameterPane("   Lower Bound): ", lowerRange);

        final CheckBox autoRangeRounding = new CheckBox();
        autoRangeRounding.selectedProperty().bindBidirectional(axis.autoRangeRoundingProperty());
        autoRangeRounding.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Auto-Range-Rounding: ", autoRangeRounding);

        final Spinner autoRangePadding = new Spinner<>(0, 100.0, axis.getAutoRangePadding(), 0.05);
        autoRangePadding.valueProperty().addListener((ch, old, value) -> {
            axis.setAutoRangePadding(value);
            chart.invalidate();
        });
        pane.addToParameterPane("   auto-range padding): ", autoRangePadding);

        final CheckBox logAxis = new CheckBox();
        logAxis.selectedProperty().bindBidirectional(axis.logAxisProperty());
        logAxis.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Log-Axis: ", logAxis);

        final CheckBox timeAxis = new CheckBox();
        timeAxis.selectedProperty().bindBidirectional(axis.timeAxisProperty());
        timeAxis.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Time-Axis: ", timeAxis);

        final CheckBox invertAxis = new CheckBox();
        invertAxis.selectedProperty().bindBidirectional(axis.invertAxisProperty());
        invertAxis.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Invert-Axis: ", invertAxis);

        final CheckBox autoUnit = new CheckBox();
        autoUnit.selectedProperty().bindBidirectional(axis.autoUnitScalingProperty());
        autoUnit.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Auto Unit: ", autoUnit);

        pane.addToParameterPane(" ", null);

        return pane;
    }

    private Tab getChartTab(XYChart chart) {
        final ParameterTab pane = new ParameterTab("Chart");

        final CheckBox gridVisibleX = new CheckBox("");
        gridVisibleX.setSelected(true);
        chart.getGridRenderer().getHorizontalMajorGrid().visibleProperty().bindBidirectional(gridVisibleX.selectedProperty());
        pane.addToParameterPane("Show X-Grid: ", gridVisibleX);

        final CheckBox gridVisibleXMinor = new CheckBox("");
        gridVisibleXMinor.setSelected(true);
        chart.getGridRenderer().getVerticalMinorGrid().visibleProperty().bindBidirectional(gridVisibleXMinor.selectedProperty());
        pane.addToParameterPane("Show Minor X-Grid: ", gridVisibleXMinor);

        final CheckBox gridVisibleY = new CheckBox("");
        gridVisibleY.setSelected(true);
        chart.getGridRenderer().getVerticalMajorGrid().visibleProperty().bindBidirectional(gridVisibleY.selectedProperty());
        pane.addToParameterPane("Show Y-Grid: ", gridVisibleY);

        final CheckBox gridVisibleYMinor = new CheckBox("");
        gridVisibleYMinor.setSelected(true);
        chart.getGridRenderer().getHorizontalMinorGrid().visibleProperty().bindBidirectional(gridVisibleYMinor.selectedProperty());
        pane.addToParameterPane("Show Minor Y-Grid: ", gridVisibleYMinor);

        final CheckBox gridOnTop = new CheckBox("");
        gridOnTop.setSelected(true);
        chart.getGridRenderer().drawOnTopProperty().bindBidirectional(gridOnTop.selectedProperty());
        pane.addToParameterPane("Grid on top: ", gridOnTop);

        final TextField style = new TextField("");
        chart.styleProperty().bindBidirectional(style.textProperty());
        pane.addToParameterPane("Style: ", style);

        return pane;
    }

    private HBox getHeaderBar(final XYChart chart) {
        final Button newDataSet = new Button("new DataSet");
        newDataSet.setOnAction(evt -> Platform.runLater(getTimerTask(chart)));

        // repetitively generate new data
        final Button startTimer = new Button(START_TIMER);
        startTimer.setOnAction(evt -> {
            if (timer == null) {
                startTimer.setText(STOP_TIMER);
                timer = new Timer("sample-update-timer", true);
                timer.scheduleAtFixedRate(getTimerTask(chart), UPDATE_DELAY, UPDATE_PERIOD);
            } else {
                startTimer.setText(START_TIMER);
                timer.cancel();
                timer = null; // NOPMD
            }
        });

        final ComboBox dataSetTypeSelector = new ComboBox<>();
        dataSetTypeSelector.getItems().addAll(DataSetType.values());
        dataSetTypeSelector.setValue(dataSetType);
        dataSetTypeSelector.valueProperty().addListener((ch, old, selection) -> {
            dataSetType = selection;
            errorTypeCombo.setDisable(dataSetType != DataSetType.ERROR_TYPE_TEST);
            generateData(chart);
        });

        final CheckBox dataSetIncludeNaNsBox = new CheckBox("Include NaNs");
        dataSetIncludeNaNsBox.selectedProperty().addListener((ch, oldVal, newVal) -> {
            dataSetIncludeNaNs = newVal;
            generateData(chart);
        });

        dataSetStyle = new TextField("");

        // H-Spacer
        final Region spacer = new Region();
        spacer.setMinWidth(Region.USE_PREF_SIZE);
        HBox.setHgrow(spacer, Priority.ALWAYS);
        final ProfilerInfoBox profilerInfoBox = new ProfilerInfoBox(DEBUG_UPDATE_RATE);
        profilerInfoBox.setDebugLevel(DebugLevel.VERSION);

        return new HBox(new Label("Function Type: "), dataSetTypeSelector, dataSetIncludeNaNsBox, new Label("DataSet Style:"), dataSetStyle, newDataSet, startTimer, spacer, profilerInfoBox);
    }

    private ParameterTab getRendererTab(final XYChart chart, final ErrorDataSetRenderer errorRenderer) {
        final ParameterTab pane = new ParameterTab("Renderer");

        final ComboBox polyLineSelect = new ComboBox<>();
        polyLineSelect.getItems().addAll(LineStyle.values());
        polyLineSelect.setValue(LineStyle.NORMAL);
        polyLineSelect.valueProperty().addListener((ch, old, selection) -> {
            errorRenderer.setPolyLineStyle(selection);
            chart.invalidate();
        });
        pane.addToParameterPane("PolyLine Style: ", polyLineSelect);

        final ComboBox errorStyleSelect = new ComboBox<>();
        errorStyleSelect.getItems().addAll(ErrorStyle.values());
        errorStyleSelect.setValue(errorRenderer.getErrorType());
        errorStyleSelect.valueProperty().addListener((ch, old, selection) -> {
            errorRenderer.setErrorStyle(selection);
            chart.invalidate();
        });
        pane.addToParameterPane("Error-Bar Style: ", errorStyleSelect);

        pane.addToParameterPane(" ", null);

        final CheckBox drawMarker = new CheckBox();
        drawMarker.setSelected(errorRenderer.isDrawMarker());
        drawMarker.selectedProperty().addListener((ch, old, selected) -> {
            errorRenderer.setDrawMarker(selected);
        });
        pane.addToParameterPane("Draw Markers: ", drawMarker);

        final Spinner markerSize = new Spinner<>(0, 100, 1.5, 0.5);
        markerSize.isEditable();
        markerSize.valueProperty().addListener((ch, old, value) -> {
            errorRenderer.getDatasetNodes().forEach(node -> node.setMarkerSize(value.doubleValue()));
        });
        pane.addToParameterPane("   Marker Size: ", markerSize);

        final ComboBox markerStyle = new ComboBox<>();
        markerStyle.getItems().addAll(DefaultMarker.values());
        markerStyle.setValue(DefaultMarker.DEFAULT);
        markerStyle.valueProperty().addListener((ch, old, selection) -> {
            errorRenderer.getDatasetNodes().forEach(node -> node.setMarkerType(selection));
            chart.invalidate();
        });
        pane.addToParameterPane("   Marker Style: ", markerStyle);

        final Spinner dashSize = new Spinner<>(0, 100, errorRenderer.getDashSize(), 1);
        dashSize.isEditable();
        dashSize.valueProperty().addListener((ch, old, value) -> {
            errorRenderer.setDashSize(value.intValue());
            chart.invalidate();
        });
        pane.addToParameterPane("Cap Dash Size: ", dashSize);

        pane.addToParameterPane(" ", null);

        final CheckBox drawBars = new CheckBox();
        drawBars.setSelected(errorRenderer.isDrawBars());
        drawBars.selectedProperty().addListener((ch, old, selected) -> {
            errorRenderer.setDrawBars(selected);
            chart.invalidate();
        });
        pane.addToParameterPane("Draw Bars: ", drawBars);

        final CheckBox dynBarWidthEnable = new CheckBox();
        dynBarWidthEnable.setSelected(errorRenderer.isDynamicBarWidth());
        dynBarWidthEnable.selectedProperty().addListener((ch, old, selected) -> {
            errorRenderer.setDynamicBarWidth(selected);
            chart.invalidate();
        });
        pane.addToParameterPane("   Dyn. Bar Width: ", dynBarWidthEnable);

        final Spinner dynBarWidth = new Spinner<>(0, 100, errorRenderer.getBarWidthPercentage(), 10);
        dynBarWidth.valueProperty().addListener((ch, old, value) -> {
            errorRenderer.setBarWidthPercentage(value.intValue());
            chart.invalidate();
        });
        pane.addToParameterPane("   Dyn. Bar Width: ", dynBarWidth);

        final Spinner barWidth = new Spinner<>(0, 100, errorRenderer.getBarWidth());
        barWidth.valueProperty().addListener((ch, old, value) -> {
            errorRenderer.setBarWidth(value.intValue());
            chart.invalidate();
        });
        pane.addToParameterPane("   Abs. Bar Width: ", barWidth);

        final CheckBox shiftBar = new CheckBox();
        shiftBar.setSelected(errorRenderer.isShiftBar());
        shiftBar.selectedProperty().addListener((ch, old, selected) -> {
            errorRenderer.setShiftBar(selected);
            chart.invalidate();
        });
        pane.addToParameterPane("   Shift Bar (mult. data sets): ", shiftBar);

        final Spinner shiftBarOffset = new Spinner<>(0, 100, errorRenderer.getShiftBarOffset());
        shiftBarOffset.valueProperty().addListener((ch, old, value) -> {
            errorRenderer.setshiftBarOffset(value.intValue());
            chart.invalidate();
        });
        pane.addToParameterPane("   Shift Bar Offset (mult. DS): ", shiftBarOffset);

        pane.addToParameterPane(" ", null);

        final CheckBox pointReduction = new CheckBox();
        pointReduction.selectedProperty().bindBidirectional(errorRenderer.pointReductionProperty());
        pointReduction.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Point Reduction: ", pointReduction);

        final DefaultDataReducer dataReducer = (DefaultDataReducer) errorRenderer.getRendererDataReducer();
        final Spinner reductionMinSize = new Spinner<>(0, 1000, errorRenderer.getMinRequiredReductionSize());
        reductionMinSize.setEditable(true);
        errorRenderer.minRequiredReductionSizeProperty().bind(reductionMinSize.valueProperty());
        reductionMinSize.valueProperty().addListener((ch, old, value) -> chart.invalidate());
        pane.addToParameterPane("   Min Req. Samples: ", reductionMinSize);

        final Spinner reductionDashSize = new Spinner<>(0, 100, dataReducer.getMinPointPixelDistance());
        dataReducer.minPointPixelDistanceProperty().bind(reductionDashSize.valueProperty());
        reductionDashSize.valueProperty().addListener((ch, old, value) -> chart.invalidate());
        pane.addToParameterPane("   Red. Min Distance: ", reductionDashSize);

        pane.addToParameterPane(" ", null);
        final CheckBox assumeSorted = new CheckBox();
        assumeSorted.selectedProperty().bindBidirectional(errorRenderer.assumeSortedDataProperty());
        assumeSorted.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("Assume sorted data: ", assumeSorted);

        pane.addToParameterPane(" ", null);
        final CheckBox cacheParallel = new CheckBox();
        cacheParallel.selectedProperty().bindBidirectional(errorRenderer.parallelImplementationProperty());
        cacheParallel.selectedProperty().addListener((ch, old, selected) -> chart.invalidate());
        pane.addToParameterPane("   Point cache parallel: ", cacheParallel);

        final CheckBox allowNaNs = new CheckBox();
        allowNaNs.setSelected(errorRenderer.isallowNaNs());
        allowNaNs.selectedProperty().addListener((ch, old, selected) -> {
            errorRenderer.setAllowNaNs(selected);
            chart.invalidate();
        });
        pane.addToParameterPane("Allow NaN: ", allowNaNs);

        return pane;
    }

    public TimerTask getTimerTask(final XYChart chart) {
        return new TimerTask() {
            private int updateCount;

            @Override
            public void run() {
                generateData(chart);

                if (updateCount % 10 == 0 && LOGGER.isDebugEnabled()) {
                    LOGGER.atDebug().addArgument(updateCount).log("update iteration #{}");
                }
                updateCount++;
            }
        };
    }

    @Override
    public Node getChartPanel(final Stage primaryStage) {
        ProcessingProfiler.setVerboseOutputState(false);
        ProcessingProfiler.setLoggerOutputState(true);
        ProcessingProfiler.setDebugState(false);

        final BorderPane root = new BorderPane();

        final DefaultNumericAxis xAxis = new DefaultNumericAxis();
        xAxis.setUnit("Largeness");
        // xAxis.setSide(Side.CENTER_HOR);
        xAxis.setMouseTransparent(true);
        final DefaultNumericAxis yAxis = new DefaultNumericAxis();
        yAxis.setUnit("Coolness");
        final XYChart chart = new XYChart(xAxis, yAxis);
        chart.getXAxis().setName("x axis");
        chart.getYAxis().setName("y axis");
        chart.legendVisibleProperty().set(true);
        // set them false to make the plot faster
        chart.setAnimated(false);
        final ErrorDataSetRenderer errorRenderer = new ErrorDataSetRenderer();
        chart.getRenderers().set(0, errorRenderer);
        chart.getPlugins().add(new Zoomer());
        chart.getPlugins().add(new EditAxis());
        chart.getPlugins().add(new DataPointTooltip());
        chart.getPlugins().add(new TableViewer());
        chart.getPlugins().add(new BenchPlugin());

        // yAxis.lookup(".axis-label")
        // .setStyle("-fx-label-padding: +10 0 +10 0;");

        final HBox headerBar = getHeaderBar(chart);

        final Label sampleIndicator = new Label();
        sampleIndicator.setText(String.valueOf(nSamples));
        final Label actualSampleIndicator = new Label();
        final Slider nSampleSlider = new Slider(10, N_MAX_SAMPLES, nSamples);
        nSampleSlider.setShowTickMarks(true);
        nSampleSlider.setMajorTickUnit(200);
        nSampleSlider.setMinorTickCount(20);
        nSampleSlider.setBlockIncrement(1);
        HBox.setHgrow(nSampleSlider, Priority.ALWAYS);
        nSampleSlider.valueProperty().addListener((ch, old, n) -> {
            nSamples = n.intValue();
            sampleIndicator.setText(String.valueOf(nSamples));
            generateData(chart);
        });
        errorTypeCombo = new ComboBox<>(FXCollections.observableList(List.of(ErrorTestDataSet.ErrorType.values())));
        errorTypeCombo.getSelectionModel().select(0);
        errorTypeCombo.setDisable(true);
        errorTypeCombo.valueProperty().addListener((ch, old, n) -> generateData(chart));

        final HBox hBoxSlider = new HBox(new Label("Number of Samples:"), nSampleSlider, sampleIndicator,
                actualSampleIndicator, errorTypeCombo);
        root.setTop(new VBox(headerBar, hBoxSlider));

        final Button newDataSet = new Button("new DataSet");
        newDataSet.setOnAction(evt -> Platform.runLater(getTimerTask(chart)));

        final Button startTimer = new Button(START_TIMER);
        startTimer.setOnAction(evt -> {
            if (timer == null) {
                startTimer.setText(STOP_TIMER);
                timer = new Timer(true);
                timer.scheduleAtFixedRate(getTimerTask(chart), ErrorDataSetRendererStylingSample.UPDATE_DELAY,
                        ErrorDataSetRendererStylingSample.UPDATE_PERIOD);
            } else {
                startTimer.setText(START_TIMER);
                timer.cancel();
                timer = null; // NOPMD
            }
        });

        // organise parameter config according to tabs
        final TabPane tabPane = new TabPane();

        tabPane.getTabs().add(getRendererTab(chart, errorRenderer));
        tabPane.getTabs().add(getAxisTab("x-Axis", chart, xAxis));
        tabPane.getTabs().add(getAxisTab("y-Axis", chart, yAxis));
        tabPane.getTabs().add(getChartTab(chart));

        root.setLeft(tabPane);

        generateData(chart);

        long startTime = ProcessingProfiler.getTimeStamp();

        ProcessingProfiler.getTimeDiff(startTime, "adding data to chart");

        startTime = ProcessingProfiler.getTimeStamp();

        root.setCenter(chart);
        ProcessingProfiler.getTimeDiff(startTime, "adding chart into StackPane");

        startTime = ProcessingProfiler.getTimeStamp();
        ProcessingProfiler.getTimeDiff(startTime, "for showing");
        return root;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(final String[] args) {
        Application.launch(args);
    }

    public enum DataSetType {
        RANDOM_WALK,
        OUTLIER,
        STEP,
        SINC,
        GAUSS,
        SINE,
        COSINE,
        ERROR_TYPE_TEST,
        MIX_TRIGONOMETRIC
    }

    private static class ParameterTab extends Tab {
        private final GridPane parameterGrid = new GridPane();
        private int rowIndex;

        public ParameterTab(final String tabName) {
            super(tabName);
            setClosable(false);
            setContent(parameterGrid);
        }

        public void addToParameterPane(final String label, final Node node) {
            parameterGrid.add(new Label(label), 0, rowIndex);
            if (node != null) {
                parameterGrid.add(node, 1, rowIndex);
            }
            rowIndex++;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy