use of qupath.lib.plugins.workflow.WorkflowStep 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;
}
use of qupath.lib.plugins.workflow.WorkflowStep in project qupath by qupath.
the class DragDropImportListener method handleFileDropImpl.
private void handleFileDropImpl(final QuPathViewer viewer, final List<File> list) throws IOException {
// Shouldn't occur... but keeps FindBugs happy to check
if (list == null) {
logger.warn("No files given!");
return;
}
// Check if we have only jar files
int nJars = 0;
for (File file : list) {
if (file.getName().toLowerCase().endsWith(".jar"))
nJars++;
}
if (nJars == list.size()) {
if (qupath.canInstallExtensions())
qupath.installExtensions(list);
else
Dialogs.showErrorMessage("Install extensions", "Sorry, extensions can only be installed when QuPath is run as a standalone application.");
return;
}
// Try to get a hierarchy for importing ROIs
ImageData<BufferedImage> imageData = viewer == null ? null : viewer.getImageData();
PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
// Some consumers can only handle one file
boolean singleFile = list.size() == 1;
// Gather together the extensions - if this has length one, we know all the files have the same extension
Set<String> allExtensions = list.stream().map(f -> GeneralTools.getExtension(f).orElse("")).collect(Collectors.toSet());
// If we have a zipped file, create a set that includes the files within the zip image
// This helps us determine whether or not a zip file contains an image or objects, for example
Set<String> allUnzippedExtensions = allExtensions;
if (allExtensions.contains(".zip")) {
allUnzippedExtensions = list.stream().flatMap(f -> {
try {
return PathIO.unzippedExtensions(f.toPath()).stream();
} catch (IOException e) {
logger.debug(e.getLocalizedMessage(), e);
return Arrays.stream(new String[0]);
}
}).collect(Collectors.toSet());
}
// Extract the first (and possibly only) file
File file = list.get(0);
String fileName = file.getName().toLowerCase();
// Check if this is a hierarchy file
if (singleFile && (fileName.endsWith(PathPrefs.getSerializationExtension()))) {
// If we have a different path, open as a new image
if (viewer == null) {
Dialogs.showErrorMessage("Load data", "Please drag the file onto a specific viewer to open!");
return;
}
try {
// Check if we should be importing objects or opening the file
if (imageData != null) {
var dialog = new Dialog<ButtonType>();
var btOpen = new ButtonType("Open image");
var btImport = new ButtonType("Import objects");
dialog.getDialogPane().getButtonTypes().setAll(btOpen, btImport, ButtonType.CANCEL);
dialog.setTitle("Open data");
dialog.setHeaderText("What do you want to do with the data file?");
dialog.setContentText("You can\n" + " 1. Open the image in the current viewer\n" + " 2. Import objects and add them to the current image");
// dialog.setHeaderText("What do you want to do?");
var choice = dialog.showAndWait().orElse(ButtonType.CANCEL);
if (choice == ButtonType.CANCEL)
return;
if (choice == btImport) {
var pathObjects = PathIO.readObjects(file);
hierarchy.addPathObjects(pathObjects);
return;
}
}
qupath.openSavedData(viewer, file, false, true);
} catch (Exception e) {
Dialogs.showErrorMessage("Load data", e);
}
return;
}
// Check if this is a directory - if so, look for a single project file
if (singleFile && file.isDirectory()) {
// Identify all files in the directory, and also all potential project files
File[] filesInDirectory = file.listFiles(f -> !f.isHidden());
List<File> projectFiles = Arrays.stream(filesInDirectory).filter(f -> f.isFile() && f.getAbsolutePath().toLowerCase().endsWith(ProjectIO.getProjectExtension())).collect(Collectors.toList());
if (projectFiles.size() == 1) {
file = projectFiles.get(0);
logger.warn("Selecting project file {}", file);
} else if (projectFiles.size() > 1) {
// Prompt to select which project file to open
logger.debug("Multiple project files found in directory {}", file);
String[] fileNames = projectFiles.stream().map(f -> f.getName()).toArray(n -> new String[n]);
String selectedName = Dialogs.showChoiceDialog("Select project", "Select project to open", fileNames, fileNames[0]);
if (selectedName == null)
return;
file = new File(file, selectedName);
} else if (filesInDirectory.length == 0) {
// If we have an empty directory, offer to set it as a project
if (Dialogs.showYesNoDialog("Create project", "Create project for empty directory?")) {
Project<BufferedImage> project = Projects.createProject(file, BufferedImage.class);
qupath.setProject(project);
if (!project.isEmpty())
project.syncChanges();
return;
} else
// Can't do anything else with an empty folder
return;
}
}
// Check if this is a project
if (singleFile && (fileName.endsWith(ProjectIO.getProjectExtension()))) {
try {
Project<BufferedImage> project = ProjectIO.loadProject(file, BufferedImage.class);
qupath.setProject(project);
} catch (Exception e) {
// Dialogs.showErrorMessage("Project error", e);
logger.error("Could not open as project file: {}, opening in the Script Editor instead", e);
qupath.getScriptEditor().showScript(file);
}
return;
}
// Check if it is an object file in GeoJSON format (.geojson)
if (PathIO.getObjectFileExtensions(false).containsAll(allUnzippedExtensions)) {
if (imageData == null || hierarchy == null) {
qupath.getScriptEditor().showScript(file);
logger.info("Opening the dragged file in the Script Editor as there is no currently opened image in the viewer");
// Dialogs.showErrorMessage("Open object file", "Please open an image first to import objects!");
return;
}
List<PathObject> pathObjects = new ArrayList<>();
List<WorkflowStep> steps = new ArrayList<>();
for (var tempFile : list) {
try {
var tempObjects = PathIO.readObjects(tempFile);
if (tempObjects.isEmpty()) {
logger.warn("No objects found in {}, opening the dragged file in the Script Editor instead", tempFile.getAbsolutePath());
qupath.getScriptEditor().showScript(file);
return;
}
pathObjects.addAll(tempObjects);
// Add step to workflow
Map<String, String> map = new HashMap<>();
map.put("path", file.getPath());
String method = "Import objects";
String methodString = String.format("%s(%s%s%s)", "importObjectsFromFile", "\"", GeneralTools.escapeFilePath(tempFile.getPath()), "\"");
steps.add(new DefaultScriptableWorkflowStep(method, map, methodString));
} catch (IOException | IllegalArgumentException e) {
Dialogs.showErrorNotification("Object import", e.getLocalizedMessage());
return;
}
}
// Ask confirmation to user
int nObjects = pathObjects.size();
String message = nObjects == 1 ? "Add object to the hierarchy?" : String.format("Add %d objects to the hierarchy?", nObjects);
var confirm = Dialogs.showConfirmDialog("Add to hierarchy", message);
if (!confirm)
return;
// Add objects to hierarchy
hierarchy.addPathObjects(pathObjects);
imageData.getHistoryWorkflow().addSteps(steps);
return;
}
// Check if this is TMA dearraying data file
if (singleFile && (fileName.endsWith(TMADataIO.TMA_DEARRAYING_DATA_EXTENSION))) {
if (hierarchy == null)
Dialogs.showErrorMessage("TMA grid import", "Please open an image first before importing a dearrayed TMA grid!");
else {
TMAGrid tmaGrid = TMADataIO.importDearrayedTMAData(file);
if (tmaGrid != null) {
if (hierarchy.isEmpty() || Dialogs.showYesNoDialog("TMA grid import", "Set TMA grid for existing hierarchy?"))
hierarchy.setTMAGrid(tmaGrid);
} else
Dialogs.showErrorMessage("TMA grid import", "Could not parse TMA grid from " + file.getName());
}
return;
}
// Open file with an extension supported by the Script Editor
ScriptEditor scriptEditor = qupath.getScriptEditor();
if (scriptEditor instanceof DefaultScriptEditor && ((DefaultScriptEditor) scriptEditor).supportsFile(file)) {
scriptEditor.showScript(file);
return;
}
// Check handlers
for (DropHandler<File> handler : dropHandlers) {
if (handler.handleDrop(viewer, list))
return;
}
// Assume we have images
if (singleFile && file.isFile()) {
// Try to open as an image, if the extension is known
if (viewer == null) {
Dialogs.showErrorMessage("Open image", "Please drag the file only a specific viewer to open!");
return;
}
qupath.openImage(viewer, file.getAbsolutePath(), true, true);
return;
} else if (qupath.getProject() != null) {
// Try importing multiple images to a project
String[] potentialFiles = list.stream().filter(f -> f.isFile()).map(f -> f.getAbsolutePath()).toArray(String[]::new);
if (potentialFiles.length > 0) {
ProjectCommands.promptToImportImages(qupath, potentialFiles);
return;
}
}
if (qupath.getProject() == null) {
if (list.size() > 1) {
Dialogs.showErrorMessage("Drag & drop", "Could not handle multiple file drop - if you want to handle multiple images, you need to create a project first");
return;
}
}
if (list.size() > 1)
Dialogs.showErrorMessage("Drag & drop", "Sorry, I couldn't figure out what to do with these files - try opening one at a time");
else
Dialogs.showErrorMessage("Drag & drop", "Sorry, I couldn't figure out what to do with " + list.get(0).getName());
}
use of qupath.lib.plugins.workflow.WorkflowStep in project qupath by qupath.
the class TMADearrayerPluginIJ method addWorkflowStep.
@Override
protected void addWorkflowStep(final ImageData<BufferedImage> imageData, final String arg) {
WorkflowStep step = new SimplePluginWorkflowStep(getName(), (Class<? extends PathPlugin<?>>) getClass(), arg, "if (!isTMADearrayed()) {\n\t", "\n\treturn;\n}");
imageData.getHistoryWorkflow().addStep(step);
logger.info("{}", step);
}
use of qupath.lib.plugins.workflow.WorkflowStep in project qupath by qupath.
the class WorkflowCommandLogView method createPane.
protected BorderPane createPane() {
BorderPane pane = new BorderPane();
TableColumn<KeyValue<Object>, String> col1 = new TableColumn<>("Parameter");
col1.setCellValueFactory(c -> c.getValue().keyProperty());
TableColumn<KeyValue<Object>, Object> col2 = new TableColumn<>("Value");
col2.setCellValueFactory(c -> c.getValue().valueProperty());
col2.setCellFactory(t -> new ParameterTableCell<>());
table.getColumns().add(col1);
table.getColumns().add(col2);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
SplitPane splitPane = new SplitPane();
splitPane.setOrientation(Orientation.VERTICAL);
splitPane.getItems().addAll(list, table);
// Allow multiple selections for static model
list.getSelectionModel().setSelectionMode(isStaticWorkflow ? SelectionMode.MULTIPLE : SelectionMode.SINGLE);
list.getSelectionModel().selectedItemProperty().addListener((e, f, g) -> {
WorkflowStep step = list.getSelectionModel().getSelectedItem();
populateList(table.getItems(), step);
});
final ContextMenu contextMenu = new ContextMenu();
MenuItem miCopyCommand = new MenuItem("Copy command" + (isStaticWorkflow ? "s" : ""));
miCopyCommand.setOnAction(e -> {
List<Integer> indices = list.getSelectionModel().getSelectedIndices();
if (indices == null || indices.isEmpty())
return;
copyScriptToClipboard(indices);
});
miCopyCommand.disableProperty().bind(workflowProperty.isNull());
contextMenu.getItems().setAll(miCopyCommand);
if (isStaticWorkflow) {
MenuItem miRemoveSelected = new MenuItem("Remove selected items");
miRemoveSelected.setOnAction(e -> {
var steps = getSelectedIndices();
if (steps == null || steps.isEmpty())
return;
String message = steps.size() == 1 ? "Remove workflow step?" : "Remove " + steps.size() + " workflow steps?";
if (!Dialogs.showYesNoDialog("Remove workflow steps", message))
return;
Collections.sort(steps);
for (int i = steps.size() - 1; i >= 0; i--) getWorkflow().removeStep(steps.get(i));
// workflow.removeSteps(steps);
});
miRemoveSelected.disableProperty().bind(workflowProperty.isNull());
MenuItem miMoveUp = new MenuItem("Move up");
miMoveUp.setOnAction(e -> {
var indices = getSelectedIndices();
if (indices == null || indices.isEmpty() || indices.get(0) <= 0)
return;
var workflow = getWorkflow();
List<WorkflowStep> steps = new ArrayList<>(workflow.getSteps());
WorkflowStep[] stepsRemoved = new WorkflowStep[indices.size()];
workflow.removeSteps(steps);
int[] newIndices = new int[indices.size()];
for (int i = indices.size() - 1; i >= 0; i--) {
int ind = indices.get(i);
int indNew = ind - 1;
newIndices[i] = indNew;
stepsRemoved[i] = steps.remove(ind);
}
for (int i = 0; i < indices.size(); i++) {
steps.add(newIndices[i], stepsRemoved[i]);
}
workflow.addSteps(steps);
list.getSelectionModel().clearSelection();
list.getSelectionModel().selectIndices(newIndices[0], newIndices);
});
miMoveUp.disableProperty().bind(workflowProperty.isNull());
MenuItem miMoveDown = new MenuItem("Move down");
miMoveDown.setOnAction(e -> {
var indices = getSelectedIndices();
var workflow = getWorkflow();
if (indices == null || indices.isEmpty() || indices.get(indices.size() - 1) >= workflow.size() - 1)
return;
list.getSelectionModel().clearSelection();
Collections.sort(indices);
List<WorkflowStep> steps = new ArrayList<>(workflow.getSteps());
WorkflowStep[] stepsRemoved = new WorkflowStep[indices.size()];
workflow.removeSteps(steps);
int[] newIndices = new int[indices.size()];
for (int i = indices.size() - 1; i >= 0; i--) {
int ind = indices.get(i);
int indNew = ind + 1;
newIndices[i] = indNew;
stepsRemoved[i] = steps.remove(ind);
}
for (int i = 0; i < indices.size(); i++) {
steps.add(newIndices[i], stepsRemoved[i]);
}
workflow.addSteps(steps);
list.getSelectionModel().select(newIndices[0]);
list.getSelectionModel().selectIndices(newIndices[0], newIndices);
// int ind = list.getSelectionModel().getSelectedIndex();
// if (ind < 0 || ind >= workflow.size()-1)
// return;
// List<WorkflowStep> steps = new ArrayList<>(workflow.getSteps());
// WorkflowStep step = steps.remove(ind);
// steps.add(ind+1, step);
// workflow.removeSteps(steps);
// workflow.addSteps(steps);
// list.getSelectionModel().select(step);
});
miMoveDown.disableProperty().bind(workflowProperty.isNull());
contextMenu.getItems().addAll(new SeparatorMenuItem(), miMoveUp, miMoveDown, new SeparatorMenuItem(), miRemoveSelected);
}
list.setCellFactory(new Callback<ListView<WorkflowStep>, ListCell<WorkflowStep>>() {
@Override
public ListCell<WorkflowStep> call(ListView<WorkflowStep> p) {
ListCell<WorkflowStep> cell = new ListCell<>() {
@Override
protected void updateItem(WorkflowStep value, boolean bln) {
super.updateItem(value, bln);
if (value instanceof WorkflowStep)
setText(value.getName());
else if (value == null)
setText(null);
else
setText(value.toString());
setContextMenu(contextMenu);
setOnMouseClicked(e -> {
// Only handle double clicks
if (!e.isPopupTrigger() && e.getClickCount() == 2)
runWorkflowStepInteractively(qupath, value);
});
setOnKeyPressed(e -> {
if (copyCombination.match(e)) {
copyScriptToClipboard(getSelectedIndices());
e.consume();
}
});
}
};
return cell;
}
});
pane.setCenter(splitPane);
Button btnCreateScript = new Button("Create script");
btnCreateScript.setMaxWidth(Double.MAX_VALUE);
btnCreateScript.setOnAction(e -> showScript());
btnCreateScript.disableProperty().bind(workflowProperty.isNull());
Button btnCreateWorkflow = null;
if (!isStaticWorkflow) {
btnCreateWorkflow = new Button("Create workflow");
btnCreateWorkflow.setMaxWidth(Double.MAX_VALUE);
btnCreateWorkflow.setOnAction(e -> {
var workflow = getWorkflow();
if (workflow == null)
return;
Stage stage = new Stage();
stage.initOwner(qupath.getStage());
stage.setTitle("Workflow");
Workflow workflowNew = new Workflow();
workflowNew.addSteps(workflow.getSteps());
stage.setScene(new Scene(new WorkflowCommandLogView(qupath, workflowNew).getPane(), 400, 600));
stage.show();
});
btnCreateWorkflow.disableProperty().bind(workflowProperty.isNull());
pane.setBottom(PaneTools.createColumnGridControls(btnCreateWorkflow, btnCreateScript));
} else
pane.setBottom(btnCreateScript);
// pane.setBottom(btnGenerateScript);
return pane;
}
use of qupath.lib.plugins.workflow.WorkflowStep in project qupath by qupath.
the class AbstractPlugin method addWorkflowStep.
/**
* Add a workflow step to the ImageData indicating the argument that this plugin was run with.
*
* Subclasses may override this if a better workflow step should be logged.
*
* A subclass may also override this to avoid adding a workflow step at all.
*
* @param imageData
* @param arg
*/
protected void addWorkflowStep(final ImageData<T> imageData, final String arg) {
@SuppressWarnings("unchecked") WorkflowStep step = new SimplePluginWorkflowStep(getName(), (Class<? extends PathPlugin<T>>) getClass(), arg);
imageData.getHistoryWorkflow().addStep(step);
logger.debug("Adding workflow step: {}", step);
}
Aggregations