Search in sources :

Example 31 with DefaultScriptableWorkflowStep

use of qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep 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());
}
Also used : EventHandler(javafx.event.EventHandler) Scene(javafx.scene.Scene) Arrays(java.util.Arrays) ButtonType(javafx.scene.control.ButtonType) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ScriptEditor(qupath.lib.gui.scripting.ScriptEditor) DragEvent(javafx.scene.input.DragEvent) TransferMode(javafx.scene.input.TransferMode) Projects(qupath.lib.projects.Projects) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) TMADataIO(qupath.lib.gui.tma.TMADataIO) Dragboard(javafx.scene.input.Dragboard) Map(java.util.Map) QuPathGUI(qupath.lib.gui.QuPathGUI) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) Dialog(javafx.scene.control.Dialog) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) Node(javafx.scene.Node) Set(java.util.Set) IOException(java.io.IOException) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) ProjectCommands(qupath.lib.gui.commands.ProjectCommands) Collectors(java.util.stream.Collectors) File(java.io.File) PathObject(qupath.lib.objects.PathObject) List(java.util.List) Project(qupath.lib.projects.Project) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ProjectIO(qupath.lib.projects.ProjectIO) DefaultScriptEditor(qupath.lib.gui.scripting.DefaultScriptEditor) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) PathIO(qupath.lib.io.PathIO) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) DefaultScriptEditor(qupath.lib.gui.scripting.DefaultScriptEditor) BufferedImage(java.awt.image.BufferedImage) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) Dialog(javafx.scene.control.Dialog) ButtonType(javafx.scene.control.ButtonType) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) IOException(java.io.IOException) IOException(java.io.IOException) ScriptEditor(qupath.lib.gui.scripting.ScriptEditor) DefaultScriptEditor(qupath.lib.gui.scripting.DefaultScriptEditor) PathObject(qupath.lib.objects.PathObject) File(java.io.File)

Aggregations

DefaultScriptableWorkflowStep (qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep)31 PathObject (qupath.lib.objects.PathObject)12 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)12 WorkflowStep (qupath.lib.plugins.workflow.WorkflowStep)12 ArrayList (java.util.ArrayList)10 List (java.util.List)10 Map (java.util.Map)10 Dialogs (qupath.lib.gui.dialogs.Dialogs)10 Collectors (java.util.stream.Collectors)9 QuPathGUI (qupath.lib.gui.QuPathGUI)9 BufferedImage (java.awt.image.BufferedImage)8 IOException (java.io.IOException)8 Arrays (java.util.Arrays)8 ImageData (qupath.lib.images.ImageData)8 File (java.io.File)7 StringProperty (javafx.beans.property.StringProperty)7 Logger (org.slf4j.Logger)7 LoggerFactory (org.slf4j.LoggerFactory)7 GeneralTools (qupath.lib.common.GeneralTools)7 Collections (java.util.Collections)6