BraiAnExtension.java
// SPDX-FileCopyrightText: 2024 Carlo Castoldi <carlo.castoldi@outlook.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
package qupath.ext.braian;
import org.controlsfx.control.action.Action;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qupath.lib.gui.QuPathGUI;
import qupath.lib.gui.actions.ActionTools;
import qupath.lib.gui.extensions.QuPathExtension;
import qupath.lib.gui.tools.MenuTools;
import qupath.lib.objects.PathObject;
import qupath.lib.objects.hierarchy.PathObjectHierarchy;
import qupath.lib.scripting.QP;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class BraiAnExtension implements QuPathExtension {
private static final String menuPosition = "Extensions>BraiAn";
static final Logger logger = LoggerFactory.getLogger(BraiAnExtension.class);
/**
* @return the logger used by the extension to print messages
*/
public static Logger getLogger() {
return logger;
}
/**
* integrate the extension in QuPath GUI
* @param qupath the istance of QuPath to modify
*/
@Override
public void installExtension(QuPathGUI qupath) {
this.addCommands(qupath);
this.addHelpScripts(qupath);
}
private void addCommands(QuPathGUI qupath) {
var showExclusions = ActionTools.createAction(
() -> {
PathObjectHierarchy hierarchy = QP.getCurrentHierarchy();
if (hierarchy == null) {
logger.error("No image is currently open!");
return;
}
AtlasManager atlas = new AtlasManager(hierarchy);
Set<PathObject> regionsToExcludeSet = atlas.getExcludedBrainRegions();
QP.resetSelection();
QP.selectObjects(regionsToExcludeSet);
},
"Show regions currently excluded");
MenuTools.addMenuItems(
qupath.getMenu(BraiAnExtension.menuPosition, true),
showExclusions
);
}
private void addHelpScripts(QuPathGUI qupath) {
File extensionJar = getExtensionJarPath();
if (extensionJar == null) {
logger.warn("Could not find the '{}' JAR file!", getClass().getSimpleName());
return;
}
String[] scripts = listJarDirectory(extensionJar, (dir, file) -> dir.toString().equals("scripts") && file.endsWith(".groovy"));
for(String scriptPath: scripts)
try (InputStream stream = getClass().getClassLoader().getResourceAsStream(scriptPath)) {
assert stream != null;
String scriptName = new File(scriptPath).getName();
String script = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
MenuTools.addMenuItems(
qupath.getMenu(BraiAnExtension.menuPosition+">Scripts", true),
new Action(scriptName, e -> openScript(qupath, scriptName, script))
);
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
}
}
private File getExtensionJarPath() {
File extensionJar = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
assert extensionJar.toString().endsWith(".jar");
return extensionJar;
}
private String[] listJarDirectory(File jarPath, FilenameFilter filter) {
Enumeration<JarEntry> internalFiles = null;
try (JarFile jar = new JarFile(URLDecoder.decode(jarPath.toString(), StandardCharsets.UTF_8))) {
internalFiles = jar.entries(); // gives ALL internalFiles in jar
List<String> selectedResources = new ArrayList<>();
while(internalFiles.hasMoreElements()) {
String internalPath = internalFiles.nextElement().getName();
if (internalPath.endsWith("/"))
continue;
File internalFilePath = new File(internalPath);
if (filter.accept(internalFilePath.getParentFile(), internalFilePath.getName()))
selectedResources.add(internalPath);
}
return selectedResources.toArray(new String[0]);
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
return new String[0];
}
}
private static void openScript(QuPathGUI qupath, String scriptName, String scriptCode) {
var editor = qupath.getScriptEditor();
if (editor == null) {
logger.error("No script editor is available!");
return;
}
qupath.getScriptEditor().showScript(scriptName, scriptCode);
}
/**
* @return the name of the extension
*/
@Override
public String getName() {
return "BraiAn extension";
}
/**
* @return the description of the extension
*/
@Override
public String getDescription() {
return "A collection of tools for whole-brain data quantification and extraction";
}
}