Search in sources :

Example 1 with ParameterPanelFX

use of qupath.lib.gui.dialogs.ParameterPanelFX in project qupath by qupath.

the class EstimateStainVectorsCommand method showStainEditor.

@SuppressWarnings("unchecked")
public static ColorDeconvolutionStains showStainEditor(final BufferedImage img, final ColorDeconvolutionStains stains) {
    // 
    int[] buf = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth());
    // int[] rgb = buf;
    int[] rgb = EstimateStainVectors.subsample(buf, 10000);
    float[] red = ColorDeconvolutionHelper.getRedOpticalDensities(rgb, stains.getMaxRed(), null);
    float[] green = ColorDeconvolutionHelper.getGreenOpticalDensities(rgb, stains.getMaxGreen(), null);
    float[] blue = ColorDeconvolutionHelper.getBlueOpticalDensities(rgb, stains.getMaxBlue(), null);
    // panelPlots.setBorder(BorderFactory.createTitledBorder(null, "Stain vector scatterplots", TitledBorder.CENTER, TitledBorder.TOP));
    StainsWrapper stainsWrapper = new StainsWrapper(stains);
    Node panelRedGreen = createScatterPanel(new ScatterPlot(red, green, null, rgb), stainsWrapper, AxisColor.RED, AxisColor.GREEN);
    Node panelRedBlue = createScatterPanel(new ScatterPlot(red, blue, null, rgb), stainsWrapper, AxisColor.RED, AxisColor.BLUE);
    Node panelGreenBlue = createScatterPanel(new ScatterPlot(green, blue, null, rgb), stainsWrapper, AxisColor.GREEN, AxisColor.BLUE);
    // GridPane panelPlots = PanelToolsFX.createColumnGrid(panelRedGreen, panelRedBlue, panelGreenBlue);
    GridPane panelPlots = new GridPane();
    panelPlots.setHgap(10);
    panelPlots.add(panelRedGreen, 0, 0);
    panelPlots.add(panelRedBlue, 1, 0);
    panelPlots.add(panelGreenBlue, 2, 0);
    // panelPlots.getChildren().addAll(panelRedGreen, panelRedBlue, panelGreenBlue);
    panelPlots.setPadding(new Insets(0, 0, 10, 0));
    BorderPane panelSouth = new BorderPane();
    TableView<Integer> table = new TableView<>();
    table.getItems().setAll(1, 2, 3);
    stainsWrapper.addStainListener(new StainChangeListener() {

        @Override
        public void stainChanged(StainsWrapper stainsWrapper) {
            table.refresh();
        }
    });
    TableColumn<Integer, String> colName = new TableColumn<>("Name");
    colName.setCellValueFactory(v -> new SimpleStringProperty(stainsWrapper.getStains().getStain(v.getValue()).getName()));
    TableColumn<Integer, String> colOrig = new TableColumn<>("Original");
    colOrig.setCellValueFactory(v -> new SimpleStringProperty(stainArrayAsString(Locale.getDefault(Category.FORMAT), stainsWrapper.getOriginalStains().getStain(v.getValue()), " | ", 3)));
    TableColumn<Integer, String> colCurrent = new TableColumn<>("Current");
    colCurrent.setCellValueFactory(v -> new SimpleStringProperty(stainArrayAsString(Locale.getDefault(Category.FORMAT), stainsWrapper.getStains().getStain(v.getValue()), " | ", 3)));
    TableColumn<Integer, String> colAngle = new TableColumn<>("Angle");
    colAngle.setCellValueFactory(v -> {
        return new SimpleStringProperty(GeneralTools.formatNumber(StainVector.computeAngle(stainsWrapper.getOriginalStains().getStain(v.getValue()), stainsWrapper.getStains().getStain(v.getValue())), 2));
    });
    // new SimpleStringProperty(stainsWrapper.getStains().getStain(v.getValue()).arrayAsString(", ", 3)));
    table.getColumns().addAll(colName, colOrig, colCurrent, colAngle);
    table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
    table.setPrefHeight(120);
    // // Fix first & preferred column sizes
    // int widthName = 0, widthStain = 0;
    // for (int row = 0; row < table.getRowCount(); row++) {
    // TableCellRenderer renderer = table.getCellRenderer(row, 0);
    // Component comp = table.prepareRenderer(renderer, row, 0);
    // widthName = Math.max(comp.getPreferredSize().width, widthName);
    // 
    // renderer = table.getCellRenderer(row, 1);
    // comp = table.prepareRenderer(renderer, row, 1);
    // widthStain = Math.max(comp.getPreferredSize().width, widthStain);
    // renderer = table.getCellRenderer(row, 2);
    // comp = table.prepareRenderer(renderer, row, 2);
    // widthStain = Math.max(comp.getPreferredSize().width, widthStain);
    // }
    // table.getColumnModel().getColumn(0).setMaxWidth(widthName + 10);
    // table.getColumnModel().getColumn(0).setPreferredWidth(widthName + 10);
    // table.getColumnModel().getColumn(1).setPreferredWidth(widthStain + 20);
    // table.getColumnModel().getColumn(2).setPreferredWidth(widthStain + 20);
    // Create auto detection parameters
    ParameterList params = new ParameterList().addDoubleParameter("minStainOD", "Min channel OD", 0.05, "", "Minimum staining OD - pixels with a lower OD in any channel (RGB) are ignored (default = 0.05)").addDoubleParameter("maxStainOD", "Max total OD", 1., "", "Maximum staining OD - more densely stained pixels are ignored (default = 1)").addDoubleParameter("ignorePercentage", "Ignore extrema", 1., "%", "Percentage of extreme pixels to ignore, to improve robustness in the presence of noise/other artefacts (default = 1)").addBooleanParameter("checkColors", "Exclude unrecognised colors (H&E only)", false, "Exclude unexpected colors (e.g. green) that are likely to be caused by artefacts and not true staining");
    // .addDoubleParameter("ignorePercentage", "Ignore extrema", 1., "%", 0, 20, "Percentage of extreme pixels to ignore, to improve robustness in the presence of noise/other artefacts");
    Button btnAuto = new Button("Auto");
    btnAuto.setOnAction(e -> {
        double minOD = params.getDoubleParameterValue("minStainOD");
        double maxOD = params.getDoubleParameterValue("maxStainOD");
        double ignore = params.getDoubleParameterValue("ignorePercentage");
        // Only accept if H&E
        boolean checkColors = params.getBooleanParameterValue("checkColors") && stainsWrapper.getOriginalStains().isH_E();
        ignore = Math.max(0, Math.min(ignore, 100));
        // ColorDeconvolutionStains stains = estimateStains(imgFinal, stainsWrapper.getStains(), minOD, maxOD, ignore);
        try {
            ColorDeconvolutionStains stainsNew = EstimateStainVectors.estimateStains(img, stainsWrapper.getStains(), minOD, maxOD, ignore, checkColors);
            stainsWrapper.setStains(stainsNew);
        } catch (Exception e2) {
            Dialogs.showErrorMessage("Estimate stain vectors", e2);
        }
    });
    ParameterPanelFX panelParams = new ParameterPanelFX(params);
    // panelParams.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    BorderPane panelAuto = new BorderPane();
    // panelAuto.setBorder(BorderFactory.createTitledBorder("Auto detect"));
    panelAuto.setCenter(panelParams.getPane());
    panelAuto.setBottom(btnAuto);
    // JScrollPane scrollPane = new JScrollPane(table);
    // JPanel panelTable = new JPanel(new BorderLayout());
    // panelTable.add(scrollPane, BorderLayout.CENTER);
    // //		JTextArea textInstructions = new JTextArea();
    // //		textInstructions.setWrapStyleWord(true);
    // //		textInstructions.setLineWrap(true);
    // //		textInstructions.setText(
    // //				"Viewer for manually and automatically adjusting stain vectors used for stain separation.\n\n" +
    // //				"Each stain vector is 3 values describing the red, green and blue components that define the colour of each " +
    // //				"stain (e.g. hematoxylin, DAB, eosin).  The scatterplots show how these relate to pixel colours for each " +
    // //				"combination of red, green and blue.\n\n" +
    // //				"'Good' stain vectors should point along the edges of the scattered points, ignoring any artefacts resulting from " +
    // //				"pixels that don't belong to normal staining patterns."
    // //				);
    // //		panelTable.add(new JScrollPane(textInstructions), BorderLayout.SOUTH);
    // panelTable.setBorder(BorderFactory.createTitledBorder("Stain vectors"));
    panelSouth.setCenter(new TitledPane("Stain vectors", table));
    panelSouth.setBottom(new TitledPane("Auto detect", panelAuto));
    BorderPane panelMain = new BorderPane();
    panelMain.setCenter(panelPlots);
    panelMain.setBottom(panelSouth);
    if (Dialogs.showConfirmDialog("Visual Stain Editor", panelMain)) {
        return stainsWrapper.getStains();
    } else {
        stainsWrapper.resetStains();
        return stainsWrapper.getStains();
    }
}
Also used : BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) Node(javafx.scene.Node) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) Button(javafx.scene.control.Button) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) TableView(javafx.scene.control.TableView) TitledPane(javafx.scene.control.TitledPane) GridPane(javafx.scene.layout.GridPane) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) TableColumn(javafx.scene.control.TableColumn) IOException(java.io.IOException) ParameterList(qupath.lib.plugins.parameters.ParameterList) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains)

Example 2 with ParameterPanelFX

use of qupath.lib.gui.dialogs.ParameterPanelFX in project qupath by qupath.

the class KaplanMeierDisplay method generatePlot.

@SuppressWarnings("unchecked")
private void generatePlot() {
    KaplanMeierDisplay.ScoreData newScoreData = scoreData;
    // If we have a hierarchy, update the scores with the most recent data
    if (hierarchy != null) {
        List<TMACoreObject> cores = PathObjectTools.getTMACoreObjects(hierarchy, false);
        double[] survival = new double[cores.size()];
        boolean[] censored = new boolean[cores.size()];
        double[] scores = new double[cores.size()];
        // scoreColumn = "RoughScore";
        for (int i = 0; i < cores.size(); i++) {
            TMACoreObject core = cores.get(i);
            MeasurementList ml = core.getMeasurementList();
            survival[i] = core.getMeasurementList().getMeasurementValue(survivalColumn);
            double censoredValue = core.getMeasurementList().getMeasurementValue(censoredColumn);
            boolean hasCensoredValue = !Double.isNaN(censoredValue) && (censoredValue == 0 || censoredValue == 1);
            censored[i] = censoredValue != 0;
            if (!hasCensoredValue) {
                // If we don't have a censored value, ensure we mask out everything else
                scores[i] = Double.NaN;
                survival[i] = Double.NaN;
            } else if (ml.containsNamedMeasurement(scoreColumn))
                // Get the score if we can
                scores[i] = ml.getMeasurementValue(scoreColumn);
            else {
                // // Try to compute score if we need to
                // Map<String, Number> map = ROIMeaningfulMeasurements.getPathClassSummaryMeasurements(core.getChildObjects(), true);
                // Number value = map.get(scoreColumn);
                // if (value == null)
                scores[i] = Double.NaN;
            // else
            // scores[i] = value.doubleValue();
            }
        }
        // Mask out any scores that don't have associated survival data
        for (int i = 0; i < survival.length; i++) {
            if (Double.isNaN(survival[i]))
                scores[i] = Double.NaN;
        }
        newScoreData = new ScoreData(scores, survival, censored);
    }
    if (newScoreData == null || newScoreData.scores.length == 0)
        return;
    // KaplanMeier kmHigh = new KaplanMeier("Above threshold");
    // KaplanMeier kmLow = new KaplanMeier("Below threshold");
    double[] quartiles = StatisticsHelper.getQuartiles(newScoreData.scores);
    double q1 = quartiles[0];
    double median = quartiles[1];
    double q3 = quartiles[2];
    double[] thresholds;
    if (params != null) {
        Object thresholdMethod = params.getChoiceParameterValue("scoreThresholdMethod");
        if (thresholdMethod.equals("Median")) {
            // panelParams.setNumericParameterValue("scoreThreshold", median);
            // ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
            thresholds = new double[] { median };
        } else if (thresholdMethod.equals("Tertiles")) {
            // ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
            thresholds = StatisticsHelper.getTertiles(newScoreData.scores);
        } else if (thresholdMethod.equals("Quartiles")) {
            // ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
            thresholds = new double[] { q1, median, q3 };
        } else if (thresholdMethod.equals("Manual (1)")) {
            thresholds = new double[] { params.getDoubleParameterValue("threshold1") };
        } else if (thresholdMethod.equals("Manual (2)")) {
            thresholds = new double[] { params.getDoubleParameterValue("threshold1"), params.getDoubleParameterValue("threshold2") };
        } else
            // if (thresholdMethod.equals("Manual (3)")) {
            thresholds = new double[] { params.getDoubleParameterValue("threshold1"), params.getDoubleParameterValue("threshold2"), params.getDoubleParameterValue("threshold3") };
    } else
        thresholds = new double[] { median };
    double minVal = Double.POSITIVE_INFINITY;
    double maxVal = Double.NEGATIVE_INFINITY;
    int numNonNaN = 0;
    for (double d : newScoreData.scores) {
        if (Double.isNaN(d))
            continue;
        if (d < minVal)
            minVal = d;
        if (d > maxVal)
            maxVal = d;
        numNonNaN++;
    }
    // If not this, we don't have valid scores that we can work with
    boolean scoresValid = maxVal > minVal;
    double maxTimePoint = 0;
    for (double d : newScoreData.survival) {
        if (Double.isNaN(d))
            continue;
        if (d > maxTimePoint)
            maxTimePoint = d;
    }
    if (panelParams != null && maxTimePoint > ((IntParameter) params.getParameters().get("censorTimePoints")).getUpperBound()) {
        panelParams.setNumericParameterValueRange("censorTimePoints", 0, Math.ceil(maxTimePoint));
    }
    // Optionally censor at specified time
    double censorThreshold = params == null ? maxTimePoint : params.getIntParameterValue("censorTimePoints");
    // Compute log-rank p-values for *all* possible thresholds
    // Simultaneously determine the threshold that yields the lowest p-value,
    // resolving ties in favour of a more even split between high/low numbers of events
    boolean pValuesChanged = false;
    if (calculateAllPValues) {
        if (!(pValues != null && pValueThresholds != null && newScoreData.equals(scoreData) && censorThreshold == lastPValueCensorThreshold)) {
            Map<Double, Double> mapLogRank = new TreeMap<>();
            Set<Double> setObserved = new HashSet<>();
            for (int i = 0; i < newScoreData.scores.length; i++) {
                Double d = newScoreData.scores[i];
                boolean observed = !newScoreData.censored[i] && newScoreData.survival[i] < censorThreshold;
                if (observed)
                    setObserved.add(d);
                if (mapLogRank.containsKey(d))
                    continue;
                List<KaplanMeierData> kmsTemp = splitByThresholds(newScoreData, new double[] { d }, censorThreshold, false);
                // if (kmsTemp.get(1).nObserved() == 0 || kmsTemp.get(1).nObserved() == 0)
                // continue;
                LogRankResult test = LogRankTest.computeLogRankTest(kmsTemp.get(0), kmsTemp.get(1));
                double pValue = test.getPValue();
                // double pValue = test.hazardRatio < 1 ? test.hazardRatio : 1.0/test.hazardRatio; // Checking usefulness of Hazard ratios...
                if (!Double.isFinite(pValue))
                    continue;
                // if (!Double.isFinite(test.getHazardRatio())) {
                // //						continue;
                // pValue = Double.NaN;
                // }
                mapLogRank.put(d, pValue);
            }
            pValueThresholds = new double[mapLogRank.size()];
            pValues = new double[mapLogRank.size()];
            pValueThresholdsObserved = new boolean[mapLogRank.size()];
            int count = 0;
            for (Entry<Double, Double> entry : mapLogRank.entrySet()) {
                pValueThresholds[count] = entry.getKey();
                pValues[count] = entry.getValue();
                if (setObserved.contains(entry.getKey()))
                    pValueThresholdsObserved[count] = true;
                count++;
            }
            // Find the longest 'significant' stretch
            int maxSigCount = 0;
            int maxSigInd = -1;
            int sigCurrent = 0;
            int[] sigCount = new int[pValues.length];
            for (int i = 0; i < pValues.length; i++) {
                if (pValues[i] < 0.05) {
                    sigCurrent++;
                    sigCount[i] = sigCurrent;
                    if (sigCurrent > maxSigCount) {
                        maxSigCount = sigCurrent;
                        maxSigInd = i;
                    }
                } else
                    sigCurrent = 0;
            }
            if (maxSigCount == 0) {
                logger.info("No p-values < 0.05");
            } else {
                double minThresh = maxSigInd - maxSigCount < 0 ? pValueThresholds[0] - 0.0000001 : pValueThresholds[maxSigInd - maxSigCount];
                double maxThresh = pValueThresholds[maxSigInd];
                int nBetween = 0;
                int nBetweenObserved = 0;
                for (int i = 0; i < newScoreData.scores.length; i++) {
                    if (newScoreData.scores[i] > minThresh && newScoreData.scores[i] <= maxThresh) {
                        nBetween++;
                        if (newScoreData.survival[i] < censorThreshold && !newScoreData.censored[i])
                            nBetweenObserved++;
                    }
                }
                logger.info("Longest stretch of p-values < 0.05: {} - {} ({} entries, {} observed)", minThresh, maxThresh, nBetween, nBetweenObserved);
            }
            pValuesSmoothed = new double[pValues.length];
            Arrays.fill(pValuesSmoothed, Double.NaN);
            int n = (pValues.length / 20) * 2 + 1;
            logger.info("Smoothing log-rank test p-values by " + n);
            for (int i = n / 2; i < pValues.length - n / 2; i++) {
                double sum = 0;
                for (int k = i - n / 2; k < i - n / 2 + n; k++) {
                    sum += pValues[k];
                }
                pValuesSmoothed[i] = sum / n;
            }
            // for (int i = 0; i < pValues.length; i++) {
            // double sum = 0;
            // for (int k = Math.max(0, i-n/2); k < Math.min(pValues.length, i-n/2+n); k++) {
            // sum += pValues[k];
            // }
            // pValuesSmoothed[i] = sum/n;
            // }
            // pValues = pValuesSmoothed;
            lastPValueCensorThreshold = censorThreshold;
            pValuesChanged = true;
        }
    } else {
        lastPValueCensorThreshold = Double.NaN;
        pValueThresholds = null;
        pValues = null;
    }
    // if (params != null && !Double.isNaN(bestThreshold) && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value")))
    if (params != null && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value"))) {
        int bestIdx = -1;
        double bestPValue = Double.POSITIVE_INFINITY;
        for (int i = pValueThresholds.length / 10; i < pValueThresholds.length * 9 / 10; i++) {
            if (pValues[i] < bestPValue) {
                bestIdx = i;
                bestPValue = pValues[i];
            }
        }
        thresholds = bestIdx >= 0 ? new double[] { pValueThresholds[bestIdx] } : new double[0];
    } else if (params != null && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest smoothed p-value"))) {
        int bestIdx = -1;
        double bestPValue = Double.POSITIVE_INFINITY;
        for (int i = pValueThresholds.length / 10; i < pValueThresholds.length * 9 / 10; i++) {
            if (pValuesSmoothed[i] < bestPValue) {
                bestIdx = i;
                bestPValue = pValuesSmoothed[i];
            }
        }
        thresholds = bestIdx >= 0 ? new double[] { pValueThresholds[bestIdx] } : new double[0];
    }
    // Split into different curves using the provided thresholds
    List<KaplanMeierData> kms = splitByThresholds(newScoreData, thresholds, censorThreshold, params != null && "Quartiles".equals(params.getChoiceParameterValue("scoreThresholdMethod")));
    if (plotter == null) {
        plotter = new KaplanMeierChartWrapper(survivalColumn + " time");
    // plotter.setBorder(BorderFactory.createTitledBorder("Survival plot"));
    // plotter.getCanvas().setWidth(300);
    // plotter.getCanvas().setHeight(300);
    }
    KaplanMeierData[] kmArray = new KaplanMeierData[kms.size()];
    plotter.setKaplanMeierCurves(survivalColumn + " time", kms.toArray(kmArray));
    tableModel.setSurvivalCurves(thresholds, params != null && params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value"), kmArray);
    // Bar width determined using 'Freedman and Diaconis' rule' (but overridden if this gives < 16 bins...)
    double barWidth = (2 * q3 - q1) * Math.pow(numNonNaN, -1.0 / 3.0);
    int nBins = 100;
    if (!Double.isNaN(barWidth))
        barWidth = (int) Math.max(16, Math.ceil((maxVal - minVal) / barWidth));
    Histogram histogram = scoresValid ? new Histogram(newScoreData.scores, nBins) : null;
    if (histogramPanel == null) {
        GridPane paneHistogram = new GridPane();
        histogramPanel = new HistogramPanelFX();
        histogramPanel.getChart().setAnimated(false);
        histogramWrapper = new ThresholdedChartWrapper(histogramPanel.getChart());
        for (ObservableNumberValue val : threshProperties) histogramWrapper.addThreshold(val, ColorToolsFX.getCachedColor(240, 0, 0, 128));
        histogramWrapper.getPane().setPrefHeight(150);
        paneHistogram.add(histogramWrapper.getPane(), 0, 0);
        Tooltip.install(histogramPanel.getChart(), new Tooltip("Distribution of scores"));
        GridPane.setHgrow(histogramWrapper.getPane(), Priority.ALWAYS);
        GridPane.setVgrow(histogramWrapper.getPane(), Priority.ALWAYS);
        NumberAxis xAxis = new NumberAxis();
        xAxis.setLabel("Score threshold");
        NumberAxis yAxis = new NumberAxis();
        yAxis.setLowerBound(0);
        yAxis.setUpperBound(1);
        yAxis.setTickUnit(0.1);
        yAxis.setAutoRanging(false);
        yAxis.setLabel("P-value");
        chartPValues = new LineChart<>(xAxis, yAxis);
        chartPValues.setAnimated(false);
        chartPValues.setLegendVisible(false);
        // Make chart so it can be navigated
        ChartTools.makeChartInteractive(chartPValues, xAxis, yAxis);
        pValuesChanged = true;
        Tooltip.install(chartPValues, new Tooltip("Distribution of p-values (log-rank test) comparing low vs. high for all possible score thresholds"));
        // chartPValues.getYAxis().setAutoRanging(false);
        pValuesWrapper = new ThresholdedChartWrapper(chartPValues);
        for (ObservableNumberValue val : threshProperties) pValuesWrapper.addThreshold(val, ColorToolsFX.getCachedColor(240, 0, 0, 128));
        pValuesWrapper.getPane().setPrefHeight(150);
        paneHistogram.add(pValuesWrapper.getPane(), 0, 1);
        GridPane.setHgrow(pValuesWrapper.getPane(), Priority.ALWAYS);
        GridPane.setVgrow(pValuesWrapper.getPane(), Priority.ALWAYS);
        ContextMenu popup = new ContextMenu();
        ChartTools.addChartExportMenu(chartPValues, popup);
        RadioMenuItem miZoomY1 = new RadioMenuItem("0-1");
        miZoomY1.setOnAction(e -> {
            yAxis.setAutoRanging(false);
            yAxis.setUpperBound(1);
            yAxis.setTickUnit(0.2);
        });
        RadioMenuItem miZoomY05 = new RadioMenuItem("0-0.5");
        miZoomY05.setOnAction(e -> {
            yAxis.setAutoRanging(false);
            yAxis.setUpperBound(0.5);
            yAxis.setTickUnit(0.1);
        });
        RadioMenuItem miZoomY02 = new RadioMenuItem("0-0.2");
        miZoomY02.setOnAction(e -> {
            yAxis.setAutoRanging(false);
            yAxis.setUpperBound(0.2);
            yAxis.setTickUnit(0.05);
        });
        RadioMenuItem miZoomY01 = new RadioMenuItem("0-0.1");
        miZoomY01.setOnAction(e -> {
            yAxis.setAutoRanging(false);
            yAxis.setUpperBound(0.1);
            yAxis.setTickUnit(0.05);
        });
        RadioMenuItem miZoomY005 = new RadioMenuItem("0-0.05");
        miZoomY005.setOnAction(e -> {
            yAxis.setAutoRanging(false);
            yAxis.setUpperBound(0.05);
            yAxis.setTickUnit(0.01);
        });
        RadioMenuItem miZoomY001 = new RadioMenuItem("0-0.01");
        miZoomY001.setOnAction(e -> {
            yAxis.setAutoRanging(false);
            yAxis.setUpperBound(0.01);
            yAxis.setTickUnit(0.005);
        });
        ToggleGroup tgZoom = new ToggleGroup();
        miZoomY1.setToggleGroup(tgZoom);
        miZoomY05.setToggleGroup(tgZoom);
        miZoomY02.setToggleGroup(tgZoom);
        miZoomY01.setToggleGroup(tgZoom);
        miZoomY005.setToggleGroup(tgZoom);
        miZoomY001.setToggleGroup(tgZoom);
        Menu menuZoomY = new Menu("Set y-axis range");
        menuZoomY.getItems().addAll(miZoomY1, miZoomY05, miZoomY02, miZoomY01, miZoomY005, miZoomY001);
        MenuItem miCopyData = new MenuItem("Copy chart data");
        miCopyData.setOnAction(e -> {
            String dataString = ChartTools.getChartDataAsString(chartPValues);
            ClipboardContent content = new ClipboardContent();
            content.putString(dataString);
            Clipboard.getSystemClipboard().setContent(content);
        });
        popup.getItems().addAll(miCopyData, menuZoomY);
        chartPValues.setOnContextMenuRequested(e -> {
            popup.show(chartPValues, e.getScreenX(), e.getScreenY());
        });
        for (int col = 0; col < tableModel.getColumnCount(); col++) {
            TableColumn<Integer, String> column = new TableColumn<>(tableModel.getColumnName(col));
            int colNumber = col;
            column.setCellValueFactory(new Callback<CellDataFeatures<Integer, String>, ObservableValue<String>>() {

                @Override
                public ObservableValue<String> call(CellDataFeatures<Integer, String> p) {
                    return new SimpleStringProperty((String) tableModel.getValueAt(p.getValue(), colNumber));
                }
            });
            column.setCellFactory(new Callback<TableColumn<Integer, String>, TableCell<Integer, String>>() {

                @Override
                public TableCell<Integer, String> call(TableColumn<Integer, String> param) {
                    TableCell<Integer, String> cell = new TableCell<Integer, String>() {

                        @Override
                        protected void updateItem(String item, boolean empty) {
                            super.updateItem(item, empty);
                            setText(item);
                            setTooltip(new Tooltip(item));
                        }
                    };
                    return cell;
                }
            });
            table.getColumns().add(column);
        }
        table.setPrefHeight(250);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        table.maxHeightProperty().bind(table.prefHeightProperty());
        params = new ParameterList();
        // maxTimePoint = 0;
        // for (TMACoreObject core : hierarchy.getTMAGrid().getTMACoreList()) {
        // double os = core.getMeasurementList().getMeasurementValue(TMACoreObject.KEY_OVERALL_SURVIVAL);
        // double rfs = core.getMeasurementList().getMeasurementValue(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL);
        // if (os > maxTimePoint)
        // maxTimePoint = os;
        // if (rfs > maxTimePoint)
        // maxTimePoint = rfs;
        // }
        params.addIntParameter("censorTimePoints", "Max censored time", (int) (censorThreshold + 0.5), null, 0, (int) Math.ceil(maxTimePoint), "Latest time point beyond which data will be censored");
        // params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Manual", Arrays.asList("Manual", "Median", "Log-rank test"));
        if (calculateAllPValues)
            // Don't include "Lowest smoothed p-value" - it's not an established method and open to misinterpretation...
            params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles", "Lowest p-value"));
        else
            // params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles", "Lowest p-value", "Lowest smoothed p-value"));
            params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles"));
        params.addDoubleParameter("threshold1", "Threshold 1", thresholds.length > 0 ? thresholds[0] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
        params.addDoubleParameter("threshold2", "Threshold 2", thresholds.length > 1 ? thresholds[1] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
        params.addDoubleParameter("threshold3", "Threshold 3", thresholds.length > 2 ? thresholds[2] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
        params.addBooleanParameter("showAtRisk", "Show at risk", plotter.getShowAtRisk(), "Show number of patients at risk below the plot");
        params.addBooleanParameter("showTicks", "Show censored ticks", plotter.getShowCensoredTicks(), "Show ticks to indicate censored data");
        params.addBooleanParameter("showKey", "Show key", plotter.getShowKey(), "Show key indicating display of each curve");
        // Hide threshold parameters if threshold can't be used
        if (!scoresValid) {
            // params.setHiddenParameters(true, "scoreThresholdMethod", "scoreThreshold");
            histogramPanel.getChart().setVisible(false);
        }
        panelParams = new ParameterPanelFX(params);
        panelParams.addParameterChangeListener(this);
        updateThresholdsEnabled();
        for (int i = 0; i < threshProperties.length; i++) {
            String p = "threshold" + (i + 1);
            threshProperties[i].addListener((v, o, n) -> {
                if (interactiveThresholds()) {
                    // Need to do a decent double check with tolerance to text field value changing while typing
                    if (!GeneralTools.almostTheSame(params.getDoubleParameterValue(p), n.doubleValue(), 0.0001))
                        panelParams.setNumericParameterValue(p, n);
                }
            });
        }
        BorderPane paneBottom = new BorderPane();
        TitledPane paneOptions = new TitledPane("Options", panelParams.getPane());
        // paneOptions.setCollapsible(false);
        Pane paneCanvas = new StackPane();
        paneCanvas.getChildren().add(plotter.getCanvas());
        GridPane paneLeft = new GridPane();
        paneLeft.add(paneOptions, 0, 0);
        paneLeft.add(table, 0, 1);
        GridPane.setHgrow(paneOptions, Priority.ALWAYS);
        GridPane.setHgrow(table, Priority.ALWAYS);
        paneBottom.setLeft(paneLeft);
        paneBottom.setCenter(paneHistogram);
        paneMain.setCenter(paneCanvas);
        paneMain.setBottom(paneBottom);
        paneMain.setPadding(new Insets(10, 10, 10, 10));
    } else if (thresholds.length > 0) {
        // Ensure the sliders/text fields are set sensibly
        if (!GeneralTools.almostTheSame(thresholds[0], params.getDoubleParameterValue("threshold1"), 0.0001)) {
            panelParams.setNumericParameterValue("threshold1", thresholds[0]);
        }
        if (thresholds.length > 1 && !GeneralTools.almostTheSame(thresholds[1], params.getDoubleParameterValue("threshold2"), 0.0001)) {
            panelParams.setNumericParameterValue("threshold2", thresholds[1]);
        }
        if (thresholds.length > 2 && !GeneralTools.almostTheSame(thresholds[2], params.getDoubleParameterValue("threshold3"), 0.0001)) {
            panelParams.setNumericParameterValue("threshold3", thresholds[2]);
        }
    }
    if (histogram != null) {
        histogramPanel.getHistogramData().setAll(HistogramPanelFX.createHistogramData(histogram, false, (Color) null));
        histogramPanel.getChart().getXAxis().setLabel(scoreColumn);
        histogramPanel.getChart().getYAxis().setLabel("Count");
        ChartTools.addChartExportMenu(histogramPanel.getChart(), null);
    // histogramWrapper.setVerticalLines(thresholds, ColorToolsFX.getCachedColor(240, 0, 0, 128));
    // Deal with threshold adjustment
    // histogramWrapper.getThresholds().addListener((Observable o) -> generatePlot());
    }
    if (pValues != null) {
        // TODO: Raise earlier where p-value calculation is
        if (pValuesChanged) {
            ObservableList<XYChart.Data<Number, Number>> data = FXCollections.observableArrayList();
            for (int i = 0; i < pValueThresholds.length; i++) {
                double pValue = pValues[i];
                if (Double.isNaN(pValue))
                    continue;
                data.add(new XYChart.Data<>(pValueThresholds[i], pValue, pValueThresholdsObserved[i]));
            }
            ObservableList<XYChart.Data<Number, Number>> dataSmoothed = null;
            if (pValuesSmoothed != null) {
                dataSmoothed = FXCollections.observableArrayList();
                for (int i = 0; i < pValueThresholds.length; i++) {
                    double pValueSmoothed = pValuesSmoothed[i];
                    if (Double.isNaN(pValueSmoothed))
                        continue;
                    dataSmoothed.add(new XYChart.Data<>(pValueThresholds[i], pValueSmoothed));
                }
            }
            // Don't bother showing the smoothed data... it tends to get in the way...
            // if (dataSmoothed != null)
            // chartPValues.getData().setAll(new XYChart.Series<>("P-values", data), new XYChart.Series<>("Smoothed P-values", dataSmoothed));
            // else
            chartPValues.getData().setAll(new XYChart.Series<>("P-values", data));
            // Add line to show 0.05 significance threshold
            if (pValueThresholds.length > 1) {
                Data<Number, Number> sigData1 = new Data<>(pValueThresholds[0], 0.05);
                Data<Number, Number> sigData2 = new Data<>(pValueThresholds[pValueThresholds.length - 1], 0.05);
                XYChart.Series<Number, Number> dataSignificant = new XYChart.Series<>("Significance 0.05", FXCollections.observableArrayList(sigData1, sigData2));
                chartPValues.getData().add(dataSignificant);
                sigData1.getNode().setVisible(false);
                sigData2.getNode().setVisible(false);
            }
            // pValuesWrapper.clearThresholds();
            for (XYChart.Data<Number, Number> dataPoint : data) {
                if (!Boolean.TRUE.equals(dataPoint.getExtraValue()))
                    dataPoint.getNode().setVisible(false);
            }
        // if (dataSmoothed != null) {
        // for (XYChart.Data<Number, Number> dataPoint : dataSmoothed) {
        // dataPoint.getNode().setVisible(false);
        // }
        // chartPValues.getData().get(1).getNode().setOpacity(0.5);
        // }
        // int count = 0;
        // for (int i = 0; i < pValueThresholds.length; i++) {
        // double pValue = pValues[i];
        // if (Double.isNaN(pValue))
        // continue;
        // boolean observed = pValueThresholdsObserved[i];
        // //						if (observed)
        // //							pValuesWrapper.addThreshold(new ReadOnlyDoubleWrapper(pValueThresholds[i]), Color.rgb(0, 0, 0, 0.05));
        // 
        // if (!observed) {
        // //							StackPane pane = (StackPane)data.get(count).getNode();
        // //							pane.setEffect(new DropShadow());
        // data.get(count).getNode().setVisible(false);
        // }
        // count++;
        // }
        }
        for (int i = 0; i < threshProperties.length; i++) {
            if (i < thresholds.length)
                threshProperties[i].set(thresholds[i]);
            else
                threshProperties[i].set(Double.NaN);
        }
        boolean isInteractive = interactiveThresholds();
        histogramWrapper.setIsInteractive(isInteractive);
        pValuesWrapper.setIsInteractive(isInteractive);
        chartPValues.setVisible(true);
    }
    // else
    // chartPValues.setVisible(false);
    // Store values for next time
    scoreData = newScoreData;
}
Also used : Histogram(qupath.lib.analysis.stats.Histogram) CellDataFeatures(javafx.scene.control.TableColumn.CellDataFeatures) ObservableValue(javafx.beans.value.ObservableValue) ThresholdedChartWrapper(qupath.lib.gui.charts.HistogramPanelFX.ThresholdedChartWrapper) StackPane(javafx.scene.layout.StackPane) HashSet(java.util.HashSet) ObservableNumberValue(javafx.beans.value.ObservableNumberValue) GridPane(javafx.scene.layout.GridPane) TMACoreObject(qupath.lib.objects.TMACoreObject) Tooltip(javafx.scene.control.Tooltip) Color(javafx.scene.paint.Color) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) TableColumn(javafx.scene.control.TableColumn) StackPane(javafx.scene.layout.StackPane) Pane(javafx.scene.layout.Pane) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) TitledPane(javafx.scene.control.TitledPane) ParameterList(qupath.lib.plugins.parameters.ParameterList) XYChart(javafx.scene.chart.XYChart) TMACoreObject(qupath.lib.objects.TMACoreObject) HistogramPanelFX(qupath.lib.gui.charts.HistogramPanelFX) LogRankResult(qupath.lib.analysis.stats.survival.LogRankTest.LogRankResult) IntParameter(qupath.lib.plugins.parameters.IntParameter) BorderPane(javafx.scene.layout.BorderPane) NumberAxis(javafx.scene.chart.NumberAxis) Insets(javafx.geometry.Insets) ClipboardContent(javafx.scene.input.ClipboardContent) MeasurementList(qupath.lib.measurements.MeasurementList) ContextMenu(javafx.scene.control.ContextMenu) RadioMenuItem(javafx.scene.control.RadioMenuItem) KaplanMeierData(qupath.lib.analysis.stats.survival.KaplanMeierData) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) TableCell(javafx.scene.control.TableCell) ContextMenu(javafx.scene.control.ContextMenu) Menu(javafx.scene.control.Menu) TitledPane(javafx.scene.control.TitledPane) MenuItem(javafx.scene.control.MenuItem) RadioMenuItem(javafx.scene.control.RadioMenuItem) KaplanMeierData(qupath.lib.analysis.stats.survival.KaplanMeierData) Data(javafx.scene.chart.XYChart.Data) TreeMap(java.util.TreeMap) ToggleGroup(javafx.scene.control.ToggleGroup)

Example 3 with ParameterPanelFX

use of qupath.lib.gui.dialogs.ParameterPanelFX in project qupath by qupath.

the class ImageJMacroRunner method runPlugin.

@Override
public boolean runPlugin(final PluginRunner<BufferedImage> runner, final String arg) {
    if (!parseArgument(runner.getImageData(), arg))
        return false;
    if (dialog == null) {
        dialog = new Stage();
        dialog.initOwner(qupath.getStage());
        dialog.setTitle("ImageJ macro runner");
        BorderPane pane = new BorderPane();
        if (arg != null)
            macroText = arg;
        // Create text area
        final TextArea textArea = new TextArea();
        textArea.setPrefRowCount(12);
        textArea.setPrefSize(400, 400);
        textArea.setWrapText(true);
        textArea.setFont(Font.font("Courier"));
        if (macroText != null)
            textArea.setText(macroText);
        BorderPane panelMacro = new BorderPane();
        // panelMacro.setBorder(BorderFactory.createTitledBorder("Macro"));
        panelMacro.setCenter(textArea);
        ParameterPanelFX parameterPanel = new ParameterPanelFX(getParameterList(runner.getImageData()));
        panelMacro.setBottom(parameterPanel.getPane());
        // Create button panel
        Button btnRun = new Button("Run");
        btnRun.setOnAction(e -> {
            macroText = textArea.getText().trim();
            if (macroText.length() == 0)
                return;
            PathObjectHierarchy hierarchy = getHierarchy(runner);
            PathObject pathObject = hierarchy.getSelectionModel().singleSelection() ? hierarchy.getSelectionModel().getSelectedObject() : null;
            if (pathObject instanceof PathAnnotationObject || pathObject instanceof TMACoreObject) {
                SwingUtilities.invokeLater(() -> {
                    runMacro(params, qupath.getViewer().getImageData(), qupath.getViewer().getImageDisplay(), pathObject, macroText);
                });
            } else {
                // DisplayHelpers.showErrorMessage(getClass().getSimpleName(), "Sorry, ImageJ macros can only be run for single selected images");
                // logger.warn("ImageJ macro being run in current thread");
                // runPlugin(runner, arg); // TODO: Consider running in a background thread?
                // Run in a background thread
                Collection<? extends PathObject> parents = getParentObjects(runner);
                if (parents.isEmpty()) {
                    Dialogs.showErrorMessage("ImageJ macro runner", "No annotation or TMA core objects selected!");
                    return;
                }
                List<Runnable> tasks = new ArrayList<>();
                for (PathObject parent : parents) addRunnableTasks(qupath.getViewer().getImageData(), parent, tasks);
                qupath.submitShortTask(() -> runner.runTasks(tasks, true));
            // runner.runTasks(tasks);
            // Runnable r = new Runnable() {
            // public void run() {
            // runPlugin(runner, arg);
            // }
            // };
            // new Thread(r).start();
            }
        });
        Button btnClose = new Button("Close");
        btnClose.setOnAction(e -> dialog.hide());
        GridPane panelButtons = PaneTools.createRowGridControls(btnRun, btnClose);
        pane.setCenter(panelMacro);
        pane.setBottom(panelButtons);
        panelButtons.setPadding(new Insets(5, 0, 0, 0));
        pane.setPadding(new Insets(10, 10, 10, 10));
        dialog.setScene(new Scene(pane));
    }
    dialog.show();
    return true;
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) Insets(javafx.geometry.Insets) TextArea(javafx.scene.control.TextArea) TMACoreObject(qupath.lib.objects.TMACoreObject) ArrayList(java.util.ArrayList) Scene(javafx.scene.Scene) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Button(javafx.scene.control.Button) Stage(javafx.stage.Stage)

Example 4 with ParameterPanelFX

use of qupath.lib.gui.dialogs.ParameterPanelFX in project qupath by qupath.

the class ImageDetailsPane method editStainVector.

void editStainVector(Object value) {
    if (!(value instanceof StainVector || value instanceof double[]))
        return;
    // JOptionPane.showMessageDialog(null, "Modifying stain vectors not yet implemented...");
    ImageData<BufferedImage> imageData = qupath.getImageData();
    if (imageData == null)
        return;
    ColorDeconvolutionStains stains = imageData.getColorDeconvolutionStains();
    // Default to background values
    int num = -1;
    String name = null;
    String message = null;
    if (value instanceof StainVector) {
        StainVector stainVector = (StainVector) value;
        if (stainVector.isResidual() && imageData.getImageType() != ImageType.BRIGHTFIELD_OTHER) {
            logger.warn("Cannot set residual stain vector - this is computed from the known vectors");
            return;
        }
        num = stains.getStainNumber(stainVector);
        if (num <= 0) {
            logger.error("Could not identify stain vector " + stainVector + " inside " + stains);
            return;
        }
        name = stainVector.getName();
        message = "Set stain vector from ROI?";
    } else
        message = "Set color deconvolution background values from ROI?";
    ROI pathROI = imageData.getHierarchy().getSelectionModel().getSelectedROI();
    boolean wasChanged = false;
    String warningMessage = null;
    boolean editableName = imageData.getImageType() == ImageType.BRIGHTFIELD_OTHER;
    if (pathROI != null) {
        if ((pathROI instanceof RectangleROI) && !pathROI.isEmpty() && ((RectangleROI) pathROI).getArea() < 500 * 500) {
            if (Dialogs.showYesNoDialog("Color deconvolution stains", message)) {
                ImageServer<BufferedImage> server = imageData.getServer();
                BufferedImage img = null;
                try {
                    img = server.readBufferedImage(RegionRequest.createInstance(server.getPath(), 1, pathROI));
                } catch (IOException e) {
                    Dialogs.showErrorMessage("Set stain vector", "Unable to read image region");
                    logger.error("Unable to read region", e);
                }
                int rgb = ColorDeconvolutionHelper.getMedianRGB(img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()));
                if (num >= 0) {
                    StainVector vectorValue = ColorDeconvolutionHelper.generateMedianStainVectorFromPixels(name, img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()), stains.getMaxRed(), stains.getMaxGreen(), stains.getMaxBlue());
                    if (!Double.isFinite(vectorValue.getRed() + vectorValue.getGreen() + vectorValue.getBlue())) {
                        Dialogs.showErrorMessage("Set stain vector", "Cannot set stains for the current ROI!\n" + "It might be too close to the background color.");
                        return;
                    }
                    value = vectorValue;
                } else {
                    // Update the background
                    value = new double[] { ColorTools.red(rgb), ColorTools.green(rgb), ColorTools.blue(rgb) };
                }
                wasChanged = true;
            }
        } else {
            warningMessage = "Note: To set stain values from an image region, draw a small, rectangular ROI first";
        }
    }
    // Prompt to set the name / verify stains
    ParameterList params = new ParameterList();
    String title;
    String nameBefore = null;
    String valuesBefore = null;
    String collectiveNameBefore = stains.getName();
    String suggestedName;
    if (collectiveNameBefore.endsWith("default"))
        suggestedName = collectiveNameBefore.substring(0, collectiveNameBefore.lastIndexOf("default")) + "modified";
    else
        suggestedName = collectiveNameBefore;
    params.addStringParameter("collectiveName", "Collective name", suggestedName, "Enter collective name for all 3 stains (e.g. H-DAB Scanner A, H&E Scanner B)");
    if (value instanceof StainVector) {
        nameBefore = ((StainVector) value).getName();
        valuesBefore = ((StainVector) value).arrayAsString(Locale.getDefault(Category.FORMAT));
        params.addStringParameter("name", "Name", nameBefore, "Enter stain name").addStringParameter("values", "Values", valuesBefore, "Enter 3 values (red, green, blue) defining color deconvolution stain vector, separated by spaces");
        title = "Set stain vector";
    } else {
        nameBefore = "Background";
        valuesBefore = GeneralTools.arrayToString(Locale.getDefault(Category.FORMAT), (double[]) value, 2);
        params.addStringParameter("name", "Stain name", nameBefore);
        params.addStringParameter("values", "Stain values", valuesBefore, "Enter 3 values (red, green, blue) defining background, separated by spaces");
        params.setHiddenParameters(true, "name");
        title = "Set background";
    }
    if (warningMessage != null)
        params.addEmptyParameter(warningMessage);
    // Disable editing the name if it should be fixed
    ParameterPanelFX parameterPanel = new ParameterPanelFX(params);
    parameterPanel.setParameterEnabled("name", editableName);
    ;
    if (!Dialogs.showConfirmDialog(title, parameterPanel.getPane()))
        return;
    // Check if anything changed
    String collectiveName = params.getStringParameterValue("collectiveName");
    String nameAfter = params.getStringParameterValue("name");
    String valuesAfter = params.getStringParameterValue("values");
    if (collectiveName.equals(collectiveNameBefore) && nameAfter.equals(nameBefore) && valuesAfter.equals(valuesBefore) && !wasChanged)
        return;
    double[] valuesParsed = ColorDeconvolutionStains.parseStainValues(Locale.getDefault(Category.FORMAT), valuesAfter);
    if (valuesParsed == null) {
        logger.error("Input for setting color deconvolution information invalid! Cannot parse 3 numbers from {}", valuesAfter);
        return;
    }
    if (num >= 0) {
        try {
            stains = stains.changeStain(StainVector.createStainVector(nameAfter, valuesParsed[0], valuesParsed[1], valuesParsed[2]), num);
        } catch (Exception e) {
            logger.error("Error setting stain vectors", e);
            Dialogs.showErrorMessage("Set stain vectors", "Requested stain vectors are not valid!\nAre two stains equal?");
        }
    } else {
        // Update the background
        stains = stains.changeMaxValues(valuesParsed[0], valuesParsed[1], valuesParsed[2]);
    }
    // Set the collective name
    stains = stains.changeName(collectiveName);
    imageData.setColorDeconvolutionStains(stains);
    qupath.getViewer().repaintEntireImage();
}
Also used : StainVector(qupath.lib.color.StainVector) IOException(java.io.IOException) RectangleROI(qupath.lib.roi.RectangleROI) ROI(qupath.lib.roi.interfaces.ROI) BufferedImage(java.awt.image.BufferedImage) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) IOException(java.io.IOException) RectangleROI(qupath.lib.roi.RectangleROI) ParameterList(qupath.lib.plugins.parameters.ParameterList) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains)

Example 5 with ParameterPanelFX

use of qupath.lib.gui.dialogs.ParameterPanelFX in project qupath by qupath.

the class ParameterDialogWrapper method createDialog.

private Stage createDialog(final PathInteractivePlugin<T> plugin, final ParameterList params, final PluginRunner<T> pluginRunner) {
    panel = new ParameterPanelFX(params);
    panel.getPane().setPadding(new Insets(5, 5, 5, 5));
    // panel.addParameterChangeListener(new ParameterChangeListener() {
    // 
    // @Override
    // public void parameterChanged(ParameterList parameterList, String key, boolean isAdjusting) {
    // 
    // if (!plugin.requestLiveUpdate())
    // return;
    // 
    // PathObjectHierarchy hierarchy = pluginRunner.getHierarchy();
    // if (hierarchy == null)
    // return;
    // 
    // Collection<Class<? extends PathObject>> supportedParents = plugin.getSupportedParentObjectClasses();
    // 
    // PathObject selectedObject = pluginRunner.getSelectedObject();
    // if (selectedObject == null) {
    // if (supportedParents.contains(PathRootObject.class))
    // Collections.singleton(hierarchy.getRootObject());
    // } else if (supportedParents.contains(selectedObject.getClass()))
    // Collections.singleton(selectedObject);
    // }
    // 
    // });
    // final Button btnRun = new Button("Run " + plugin.getName());
    final Button btnRun = new Button("Run");
    btnRun.textProperty().bind(Bindings.createStringBinding(() -> {
        if (btnRun.isDisabled())
            return "Please wait...";
        else
            return "Run";
    }, btnRun.disabledProperty()));
    final Stage dialog = new Stage();
    QuPathGUI qupath = QuPathGUI.getInstance();
    if (qupath != null)
        dialog.initOwner(qupath.getStage());
    dialog.setTitle(plugin.getName());
    final String emptyLabel = " \n";
    final Label label = new Label(emptyLabel);
    label.setStyle("-fx-font-weight: bold;");
    label.setPadding(new Insets(5, 5, 5, 5));
    label.setAlignment(Pos.CENTER);
    label.setTextAlignment(TextAlignment.CENTER);
    btnRun.setOnAction(e -> {
        // Check if we have the parent objects available to make this worthwhile
        if (plugin instanceof PathInteractivePlugin) {
            // // Strip off any of our extra parameters
            // params.removeParameter(KEY_REGIONS);
            boolean alwaysPrompt = plugin.alwaysPromptForObjects();
            ImageData<?> imageData = pluginRunner.getImageData();
            Collection<PathObject> selected = imageData == null ? Collections.emptyList() : imageData.getHierarchy().getSelectionModel().getSelectedObjects();
            Collection<? extends PathObject> parents = PathObjectTools.getSupportedObjects(selected, plugin.getSupportedParentObjectClasses());
            if (alwaysPrompt || parents == null || parents.isEmpty()) {
                if (!ParameterDialogWrapper.promptForParentObjects(pluginRunner, plugin, alwaysPrompt && !parents.isEmpty()))
                    return;
            }
        // promptForParentObjects
        }
        dialog.getScene().setCursor(Cursor.WAIT);
        btnRun.setDisable(true);
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                try {
                    WorkflowStep lastStep = pluginRunner.getImageData().getHistoryWorkflow().getLastStep();
                    boolean success = plugin.runPlugin(pluginRunner, ParameterList.getParameterListJSON(params, "  "));
                    WorkflowStep lastStepNew = pluginRunner.getImageData().getHistoryWorkflow().getLastStep();
                    if (success && lastStep != lastStepNew)
                        lastWorkflowStep = lastStepNew;
                    else
                        lastWorkflowStep = null;
                } catch (Exception e) {
                    Dialogs.showErrorMessage("Plugin error", e);
                } catch (OutOfMemoryError e) {
                    // This doesn't actually work...
                    Dialogs.showErrorMessage("Out of memory error", "Out of memory - try to close other applications, or decrease the number of parallel processors in the QuPath preferences");
                } finally {
                    Platform.runLater(() -> {
                        QuPathGUI.getInstance().pluginRunningProperty().set(false);
                        dialog.getScene().setCursor(Cursor.DEFAULT);
                        label.setText(plugin.getLastResultsDescription());
                        btnRun.setDisable(false);
                    });
                }
            }
        };
        Thread t = new Thread(runnable, "Plugin thread");
        QuPathGUI.getInstance().pluginRunningProperty().set(true);
        t.start();
    });
    BorderPane pane = new BorderPane();
    ScrollPane scrollPane = new ScrollPane();
    label.setMaxWidth(Double.MAX_VALUE);
    scrollPane.setContent(panel.getPane());
    scrollPane.setFitToWidth(true);
    pane.setCenter(scrollPane);
    btnRun.setMaxWidth(Double.MAX_VALUE);
    btnRun.setPadding(new Insets(5, 5, 5, 5));
    pane.setBottom(btnRun);
    Scene scene = new Scene(pane);
    dialog.setScene(scene);
    // Request focus, to make it easier to run from the keyboard
    btnRun.requestFocus();
    dialog.sizeToScene();
    return dialog;
}
Also used : BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) Label(javafx.scene.control.Label) Scene(javafx.scene.Scene) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) PathInteractivePlugin(qupath.lib.plugins.PathInteractivePlugin) PathObject(qupath.lib.objects.PathObject) Button(javafx.scene.control.Button) ScrollPane(javafx.scene.control.ScrollPane) Stage(javafx.stage.Stage)

Aggregations

ParameterPanelFX (qupath.lib.gui.dialogs.ParameterPanelFX)8 BorderPane (javafx.scene.layout.BorderPane)7 Insets (javafx.geometry.Insets)6 ParameterList (qupath.lib.plugins.parameters.ParameterList)5 IOException (java.io.IOException)4 Label (javafx.scene.control.Label)4 TitledPane (javafx.scene.control.TitledPane)4 Tooltip (javafx.scene.control.Tooltip)4 Button (javafx.scene.control.Button)3 ContextMenu (javafx.scene.control.ContextMenu)3 MenuItem (javafx.scene.control.MenuItem)3 GridPane (javafx.scene.layout.GridPane)3 BufferedImage (java.awt.image.BufferedImage)2 File (java.io.File)2 FileNotFoundException (java.io.FileNotFoundException)2 ArrayList (java.util.ArrayList)2 SimpleStringProperty (javafx.beans.property.SimpleStringProperty)2 Scene (javafx.scene.Scene)2 SeparatorMenuItem (javafx.scene.control.SeparatorMenuItem)2 TableColumn (javafx.scene.control.TableColumn)2