diff --git a/src/main/java/ai/nets/samj/gui/HTMLPane2.java b/src/main/java/ai/nets/samj/gui/HTMLPane2.java deleted file mode 100644 index 0006365..0000000 --- a/src/main/java/ai/nets/samj/gui/HTMLPane2.java +++ /dev/null @@ -1,110 +0,0 @@ -package ai.nets.samj.gui; - -import java.awt.Dimension; - -import javax.swing.JEditorPane; -import javax.swing.JScrollPane; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; - -/** - * This class extends the Java JEditorPane to make a easy to use panel to - * display HTML information. - * - * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. - * - */ -public class HTMLPane2 extends JEditorPane { - - private String html = ""; - private String header = ""; - private String footer = ""; - private Dimension dim; - private String font = "verdana"; - private String color = "#222222"; - private String background = "#f8f8f8"; - - public HTMLPane2() { - create(); - } - - public HTMLPane2(String font) { - this.font = font; - create(); - } - - public HTMLPane2(int width, int height) { - this.dim = new Dimension(width, height); - create(); - } - - public HTMLPane2(String font, int width, int height) { - this.font = font; - this.dim = new Dimension(width, height); - create(); - } - - public HTMLPane2(String font, String color, String background, int width, int height) { - this.font = font; - this.dim = new Dimension(width, height); - this.color = color; - this.background = background; - create(); - } - - @Override - public String getText() { - Document doc = this.getDocument(); - try { - return doc.getText(0, doc.getLength()); - } - catch (BadLocationException e) { - e.printStackTrace(); - return getText(); - } - } - - public void clear() { - html = ""; - append(""); - } - - private void create() { - header += "\n"; - header += "\n"; - header += "\n"; - header += "\n"; - header += "\n"; - header += "\n"; - header += "\n"; - header += "\n"; - header += "\n"; - footer += "\n"; - setEditable(false); - setContentType("text/html; charset=ISO-8859-1"); - } - - public void append(String content) { - html += content; - setText(header + html + footer); - if (dim != null) { - setPreferredSize(dim); - } - setCaretPosition(0); - } - - public void append(String tag, String content) { - html += "<" + tag + ">" + content + ""; - setText(header + html + footer); - if (dim != null) { - setPreferredSize(dim); - } - setCaretPosition(0); - } - - public JScrollPane getPane() { - JScrollPane scroll = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - scroll.setPreferredSize(dim); - return scroll; - } -} diff --git a/src/main/java/ai/nets/samj/gui2/icons/LoadingButton.java b/src/main/java/ai/nets/samj/gui/LoadingButton.java similarity index 99% rename from src/main/java/ai/nets/samj/gui2/icons/LoadingButton.java rename to src/main/java/ai/nets/samj/gui/LoadingButton.java index c002910..6d881ab 100644 --- a/src/main/java/ai/nets/samj/gui2/icons/LoadingButton.java +++ b/src/main/java/ai/nets/samj/gui/LoadingButton.java @@ -17,7 +17,7 @@ * limitations under the License. * #L% */ -package ai.nets.samj.gui.icons; +package ai.nets.samj.gui; import java.awt.Image; import java.io.File; diff --git a/src/main/java/ai/nets/samj/gui2/SAMJDialog.java b/src/main/java/ai/nets/samj/gui2/SAMJDialog.java deleted file mode 100644 index 718b918..0000000 --- a/src/main/java/ai/nets/samj/gui2/SAMJDialog.java +++ /dev/null @@ -1,618 +0,0 @@ -/*- - * #%L - * Library to call models of the family of SAM (Segment Anything Model) from Java - * %% - * Copyright (C) 2024 SAMJ developers. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ai.nets.samj.gui; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.stream.IntStream; - -import javax.swing.BorderFactory; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JFileChooser; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.JToolBar; -import javax.swing.SwingUtilities; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; - -import ai.nets.samj.communication.model.SAMModel; -import ai.nets.samj.communication.model.SAMModels; -import ai.nets.samj.gui.components.ComboBoxItem; -import ai.nets.samj.gui.components.GridPanel; -import ai.nets.samj.gui.icons.ButtonIcon; -import ai.nets.samj.gui.icons.LoadingButton; -import ai.nets.samj.gui.tools.Tools; -import ai.nets.samj.install.SamEnvManagerAbstract; -import ai.nets.samj.ui.PromptsResultsDisplay; -import ai.nets.samj.ui.PromptsResultsDisplay.SAMJException; -import ai.nets.samj.ui.SAMJLogger; -import ai.nets.samj.ui.UtilityMethods; -import ai.nets.samj.utils.Constants; - -/** - * Class that creates a default graphical user interface to interact with SAMJ models - * @author Carlos Garcia - * @author Daniel Sage - * @author Vladimir Ulman - */ -public class SAMJDialog extends JPanel implements ActionListener, PopupMenuListener { - - /** - * Unique serial version identifier - */ - private static final long serialVersionUID = -4362794696325316195L; - /** - * Name of the folder where the icon images for the dialog buttons are within the resources folder - */ - private static final String RESOURCES_FOLDER = "icons_samj/"; - /** - * Button that closes the GUI - */ - private JButton bnClose = new JButton("Close"); - /** - * Button that opens the help button - */ - private JButton bnHelp = new JButton("Help"); - /** - * Button that when pressed runs the SAMJ model encoder on the image selected - */ - - private LoadingButton bnStart; - /** TODO will this ever happen? - * Button for the auto-complete function, it is not ready yet - */ - private JButton bnComplete = new JButton("Coming soon..."); - /** - * Button to export the ROIs of an image to a instance segmentation labeling - */ - private JButton bnRoi2Labeling = new JButton("Export as Label"); - /** - * Text field containing copyrigth info - */ - private JTextField txtStatus = new JTextField("(c) SAMJ team 2024 - " + Constants.SAMJ_VERSION); - /** - * Button that activates the annotation with SAMJ models using bounding box prompts - */ - private ButtonIcon bnRect = new ButtonIcon("Rect", RESOURCES_FOLDER, "rect_samj.png"); - /** - * Button that activates the annotation with SAMJ models using point or multiple points prompts - */ - private ButtonIcon bnPoints = new ButtonIcon("Points", RESOURCES_FOLDER, "points_samj.png"); - /** - * Button that activates the annotation with SAMJ models using freeline prompts drawn with a freeline - */ - private ButtonIcon bnBrush = new ButtonIcon("Brush", RESOURCES_FOLDER, "brush_samj.png"); - /** - * Button that allows selecting a mask as a prompt for the image - */ - private ButtonIcon bnMask = new ButtonIcon("Mask", RESOURCES_FOLDER, "help_samj.png"); - /** - * Key that specifies that the user only wants the model to return the largest ROI - */ - private static final String ONLY_BIGGEST = "Only return largest ROI"; - /** - * Key that specifies that the user wants to return every ROI produced by the model - */ - private static final String ALL = "Return all ROIs"; - /** - * Combobox to choose whether to return only the biggest ROI produced by a SAM-based model - * or to return all what is outputed by the model - */ - private JComboBox cmbROIs = new JComboBox(new String[] {ONLY_BIGGEST, ALL}); - /** - * Checkbox that defines whether to add the segmentations to the roi manager or not - */ - private JCheckBox chkROIManager = new JCheckBox("Add to ROI Manager", true); - /** - * Combo box containing the images that are currently open - */ - private JComboBox cmbImage = new JComboBox(); - /** - * Panel containing the model choice and HTML panel with info about the models or installation - */ - private final SAMModelPanel panelModel; - /** - * Logger for the GUI actions - */ - private final SAMJLogger GUIsOwnLog; - /** - * Logger for the SAMJ models - */ - private final SAMJLogger logForNetworks; - /** - * Interface implementation that contains methods specific to each consumer software - */ - private final UtilityMethods consumerMethods; - /** - * Unique identifier of the selected image - */ - private Integer selectedID = null; - /** - * Functional interface that allows creating different instances of SAMJ models for different images - */ - public interface PromptsFunctionalInterface { PromptsResultsDisplay getPrompts(Object image); } - /** - * Implementation of the {@link PromptsFunctionalInterface} in the consumer software that allows the consumer - * software to provide prompts to the image of interest - */ - private PromptsFunctionalInterface displayInterface; - /** - * Implementation of the {@link PromptsResultsDisplay} in the consumer software that allows the consumer - * software to provide prompts to the image of interest - */ - private PromptsResultsDisplay display; - /** - * Whether the encodings for the current image are available or the image encoder needs to be - * run to provide annotations - */ - private boolean encodingsDone = false; - - /** - * Constructor that creates the default GUI for SAMJ. This GUI lets the user decide between - * several SAM based models. It also lets the user install all the models with just one click. - * Once installed the user can use any of the models to annotate providing the needed prompts - * in the way of the consumer software. - * @param availableModel - * all SAM-based models available to be used - * @param consumerMethods - * interface implementing methods on the consumer software - */ - public SAMJDialog(final SAMModels availableModel, UtilityMethods consumerMethods) { - this(availableModel, consumerMethods, null, null); - } - - /** - * Constructor that creates the default GUI for SAMJ. This GUI lets the user decide between - * several SAM based models. It also lets the user install all the models with just one click. - * Once installed the user can use any of the models to annotate providing the needed prompts - * in the way of the consumer software. - * @param availableModel - * all SAM-based models available to be used - * @param consumerMethods - * interface implementing methods on the consumer software - * @param guilogger - * interface implemented on the consumer software to log the events on the GUI - */ - public SAMJDialog(final SAMModels availableModel, - UtilityMethods consumerMethods, - final SAMJLogger guilogger) { - this(availableModel, consumerMethods, guilogger, null); - } - - /** - * Constructor that creates the default GUI for SAMJ. This GUI lets the user decide between - * several SAM based models. It also lets the user install all the models with just one click. - * Once installed the user can use any of the models to annotate providing the needed prompts - * in the way of the consumer software. - * @param availableModel - * all SAM-based models available to be used - * @param consumerMethods - * interface implementing methods on the consumer software - * @param guilogger - * interface implemented on the consumer software to log the events on the GUI - * @param networkLogger - * interface implemented on the consumer software to log the SAMJ models events - */ - public SAMJDialog(final SAMModels availableModel, UtilityMethods consumerMethods, - final SAMJLogger guilogger, - final SAMJLogger networkLogger) { - // TODO super(new JFrame(), "SAMJ Annotator"); - if (guilogger == null) { - this.GUIsOwnLog = new SAMJLogger () { - @Override - public void info(String text) {} - @Override - public void warn(String text) {} - @Override - public void error(String text) {} - }; - } else { - this.GUIsOwnLog = guilogger; - } - if (networkLogger == null) { - this.logForNetworks = new SAMJLogger () { - @Override - public void info(String text) {} - @Override - public void warn(String text) {} - @Override - public void error(String text) {} - }; - } else { - this.logForNetworks = networkLogger; - } - this.consumerMethods = consumerMethods; - - panelModel = new SAMModelPanel(availableModel, (boolean bol) -> this.updateInterface(bol)); - // Buttons - JPanel pnButtons = new JPanel(new FlowLayout()); - pnButtons.add(bnRect); - pnButtons.add(bnPoints); - pnButtons.add(bnBrush); - // TODO decide what to do pnButtons.add(bnMask); - - // Status - JToolBar pnStatus = new JToolBar(); - pnStatus.setFloatable(false); - pnStatus.setLayout(new BorderLayout()); - pnStatus.add(bnHelp, BorderLayout.EAST); - txtStatus.setEditable(false); - pnStatus.add(txtStatus, BorderLayout.CENTER); - pnStatus.add(bnClose, BorderLayout.WEST); - - JPanel pnActions = new JPanel(new FlowLayout()); - pnActions.add(bnRoi2Labeling); - pnActions.add(bnComplete); - pnActions.add(cmbROIs); - pnActions.add(chkROIManager); - // TODO think what to do with "add to roimanager" cehckbox - chkROIManager.setEnabled(false); - - List listImages = this.consumerMethods.getListOfOpenImages(); - for(ComboBoxItem item : listImages) - cmbImage.addItem(item); - cmbImage.addPopupMenuListener(this); - - GridBagLayout middleLayout = new GridBagLayout(); - //middleLayout.columnWidths = new int[] {1, 5}; - middleLayout.columnWeights = new double[] {1.0, 10.0}; - middleLayout.rowHeights = new int[] {1}; - JPanel panelImage = new JPanel(middleLayout); - panelImage.setBorder(BorderFactory.createEtchedBorder()); - - GridBagConstraints gbc0 = new GridBagConstraints(); - gbc0.fill = GridBagConstraints.HORIZONTAL; - gbc0.gridx = 0; - gbc0.insets = new Insets(5, 5, 5, 0); - bnStart = new LoadingButton("Go!", RESOURCES_FOLDER, "loading_animation_samj.gif", 20); - panelImage.add(bnStart, gbc0); - GridBagConstraints gbc1 = new GridBagConstraints(); - gbc1.fill = GridBagConstraints.HORIZONTAL; - gbc1.gridx = 1; - gbc1.insets = new Insets(5, 5, 5, 6); - panelImage.add(cmbImage, gbc1); - cmbImage.setPreferredSize(new Dimension(panelModel.getWidth(), 25)); - - GridPanel pn = new GridPanel(); - pn.place(1, 1, panelModel); - pn.place(2, 1, panelImage); - pn.place(3, 1, pnButtons); - pn.place(4, 1, pnActions); - - setLayout(new BorderLayout()); - add(pn, BorderLayout.NORTH); - add(pnStatus, BorderLayout.SOUTH); - - bnRoi2Labeling.addActionListener(this); - // TODO not ready yet bnComplete.addActionListener(this); - bnRoi2Labeling.setEnabled(false); - bnComplete.setEnabled(false); - bnClose.addActionListener(this); - bnHelp.addActionListener(this); - chkROIManager.addActionListener(this); - cmbROIs.addPopupMenuListener(this); - - bnStart.addActionListener(this); - bnRect.addActionListener(this); - bnPoints.addActionListener(this); - bnBrush.addActionListener(this); - // TODO decide what to do bnMask.addActionListener(this); - // TODO decide what to do bnMask.setDropTarget(new LocalDropTarget()); - - add(pn); - updateInterface(); - } - - /** - * Set the implementation of {@link PromptsFunctionalInterface} in the consumer software that allows working with - * image objects native to the consumer software - * @param pp - * the implementation of {@link PromptsFunctionalInterface} in the consumer software - */ - public void setPromptsProvider(PromptsFunctionalInterface pp) { - this.displayInterface = pp; - } - - /** - * - * @return the {@link SamEnvManagerAbstract} used to install and manage the models - */ - public SamEnvManagerAbstract getModelInstallationManager() { - return this.panelModel.getInstallationManager(); - } - - @Override - /** - * Method that controls that the logic of the buttons is correct - */ - public void actionPerformed(ActionEvent e) { - - if (e.getSource() == bnRect && !bnRect.isSelected()) { - display.switchToUsingRectangles(); - bnRect.setPressed(true); bnPoints.setPressed(false); bnBrush.setPressed(false); - } else if (e.getSource() == bnPoints && !bnPoints.isSelected()) { - display.switchToUsingPoints(); - bnRect.setPressed(false); bnPoints.setPressed(true); bnBrush.setPressed(false); - } else if (e.getSource() == bnBrush && !bnBrush.isSelected()) { - display.switchToUsingBrush(); - bnRect.setPressed(false); bnPoints.setPressed(false); bnBrush.setPressed(true); - } else if (e.getSource() == bnRect || e.getSource() == bnPoints || e.getSource() == bnBrush) { - display.switchToNone(); - bnRect.setPressed(false); bnPoints.setPressed(false); bnBrush.setPressed(false); - } else if (e.getSource() == bnHelp) { - Tools.help(); - } else if (e.getSource() == bnClose) { - this.close(); - } else if (e.getSource() == bnComplete) { - GUIsOwnLog.warn("TO DO call Auto-complete"); - } else if (e.getSource() == bnStart) { - if (!panelModel.getSelectedModel().isInstalled()) - GUIsOwnLog.warn("Not starting encoding as the selected model is not installed."); - - GUIsOwnLog.warn("Start the encoding"); - if (display == null - || !display.getFocusedImage().equals(((ComboBoxItem) this.cmbImage.getSelectedItem()).getValue())) { - display = displayInterface.getPrompts(((ComboBoxItem) this.cmbImage.getSelectedItem()).getValue()); - display.setRectIconConsumer(b -> this.bnRect.setPressed(b)); - display.setFreelineIconConsumer(b -> this.bnBrush.setPressed(b)); - display.setPointsIconConsumer(b -> this.bnPoints.setPressed(b)); - } - SAMModel selectedSAMModel = this.panelModel.getSelectedModel(); - this.bnStart.setPressed(true); - new Thread(() -> { - SAMModel netAdapter = null; - SwingUtilities.invokeLater(() -> { - panelModel.bnUninstall.setEnabled(false); - panelModel.bnInstall.setEnabled(false); - panelModel.rbModels.stream().forEach(btn -> btn.setEnabled(false)); - panelModel.progressInstallation.setIndeterminate(true); - }); - try { - netAdapter = panelModel.getSelectedModel(); - netAdapter.setImage(display.giveProcessedSubImage(selectedSAMModel), logForNetworks); - netAdapter.setReturnOnlyBiggest(cmbROIs.getSelectedItem().equals(ONLY_BIGGEST)); - } catch (Exception ex) { - display.notifyException(SAMJException.ENCODING, ex); - netAdapter = null; - } - SwingUtilities.invokeLater(() -> { - this.bnStart.setPressed(false); - panelModel.bnUninstall.setEnabled(panelModel.getSelectedModel().isInstalled()); - panelModel.bnInstall.setEnabled(!panelModel.getSelectedModel().isInstalled()); - panelModel.rbModels.stream().forEach(btn -> btn.setEnabled(true)); - panelModel.progressInstallation.setIndeterminate(false); - panelModel.progressInstallation.setValue(100); - }); - if (netAdapter == null) return; - display.switchToThisNet(netAdapter); - GUIsOwnLog.warn("Finished the encoding"); - SwingUtilities.invokeLater(() -> { - //TODO: encoding should be a property of a model - this.setEncodingsDone(true); - updateInterface(); - }); - }).start(); - return; - } else if (e.getSource() == chkROIManager) { - if (display != null) - display.enableAddingToRoiManager(chkROIManager.isSelected()); - } else if (e.getSource() == this.bnMask) { - JFileChooser fileChooser = new JFileChooser(); - int returnValue = fileChooser.showOpenDialog(null); - if (returnValue == JFileChooser.APPROVE_OPTION) { - this.display.improveExistingMask(fileChooser.getSelectedFile()); - } - } else if (e.getSource() == this.bnRoi2Labeling) { - this.display.exportImageLabeling(); - } - - updateInterface(); - } - - /** - * Update the interface - */ - public void updateInterface() { - updateInterface(false); - } - - /** - * Update the interface deleting the encodings and current Python process if needed - * @param deleteEncodings - * whether to delete or not the current encodings and the current Python process - */ - public void updateInterface(boolean deleteEncodings) { - if (deleteEncodings) this.setEncodingsDone(false); - if (!this.panelModel.isSelectedModelInstalled()) { - this.bnStart.setEnabled(false); - this.cmbImage.setEnabled(false); - setEncodingsDone(false); - } else if (this.panelModel.isSelectedModelInstalled() - && !this.encodingsDone - && this.cmbImage.getSelectedItem() != null - && ((ComboBoxItem) this.cmbImage.getSelectedItem()).getId() != -1) { - this.bnStart.setEnabled(true); - this.cmbImage.setEnabled(true); - // TODO think what to do this.chkROIManager.setEnabled(true); - this.cmbROIs.setEnabled(true); - } else if (this.panelModel.isSelectedModelInstalled() - && this.cmbImage.getSelectedItem() != null - && ((ComboBoxItem) this.cmbImage.getSelectedItem()).getId() != -1) { - this.bnStart.setEnabled(false); - this.cmbImage.setEnabled(true); - } else if (this.panelModel.isSelectedModelInstalled()) { - this.bnStart.setEnabled(false); - this.cmbImage.setEnabled(true); - } - // TODO not ready yet bnComplete.setEnabled(this.encodingsDone); - bnRoi2Labeling.setEnabled(this.encodingsDone); - // TODO think what to do chkROIManager.setEnabled(this.encodingsDone); - cmbROIs.setEnabled(this.encodingsDone); - bnRect.setEnabled(this.encodingsDone); - bnPoints.setEnabled(this.encodingsDone); - bnBrush.setEnabled(this.encodingsDone); - bnMask.setEnabled(this.encodingsDone); - if (!encodingsDone) { - this.bnRect.setPressed(false); - this.bnPoints.setPressed(false); - this.bnBrush.setPressed(false); - this.bnMask.setPressed(false); - } - } - - /** - * Set whether the encodings for the current image have been already calculated or not. - * If true, the encodings for the selected image do not need to be calculated until the iuser - * selects another image - * @param isDone - * whether the encodings for the current image have been calculated or not - */ - private void setEncodingsDone(boolean isDone) { - this.encodingsDone = isDone; - if (!isDone) { - if (display != null) { - display.notifyNetToClose(); - display.switchToNone(); - } - this.bnRect.setPressed(false); - this.bnPoints.setPressed(false); - this.bnBrush.setPressed(false); - this.bnMask.setPressed(false); - } - } - - /** - * Class that implements drag and drop for the mask prompt - */ - public class LocalDropTarget extends DropTarget { - - private static final long serialVersionUID = 286813958463411816L; - - @Override - /** - * Enable drag and drop on the mask prompt button and calculate the annotation once the - * mask is dropped - */ - public void drop(DropTargetDropEvent e) { - e.acceptDrop(DnDConstants.ACTION_COPY); - e.getTransferable().getTransferDataFlavors(); - Transferable transferable = e.getTransferable(); - DataFlavor[] flavors = transferable.getTransferDataFlavors(); - for (DataFlavor flavor : flavors) { - if (flavor.isFlavorJavaFileListType()) { - try { - List files = (List) transferable.getTransferData(flavor); - for (File file : files) { - GUIsOwnLog.info("Taking mask from file " + file.getAbsolutePath()); - display.improveExistingMask(file); - } - } - catch (UnsupportedFlavorException ex) { - ex.printStackTrace(); - } - catch (IOException ex) { - ex.printStackTrace(); - } - } - } - e.dropComplete(true); - super.drop(e); - } - } - - /** - * Close the panel and the plugin, deleting the python process if it exists - */ - public void close() { - if (display != null) { - display.notifyNetToClose(); - display.switchToNone(); - } - display = null; - this.panelModel.interrupExistingThreads(); - if (SwingUtilities.windowForComponent(this).isDisplayable()) - SwingUtilities.windowForComponent(this).dispose(); - } - - @Override - /** - * Update the list of images opened once the combobox pop up is open - */ - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - if (e.getSource() == this.cmbROIs) return; - Object item = this.cmbImage.getSelectedItem(); - List openSeqs = consumerMethods.getListOfOpenImages(); - ComboBoxItem[] objects = new ComboBoxItem[openSeqs.size()]; - for (int i = 0; i < objects.length; i ++) objects[i] = openSeqs.get(i); - DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(objects); - this.cmbImage.setModel(comboBoxModel); - if (item != null && objects.length != 0) - this.cmbImage.setSelectedIndex( - IntStream.range(0, objects.length).filter(i -> objects[i].getId() == ((ComboBoxItem) item).getId()).findFirst().orElse(0) - ); - } - - @Override - /** - * Check if the image selected has been changed once the combobox pop up is closed - */ - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - if (e.getSource() == this.cmbROIs) { - if (display != null) - display.getNetBeingUsed().setReturnOnlyBiggest(cmbROIs.getSelectedItem().equals(ONLY_BIGGEST)); - return; - } - ComboBoxItem item = (ComboBoxItem) this.cmbImage.getSelectedItem(); - if ( item == null || (item != null && item.getId() == -1) ) { - setEncodingsDone(false); - selectedID = null; - } else if (selectedID == null || (selectedID != null && selectedID != item.getId())) { - setEncodingsDone(false); - selectedID = item.getId(); - } - this.updateInterface(); - } - - @Override - /** - * Nothing - */ - public void popupMenuCanceled(PopupMenuEvent e) { - } -} diff --git a/src/main/java/ai/nets/samj/gui2/SAMModelPanel.java b/src/main/java/ai/nets/samj/gui2/SAMModelPanel.java deleted file mode 100644 index 40b9a23..0000000 --- a/src/main/java/ai/nets/samj/gui2/SAMModelPanel.java +++ /dev/null @@ -1,532 +0,0 @@ -/*- - * #%L - * Library to call models of the family of SAM (Segment Anything Model) from Java - * %% - * Copyright (C) 2024 SAMJ developers. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ai.nets.samj.gui; - -import java.awt.BorderLayout; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Objects; - -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.JToolBar; -import javax.swing.SwingUtilities; -import javax.swing.text.BadLocationException; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; - -import ai.nets.samj.communication.model.SAMModel; -import ai.nets.samj.communication.model.SAMModels; -import ai.nets.samj.gui.components.GridPanel; -import ai.nets.samj.gui.components.HTMLPane; -import ai.nets.samj.install.SamEnvManagerAbstract; -import io.bioimage.modelrunner.apposed.appose.Mamba; - -/** - * Class that creates a subpanel in the main panel of SAMJ default GUI. - * This panel handles model selection and installation. - * @author Carlos Garcia - * @author Daniel Sage - * @author Vladimir Ulman - */ -public class SAMModelPanel extends JPanel implements ActionListener { - /** - * Unique serial identifier - */ - private static final long serialVersionUID = 7623385356575804931L; - /** - * HTML panel where all the info about models and installation is going to be shown - */ - private HTMLPane info = new HTMLPane(450, 135); - /** - * Parameter used in the HTML panel during installation to know when to update - */ - private int waitingIter = 0; - /** - * Button that when clicked installs the model selected - */ - protected JButton bnInstall = new JButton("Install"); - /** - * Button that when clicked uninstalls the model selected - */ - protected JButton bnUninstall = new JButton("Uninstall"); - /** - * Progress bar used during the model installation. If the model is already installed it - * is full, if it is not it is empty - */ - protected JProgressBar progressInstallation = new JProgressBar(); - /** - * List of radio buttons that point to the models available - */ - protected ArrayList rbModels = new ArrayList(); - /** - * Object contianing a list of the models available, and whether they are selected, installed... - */ - private SAMModels models; - /** - * Index of hte selected model in the list of available models - */ - private int selectedModel = 0; - /** - * Whether the model selected has changed or not - */ - private boolean modelChanged = false; - /** - * Time format using to update the installation information - */ - private static DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss"); - /** - * Interface implemented at {@link SAMJDialog} to tell the parent JPanel to update the - * interface - */ - interface CallParent { - /** - * The implemented task to be done at {@link SAMJDialog} - * @param bool - * some helper parameter - */ - public void task(boolean bool); - } - /** - * Implementation of the interface {@link CallParent} - */ - private CallParent updateParent; - /** - * Thread used to install a model - */ - protected Thread installationThread; - /** - * Thread used to check the models that are installed - */ - protected Thread checkingThread; - - /** - * Constructor of the class. Creates a panel that contains the selection of available models - * and an html panel with info about the models that also displays the installation progress - * when the model is being installed - * @param models - * list of models that are available - * @param updateParent - * interface implementation on {@link SAMJDialog} that allows making modifications in the parent GUI - */ - public SAMModelPanel(SAMModels models, CallParent updateParent) { - super(); - this.updateParent = updateParent; - this.models = models; - JToolBar pnToolbarModel = new JToolBar(); - pnToolbarModel.setFloatable(false); - pnToolbarModel.setLayout(new GridLayout(1, 2)); - pnToolbarModel.add(bnInstall); - pnToolbarModel.add(bnUninstall); - - ButtonGroup group = new ButtonGroup(); - for(SAMModel model : models) { - model.getInstallationManger().setConsumer((str) -> addHtml(str)); - JRadioButton rb = new JRadioButton(model.getName(), false); - rbModels.add(rb); - rb.addActionListener(this); - group.add(rb); - } - rbModels.get(0).setSelected(true); - - JPanel pnManageModel = new JPanel(new BorderLayout()); - pnManageModel.add(pnToolbarModel, BorderLayout.NORTH); - pnManageModel.add(progressInstallation, BorderLayout.SOUTH); - pnManageModel.add(new JScrollPane(info), BorderLayout.CENTER); - - GridPanel pnModel = new GridPanel(true); - int col = 1; - for(JRadioButton rb : rbModels) - pnModel.place(1, col++, 1, 1, rb); - - pnModel.place(2, 1, 6, 2, pnManageModel); - - add(pnModel); - info.append("p", "Description of the model"); - info.append("p", "Link to source"); - bnInstall.addActionListener(this); - bnUninstall.addActionListener(this); - - updateInterface(); - checkInstalledModelsThread(); - checkingThread.start(); - Thread reportThread = reportHelperThread(checkingThread); - reportThread.start(); - } - - private void checkInstalledModelsThread() { - boolean isEDT = SwingUtilities.isEventDispatchThread(); - checkingThread= new Thread(() -> { - if (isEDT) { - SwingUtilities.invokeLater(() -> { - this.info.clear(); - this.installationInProcess(true); - this.info.append("FINDING INSTALLED MODELS"); - }); - } else { - this.info.clear(); - this.addHtml("FINDING INSTALLED MODELS"); - this.installationInProcess(true); - } - for(SAMModel model : models) { - if (Thread.currentThread().isInterrupted()) return; - model.setInstalled(model.getInstallationManger().checkEverythingInstalled()); - } - - if (isEDT) { - SwingUtilities.invokeLater(() -> { - this.installationInProcess(false); - rbModels.get(0).setSelected(true); - updateInterface(); - this.updateParent.task(false); - }); - } else { - this.installationInProcess(false); - rbModels.get(0).setSelected(true); - updateInterface(); - this.updateParent.task(false); - } - - }); - } - - private void updateInterface() { - for(int i=0; i { - try { - SwingUtilities.invokeLater(() -> installationInProcess(true)); - this.getSelectedModel().getInstallationManger().installEverything(); - getSelectedModel().setInstalled(true); - SwingUtilities.invokeLater(() -> { - installationInProcess(false); - this.updateParent.task(false);}); - } catch (Exception e1) { - e1.printStackTrace(); - SwingUtilities.invokeLater(() -> {installationInProcess(false); this.updateParent.task(false);}); - } - }); - } - - @Override - /** - * Mange the interface actions and logic - */ - public void actionPerformed(ActionEvent e) { - - if (e.getSource() == bnInstall) { - createInstallationThread(); - this.installationThread.start(); - // TODO remove Thread controlThread = createControlThread(installThread); - // TODO remove controlThread.start(); - } else if (e.getSource() == bnUninstall) { - Thread uninstallThread = createUninstallThread(); - uninstallThread.start(); - Thread reportThread = reportHelperThread(uninstallThread); - reportThread.start(); - // TODO remove Thread controlThread = createControlThread(uninstallThread); - // TODO remove controlThread.start(); - } - - updateInterface(); - this.updateParent.task(modelChanged); - modelChanged = false; - } - - /** - * Create thread in charge of reporting that there is an action in progress - * @param importantThread - * the thread where the process happens - * @return the thread that keeps the reporting so the user does not think that the activity stopped - */ - private Thread reportHelperThread(Thread importantThread) { - - Thread t = new Thread(() -> { - try { Thread.sleep(300); } catch (InterruptedException e) { return; } - while (importantThread.isAlive()) { - addHtml(""); - try { Thread.sleep(300); } catch (InterruptedException e) { return; } - } - SwingUtilities.invokeLater(() -> updateInterface()); - }); - return t; - } - - /** - * Create the Thread that is used to uninstall the selected model - * @return the thread where the models will be installed - */ - private Thread createUninstallThread() { - Thread installThread = new Thread(() -> { - this.getSelectedModel().setInstalled(false); - try { - SwingUtilities.invokeLater(() -> { - this.info.clear(); - this.addHtml("UNINSTALL MODEL"); - installationInProcess(true); - this.updateParent.task(true); - }); - uninstallModel(); - SwingUtilities.invokeLater(() -> { - installationInProcess(false);}); - } catch (Exception e1) { - e1.printStackTrace(); - SwingUtilities.invokeLater(() -> {installationInProcess(false); this.updateParent.task(false);}); - } - }); - return installThread; - } - - /** - * Uninstall the model from the user computer. Delete the required files - */ - private void uninstallModel() { - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - getSelectedModel().getInstallationManger().uninstall(); - getSelectedModel().setInstalled(false); - } - - /** - * Update the interface accordingly once the installation starts or finishes - * @param inProcess - * whether the installation is happening or it has finished already - */ - private void installationInProcess(boolean inProcess) { - info.clear(); - this.bnUninstall.setEnabled(inProcess ? false : getSelectedModel().isInstalled()); - this.bnInstall.setEnabled(inProcess ? false : !getSelectedModel().isInstalled()); - this.rbModels.stream().forEach(btn -> btn.setEnabled(!inProcess)); - this.progressInstallation.setIndeterminate(inProcess); - if (!inProcess) { - this.progressInstallation.setValue(this.getSelectedModel().isInstalled() ? 100 : 0); - this.updateInterface(); - } - } - - /** - * Sets the HTML text to be displayed. - * - * @param html - * HTML text. - * @throws NullPointerException - * If the HTML text is null. - */ - public void setHtml(String html) throws NullPointerException - { - Objects.requireNonNull(html, "HTML text is null"); - info.setText(formatHTML(html)); - info.setCaretPosition(0); - } - - /** - * Sets the HTML text to be displayed and moves the caret until the end of the text - * - * @param html - * HTML text. - * @throws NullPointerException - * If the HTML text is null. - */ - public void setHtmlAndDontMoveCaret(String html) throws NullPointerException { - Objects.requireNonNull(html, "HTML text is null"); - HTMLDocument doc = (HTMLDocument) info.getDocument(); - HTMLEditorKit editorKit = (HTMLEditorKit) info.getEditorKit(); - try { - doc.remove(0, doc.getLength()); - editorKit.insertHTML(doc, doc.getLength(), formatHTML(html), 0, 0, null); - } catch (BadLocationException | IOException e) { - e.printStackTrace(); - } - } - - /** - * @return HTML text shown in this component. - */ - public String getHtml() - { - return info.getText(); - } - - /** - * Add a String to the html pane in the correct format - * @param html - * the String to be converted into HTML and added to the HTML panel - */ - public void addHtml(String html) { - if (html == null) return; - if (html.trim().isEmpty()) { - html = manageEmptyMessage(html); - } else { - waitingIter = 0; - } - String nContent = formatHTML(html); - - SwingUtilities.invokeLater(() -> { - try { - HTMLDocument doc = (HTMLDocument) info.getDocument(); - HTMLEditorKit editorKit = (HTMLEditorKit) info.getEditorKit(); - editorKit.insertHTML(doc, doc.getLength(), nContent, 0, 0, null); - info.setCaretPosition(doc.getLength()); - } catch (Exception e) { - e.printStackTrace(); - } - }); - } - - /** - * Convert the input String into the correct HTML string for the HTML panel - * @param html - * the input Stirng to be formatted - * @return the String formatted into the correct HTML string - */ - private static String formatHTML(String html) { - html = html.replace(System.lineSeparator(), "
") - .replace(" ", " ") - .replace(" ", " ") - .replace(" ", " "); - - if (html.startsWith(Mamba.ERR_STREAM_UUUID)) { - html = "" + html.replace(Mamba.ERR_STREAM_UUUID, "") + ""; - } else { - html = "" + html + ""; - } - return html; - } - - /** - * Check if a message is empty, thus no information is comming. If the message is not empty, nothing is done. - * If it is, the html panel is updated so a changing installation in progress message appears - * @param html - * the message sent by the installation thread - * @return the message to be print in the html panel - */ - private String manageEmptyMessage(String html) { - String working = "Working, this might take several minutes"; - if (html.trim().isEmpty() && waitingIter == 0) { - html = LocalDateTime.now().format(DATE_FORMAT).toString() + " -- " + working + " ."; - waitingIter += 1; - } else if (html.trim().isEmpty() && waitingIter % 3 == 1) { - html = LocalDateTime.now().format(DATE_FORMAT).toString() + " -- " + working + " . ."; - int len = html.length() - (" .").length() + System.lineSeparator().length(); - SwingUtilities.invokeLater(() -> { - HTMLDocument doc = (HTMLDocument) info.getDocument(); - try {doc.remove(doc.getLength() - len, len);} catch (BadLocationException e) {} - }); - waitingIter += 1; - } else if (html.trim().isEmpty() && waitingIter % 3 == 2) { - html = LocalDateTime.now().format(DATE_FORMAT).toString() + " -- " + working + " . . ."; - int len = html.length() - (" .").length() + System.lineSeparator().length(); - SwingUtilities.invokeLater(() -> { - HTMLDocument doc = (HTMLDocument) info.getDocument(); - try {doc.remove(doc.getLength() - len, len);} catch (BadLocationException e) {} - }); - waitingIter += 1; - } else if (html.trim().isEmpty() && waitingIter % 3 == 0) { - html = LocalDateTime.now().format(DATE_FORMAT).toString() + " -- " + working + " ."; - int len = html.length() + (" . .").length() + System.lineSeparator().length(); - SwingUtilities.invokeLater(() -> { - HTMLDocument doc = (HTMLDocument) info.getDocument(); - try {doc.remove(doc.getLength() - len, len);} catch (BadLocationException e) {} - }); - waitingIter += 1; - } - return html; - } - - /** - * Tries to interrupt any thread that might be runnning - */ - public void interrupExistingThreads() { - if (this.checkingThread != null && this.checkingThread.isAlive()) - this.checkingThread.interrupt(); - if (this.installationThread != null && this.installationThread.isAlive()) - this.installationThread.interrupt(); - } -} - - diff --git a/src/main/java/ai/nets/samj/gui2/icons/ButtonIcon.java b/src/main/java/ai/nets/samj/gui2/icons/ButtonIcon.java deleted file mode 100644 index dddc2fe..0000000 --- a/src/main/java/ai/nets/samj/gui2/icons/ButtonIcon.java +++ /dev/null @@ -1,180 +0,0 @@ -/*- - * #%L - * Library to call models of the family of SAM (Segment Anything Model) from Java - * %% - * Copyright (C) 2024 SAMJ developers. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ai.nets.samj.gui.icons; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Image; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.CodeSource; -import java.security.ProtectionDomain; - -import javax.swing.BorderFactory; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.SwingConstants; -/** - * ButtonIcon class represents a custom JButton with icons for normal and pressed states. - * @author Carlos Garcia - * @author Daniel Sage - */ -public class ButtonIcon extends JButton { - - /** - * Serial version unique identifier - */ - private static final long serialVersionUID = 7396676607184967666L; - /** - * Prefix used in the file name of the images that represent that the button is pressed. - */ - private static final String PRESSED_PREFIX = "pressed_"; - /** - * Icon for when the button is not pressed - */ - private ImageIcon normalIcon; - /** - * Icon for when the button is pressed - */ - private ImageIcon pressedIcon; - /** - * String that is displayed inside the button - */ - private String text; - /** - * Original color of the buttons - */ - private Color color; - /** - * HTML used to format the button label text. First the color needs to be specified, then the - * text that wants to be in the button - */ - private static final String BTN_TEXT_HTML = "%s"; - /** - * Color code for the HTML String of the button for when the button is not pressed - */ - private static final String NOT_PRESSED_COLOR = "black"; - /** - * Color code for the HTML String of the button for when the button is pressed - */ - private static final String PRESSED_COLOR = "white"; - - /** - * Constructor. Creates a button that has an icon inside. The icon changes when pressed. - * @param text - * the text inside the button - * @param filePath - * the path to the file that contains the image that is going to be used - * @param filename - * the name of the file that is going to be used - */ - public ButtonIcon(String text, String filePath, String filename) { - super(); - this.text = text; - try { - normalIcon = getIcon(filePath + "/" + filename); - pressedIcon = getIcon(filePath + "/" + PRESSED_PREFIX + filename); - if (normalIcon != null) { - setIcon(normalIcon); - setBorder(BorderFactory.createEtchedBorder()); - setOpaque(false); - setContentAreaFilled(false); - setPreferredSize(new Dimension(58, 58)); - setVerticalTextPosition(SwingConstants.BOTTOM); - setHorizontalTextPosition(SwingConstants.CENTER); - setText(String.format(BTN_TEXT_HTML, NOT_PRESSED_COLOR, text)); - } - if (pressedIcon != null) { - this.setPressedIcon(pressedIcon); - } - } - catch (Exception ex) { - setText(text); - } - color = this.getBackground(); - } - - private ImageIcon getIcon(String path) { - while (path.indexOf("//") != -1) path = path.replace("//", "/"); - URL url = ButtonIcon.class.getClassLoader().getResource(path); - if (url == null) { - File f = findJarFile(ButtonIcon.class); - if (f.getName().endsWith(".jar")) { - try (URLClassLoader clsloader = new URLClassLoader(new URL[]{f.toURI().toURL()})){ - url = clsloader.getResource(path); - } catch (IOException e) { - } - } - } - if (url != null) { - ImageIcon img = new ImageIcon(url) ; - Image image = img.getImage(); - Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); - return new ImageIcon(scaled); - } - return null; - } - - private static File findJarFile(Class clazz) { - ProtectionDomain protectionDomain = clazz.getProtectionDomain(); - if (protectionDomain != null) { - CodeSource codeSource = protectionDomain.getCodeSource(); - if (codeSource != null) { - URL location = codeSource.getLocation(); - if (location != null) { - try { - return new File(URI.create(location.toURI().getSchemeSpecificPart()).getPath()); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - } - } - } - return null; - } - - /** - * Set the button as pressed or not pressed, changing the image displayed - * @param isPressed - * whether the button is pressed or not - */ - public void setPressed(boolean isPressed) { - if (isPressed) { - this.setIcon(pressedIcon); - this.setBackground(Color.BLACK); - this.setOpaque(true); - this.setContentAreaFilled(true); - } else { - this.setIcon(normalIcon); - this.setBackground(color); - this.setOpaque(false); - } - - setText(String.format(BTN_TEXT_HTML, isPressed ? PRESSED_COLOR : NOT_PRESSED_COLOR, text)); - - this.setSelected(isPressed); - this.validate(); - this.repaint(); - } -}