use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class QuPathGUI method runPlugin.
/**
* Run a plugin, interactively (i.e. launching a dialog) if necessary.
* <p>
* Note that this does not in itself perform any exception handling.
*
* @param plugin the plugin to run
* @param arg optional string argument (usually JSON)
* @param doInteractive if true, show an interactive dialog if the plugin is an instance of {@link PathInteractivePlugin}
* @return true if running the plugin was successful and was not cancelled.
* Note that if {@code doInteractive == true} and the dialog was launched
* but not run, this will also return true.
* @throws Exception
*/
public boolean runPlugin(final PathPlugin<BufferedImage> plugin, final String arg, final boolean doInteractive) throws Exception {
// TODO: Check safety...
if (doInteractive && plugin instanceof PathInteractivePlugin) {
PathInteractivePlugin<BufferedImage> pluginInteractive = (PathInteractivePlugin<BufferedImage>) plugin;
ParameterList params = pluginInteractive.getDefaultParameterList(getImageData());
// Update parameter list, if necessary
if (arg != null) {
Map<String, String> map = GeneralTools.parseArgStringValues(arg);
// We use the US locale because we need to ensure decimal points (not commas)
ParameterList.updateParameterList(params, map, Locale.US);
}
var runner = new PluginRunnerFX(this);
ParameterDialogWrapper<BufferedImage> dialog = new ParameterDialogWrapper<>(pluginInteractive, params, runner);
dialog.showDialog();
return !runner.isCancelled();
} else {
try {
pluginRunning.set(true);
var runner = new PluginRunnerFX(this);
@SuppressWarnings("unused") var completed = plugin.runPlugin(runner, arg);
return !runner.isCancelled();
} finally {
pluginRunning.set(false);
}
}
// } catch (Exception e) {
// logger.error("Unable to run plugin " + plugin, e);
// return false;
// }
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class AbstractTileableDetectionPlugin method addRunnableTasks.
/**
* Intercepts the 'standard' addRunnableTasks to (if necessary) insert ParallelTileObjects along the way,
* thereby breaking an excessively-large parentObject into more manageable pieces.
* <p>
* TODO: Avoid hard-coding what is considered a 'manageable size' or a preferred size for parallel tiles.
*/
@Override
protected void addRunnableTasks(ImageData<T> imageData, PathObject parentObject, List<Runnable> tasks) {
if (imageData == null)
return;
ParameterList params = getParameterList(imageData);
// Determine appropriate sizes
// Note, for v0.1.2 and earlier the downsample was restricted to be a power of 2
double downsampleFactor = ServerTools.getDownsampleFactor(imageData.getServer(), getPreferredPixelSizeMicrons(imageData, params));
int preferred = (int) (PREFERRED_TILE_SIZE * downsampleFactor);
int max = (int) (MAX_TILE_SIZE * downsampleFactor);
ImmutableDimension sizePreferred = ImmutableDimension.getInstance(preferred, preferred);
ImmutableDimension sizeMax = ImmutableDimension.getInstance(max, max);
// parentObject.clearPathObjects();
// Extract (or create) suitable ROI
ROI parentROI = parentObject.getROI();
if (parentROI == null)
parentROI = ROIs.createRectangleROI(0, 0, imageData.getServer().getWidth(), imageData.getServer().getHeight(), ImagePlane.getDefaultPlane());
// Make tiles
Collection<? extends ROI> pathROIs = RoiTools.computeTiledROIs(parentROI, sizePreferred, sizeMax, false, getTileOverlap(imageData, params));
// No tasks to complete
if (pathROIs.isEmpty())
return;
// // Exactly one task to complete
// if (pathROIs.size() == 1 && pathROIs.iterator().next() == parentObject.getROI()) {
// tasks.add(DetectionPluginTools.createRunnableTask(createDetector(imageData, params), getParameterList(imageData), imageData, parentObject));
// return;
// }
ParallelDetectionTileManager manager = new ParallelDetectionTileManager(parentObject);
List<ParallelTileObject> tileList = new ArrayList<>();
AtomicInteger countdown = new AtomicInteger(pathROIs.size());
for (ROI pathROI : pathROIs) {
ParallelTileObject tile = new ParallelTileObject(manager, pathROI, imageData.getHierarchy(), countdown);
parentObject.addPathObject(tile);
for (ParallelTileObject tileTemp : tileList) {
if (tileTemp.suggestNeighbor(tile))
tile.suggestNeighbor(tileTemp);
}
tileList.add(tile);
tasks.add(DetectionPluginTools.createRunnableTask(createDetector(imageData, params), params, imageData, tile));
}
manager.setTiles(tileList);
imageData.getHierarchy().fireHierarchyChangedEvent(this);
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class AbstractInteractivePlugin method getLoggableParameters.
/**
* Get a copy of the current parameter list (with empty parameters removed) suitable for logging.
* Subclasses might choose to append extra parameters here, which aren't part of the main list
* (e.g. because they shouldn't be included in any automatically created dialog box)
* @param imageData
* @return
*/
protected ParameterList getLoggableParameters(final ImageData<T> imageData) {
ParameterList params = getParameterList(imageData).duplicate();
params.removeEmptyParameters();
return params;
}
use of qupath.lib.plugins.parameters.ParameterList 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;
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class GuiTools method promptForSliderRange.
/**
* Prompt the user to input min/max values for a slider.
* @param slider
* @return true if the user may have made changes, false if they cancelled the dialog
*/
public static boolean promptForSliderRange(Slider slider) {
var params = new ParameterList().addEmptyParameter("Specify the min/max values supported by the slider").addDoubleParameter("minValue", "Slider minimum", slider.getMin()).addDoubleParameter("maxValue", "Slider maximum", slider.getMax());
if (!Dialogs.showParameterDialog("Slider range", params))
return false;
slider.setMin(params.getDoubleParameterValue("minValue"));
slider.setMax(params.getDoubleParameterValue("maxValue"));
return true;
}
Aggregations