Skip to content

Commit

Permalink
Add Choose Tracing Image > From System Clipboard command...
Browse files Browse the repository at this point in the history
While at it:
- Fix outdated URLs
- Simplify menu entry for TrakeEM2 listener
- Code cleanup
  • Loading branch information
tferr committed Jan 14, 2025
1 parent b18e1db commit a672b92
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/main/java/sc/fiji/snt/BookmarkManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ void populateFromFile(final File file) throws IOException {
final int tIdx = table.getColumnIndex(getHeader()[5]);

if (lIdx == -1 || xIdx == -1 || yIdx == -1 || zIdx == -1)
throw new IOException("Unexpected column header(s) in CSV file");
throw new IOException("Unexpected column header(s) in CSV file.");
final List<Bookmark> dataList = new ArrayList<>();
for (int i = 0; i < table.getRowCount(); i++) {
dataList.add(new Bookmark((String) table.get(lIdx, i), // label
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/sc/fiji/snt/FillManagerUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class FillManagerUI extends JDialog implements PathAndFillListener,
static { net.imagej.patcher.LegacyInjector.preinit(); } // required for _every_ class that imports ij. classes

private static final long serialVersionUID = 1L;
protected static final String FILLING_URI = "https://imagej.net/plugins/snt/step-by-step-instructions#filling";
protected static final String FILLING_URI = "https://imagej.net/plugins/snt/walkthroughs#filling";
private static final int STATUS_MARGIN = 2;
private static final int MARGIN = 10;

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/sc/fiji/snt/PathManagerUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ private JMenu getSpineUtilsMenu(final MultiPathActionListener multiPathListener)
menu.add(jmi);
menu.addSeparator();
jmi = GuiUtils.menuItemTriggeringHelpURL("Spine/Varicosity Utilities Help",
"https://imagej.net/plugins/snt/step-by-step-instructions#spinevaricosity-analysis");
"https://imagej.net/plugins/snt/walkthroughs#spinevaricosity-analysis");
menu.add(jmi);
return menu;
}
Expand All @@ -501,7 +501,7 @@ private JMenu getTimeSequenceMenu(final MultiPathActionListener multiPathListene
menu.add(jmi);
menu.addSeparator();
jmi = GuiUtils.menuItemTriggeringHelpURL("Time-lapse Utilities Help",
"https://imagej.net/plugins/snt/step-by-step-instructions#time-lapse-analysis");
"https://imagej.net/plugins/snt/walkthroughs#time-lapse-analysis");
menu.add(jmi);
return menu;
}
Expand Down
23 changes: 16 additions & 7 deletions src/main/java/sc/fiji/snt/SNTUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ public class SNTUI extends JDialog {
private JButton showOrHideFillList = new JButton(); // must be initialized
private JMenuItem saveMenuItem;
private JMenuItem quitMenuItem;
private JMenuItem sendToTrakEM2;
private JLabel statusText;
private JLabel statusBarText;
private JButton keepSegment;
Expand Down Expand Up @@ -859,7 +858,6 @@ protected void disableImageDependentComponents() {
private void disableEverything() {
assert SwingUtilities.isEventDispatchThread();
disableImageDependentComponents();
sendToTrakEM2.setEnabled(false);
saveMenuItem.setEnabled(false);
quitMenuItem.setEnabled(false);
}
Expand Down Expand Up @@ -896,7 +894,6 @@ public void changeState(final int newState) {
fmUI.setEnabledWhileNotFilling();
saveMenuItem.setEnabled(true);

sendToTrakEM2.setEnabled(plugin.anyListeners());
quitMenuItem.setEnabled(true);
showPathsSelected.setEnabled(true);
updateStatusText("Click somewhere to start a new path...");
Expand All @@ -917,7 +914,6 @@ public void changeState(final int newState) {
// setFillListVisible(false);
saveMenuItem.setEnabled(true);

sendToTrakEM2.setEnabled(plugin.anyListeners());
quitMenuItem.setEnabled(true);
showPathsSelected.setEnabled(true);
updateRebuildCanvasButton();
Expand Down Expand Up @@ -2521,6 +2517,9 @@ private JMenuBar createMenuBar() {
});
changeImpMenu.add(getImportActionMenuItem(ImportAction.IMAGE));
changeImpMenu.add(fromList);
final JMenuItem fromClipboard = getImportActionMenuItem(ImportAction.IMAGE_CLIPBOARD);
fromClipboard.setIcon(IconFactory.getMenuIcon(GLYPH.CLIPBOARD));
changeImpMenu.add(fromClipboard);
fileMenu.add(changeImpMenu);
fileMenu.addSeparator();
final JMenuItem autoTrace = getImportActionMenuItem(ImportAction.AUTO_TRACE_IMAGE);
Expand Down Expand Up @@ -2561,8 +2560,13 @@ private JMenuBar createMenuBar() {
fileMenu.add(restoreMenuItem);

fileMenu.addSeparator();
sendToTrakEM2 = new JMenuItem("Send to TrakEM2");
sendToTrakEM2.addActionListener(e -> plugin.notifyListeners(new SNTEvent(SNTEvent.SEND_TO_TRAKEM2)));
final JMenuItem sendToTrakEM2 = new JMenuItem("Send to TrakEM2", IconFactory.getMenuIcon('T', false));
sendToTrakEM2.addActionListener(e -> {
if (!plugin.anyListeners())
error("TrakEM2 is either not running or not listening to SNT events.");
else
plugin.notifyListeners(new SNTEvent(SNTEvent.SEND_TO_TRAKEM2));
});
fileMenu.add(sendToTrakEM2);
fileMenu.add(showFolderMenu(installer));

Expand Down Expand Up @@ -4343,6 +4347,8 @@ static String getImportActionName(final int type) {
return "NDF...";
case ImportAction.TRACES:
return "TRACES...";
case ImportAction.IMAGE_CLIPBOARD:
return "From System Clipboard";
default:
throw new IllegalArgumentException("Unknown type '" + type + "'");
}
Expand Down Expand Up @@ -4619,6 +4625,7 @@ private class ImportAction {
private static final int DEMO = 6;
private static final int AUTO_TRACE_IMAGE = 7;
private static final int NDF = 8;
private static final int IMAGE_CLIPBOARD = 9;

private final int type;
private File file;
Expand Down Expand Up @@ -4669,10 +4676,12 @@ private void run() {
choice.load(); // will reset UI
break;
case IMAGE:
case IMAGE_CLIPBOARD:
if (plugin.isSecondaryDataAvailable()) {
flushSecondaryDataPrompt();
}
inputs.put("file", file);
inputs.put("file", (IMAGE==type) ? file : null);
inputs.put("clipboard", IMAGE_CLIPBOARD==type);
(new DynamicCmdRunner(OpenDatasetCmd.class, inputs, LOADING)).run();
break;
case JSON:
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/sc/fiji/snt/gui/DemoRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,7 @@ public void load() {

entry.summary = "Downloads the test dataset of the Spot Spine software (image stack and traced dendrite).";
entry.data = "Image (3D; 0.7MB) and tracings (160KB)";
entry.source = "Spot Spine (https://imagej.net/plugins/spot-spine) manuscript, doi:10.12688/f1000research.146327.2" +
".146327.1";
entry.source = "Spot Spine (https://imagej.net/plugins/spot-spine) manuscript, doi:10.12688/f1000research.146327.2";
entry.online = true;
entry.tracingsURL = "https://raw.githubusercontent.com/morphonets/misc/master/dataset-demos/SpotSpine/SpotSpine_ImageStack_Test.swc";
return entry;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/sc/fiji/snt/gui/GuiUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,7 @@ public static JMenu helpMenu() {
mi = menuItemTriggeringURL("Screencasts", URL + "screencasts");
mi.setIcon(IconFactory.getMenuIcon(GLYPH.VIDEO));
helpMenu.add(mi);
mi = menuItemTriggeringURL("Step-by-step Instructions", URL + "step-by-step-instructions");
mi = menuItemTriggeringURL("Walkthroughs", URL + "walkthroughs");
mi.setIcon(IconFactory.getMenuIcon(GLYPH.FOOTPRINTS));
helpMenu.add(mi);

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/sc/fiji/snt/gui/SNTCommandFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.scijava.ui.awt.AWTWindows;
import org.scijava.util.PlatformUtils;
import sc.fiji.snt.SNTUI;
import sc.fiji.snt.SNTUtils;
import sc.fiji.snt.viewer.Viewer3D;

import javax.swing.*;
Expand Down Expand Up @@ -258,7 +259,7 @@ private void recordCmdSNTUI(final CmdAction cmdAction) {
}
if (promptCmd) {
switch (cmdAction.id) {
// FIXME: This should be move to the executing command
// FIXME: This should be moved to the executing command
case "Path-based Distributions...":
case "Branch-based Distributions...":
sb.append("\", \"metric 1 chosen in prompt\", \"[true or false (default) for polar histogram]\", \"[metric 2]\", \"[...]\")");
Expand Down Expand Up @@ -332,6 +333,7 @@ private void recordComment(final String string) {
public void toggleVisibility() {
if (frame == null || table == null) {
assemblePalette();
SNTUtils.log(String.format("Command Finder: %d commands indexed", (cmdScrapper.cmdMap.size()+cmdScrapper.otherMap.size())));
}
if (frame.isVisible()) {
hideWindow();
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/sc/fiji/snt/gui/ScriptInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class ScriptInstaller implements MenuKeyListener {
private static TextEditor editor;

private boolean openInsteadOfRun;
private Icon icon;

public ScriptInstaller(final Context context, final Component parent){
context.inject(this);
Expand All @@ -78,6 +79,7 @@ public ScriptInstaller(final Context context, final Component parent){
ui = null;
guiUtils = new GuiUtils(parent);
init();
icon = UIManager.getIcon("Tree.openIcon");
}

private void init() {
Expand Down Expand Up @@ -242,7 +244,7 @@ private JMenuItem menuItem(final ScriptInfo si, final boolean trimExtension) {

private void updateMenuItemIcon(final JMenuItem item) {
if (openInsteadOfRun && (item.isSelected() || item.isArmed())) {
item.setIcon(IconFactory.getMenuIcon(GLYPH.EYE));
item.setIcon(icon);
} else {
item.setIcon(null);
}
Expand Down
29 changes: 21 additions & 8 deletions src/main/java/sc/fiji/snt/gui/cmds/OpenDatasetCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@ public class OpenDatasetCmd extends CommonDynamicCmd {
@Parameter(required=false, persist = false)
private File file;

@Parameter(required=false, persist = false)
private boolean clipboard;

@SuppressWarnings("unused")
private void init() {
init(true);
if (file == null || !file.exists()) {
if (clipboard)
resolveInput("file");
else if (file == null || !file.exists()) {
file = new GuiUtils(ui).getImageFile(file);
resolveInput("file");
}
Expand All @@ -66,26 +71,34 @@ public void run() {
// final Dataset ds = ioService.open(file.getAbsolutePath());
// final ImagePlus imp = convertService.convert(ds, ImagePlus.class);
// snt.initialize(imp);
if (file != null) {
ImagePlus imp = ImpUtils.open(file);
imp = ChooseDatasetCmd.convertInPlaceToCompositeAsNeeded(ui, imp);
if (imp.getType() != ImagePlus.COLOR_RGB) {
snt.initialize(imp);
}
if (clipboard) {
final ImagePlus imp = ImpUtils.getSystemClipboard(true);
if (imp == null)
error("No (valid) image found on system clipboard.");
else
initializeSNT(imp);
} else if (file != null) {
initializeSNT(ImpUtils.open(file));
}
}
catch (final NullPointerException ex) {
error("Loading of image failed (see Console for details)... "
+ "File may not be valid or may encode a proprietary format "
+ "not immediately recognized. Please open the image using "
+ "IJ/Bioformats (if not yet open). Then, load it in SNT using "
+ "Bioformats (if not yet open). Then, load it in SNT using "
+ "'Choose Tracing Image -> From Open Image...'.");
ex.printStackTrace();
} finally {
resetUI();
}
}

void initializeSNT(ImagePlus imp) {
imp = ChooseDatasetCmd.convertInPlaceToCompositeAsNeeded(ui, imp);
if (imp.getType() != ImagePlus.COLOR_RGB)
snt.initialize(imp);
}

public static void main(final String... args) {
final ImageJ ij = new ImageJ();
ij.ui().showUI();
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/sc/fiji/snt/util/ImpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
import sc.fiji.snt.SNTUtils;
import sc.fiji.snt.Tree;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.InputStream;
Expand Down Expand Up @@ -275,6 +280,47 @@ public static ImagePlus combineSkeletons(final Collection<Tree> trees, final boo
return imp;
}

/**
* Retrieves an ImagePlus from the system clipboard
*
* @param asMultiChannel if true and clipboard contains RGB data image is returned as composite (RGB/8-bit grayscale otherwise)
* @return the image stored in the system clipboard or null if no image found
*/
public static ImagePlus getSystemClipboard(final boolean asMultiChannel) {
try {
final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
final Transferable transferable = clipboard.getContents(null);
if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
final Image img = (Image) transferable.getTransferData(DataFlavor.imageFlavor);
final int width = img.getWidth(null);
final int height = img.getHeight(null);
final BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
final Graphics g = bi.createGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
final ImagePlus result = new ImagePlus("Clipboard", bi);
final boolean isGray = isGrayscale(bi, width, height);
if (isGray) convertTo8bit(result);
return (asMultiChannel && !isGray) ? convertRGBtoComposite(result) : result;
}
} catch (final Throwable e) {
SNTUtils.error("No image in clipboard", e);
}
return null;
}

private static boolean isGrayscale(final BufferedImage image, final int width, final int height) {
for (int i = 0; i < width; i++) { // https://stackoverflow.com/a/36173328
for (int j = 0; j < height; j++) {
final int pixel = image.getRGB(i, j);
final int red = (pixel >> 16) & 0xff;
final int green = (pixel >> 8) & 0xff;
if (red != green || green != ((pixel) & 0xff)) return false;
}
}
return true;
}

/**
* Returns one of the demo images bundled with SNT image associated with the
* demo (fractal) tree.
Expand Down

0 comments on commit a672b92

Please sign in to comment.