diff --git a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerFrame.java b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerFrame.java index cc78ae3..9d3d356 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerFrame.java +++ b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerFrame.java @@ -1,7 +1,6 @@ package org.micromanager.lightsheetmanager; import com.google.common.eventbus.Subscribe; -import mmcorej.CMMCore; import net.miginfocom.swing.MigLayout; import org.micromanager.Studio; @@ -12,7 +11,6 @@ import org.micromanager.lightsheetmanager.gui.components.Label; import org.micromanager.lightsheetmanager.gui.data.Icons; import org.micromanager.lightsheetmanager.gui.tabs.TabPanel; -import org.micromanager.lightsheetmanager.gui.tabs.navigation.NavigationPanel; import org.micromanager.lightsheetmanager.gui.utils.WindowUtils; import org.micromanager.internal.utils.WindowPositioning; @@ -22,13 +20,10 @@ /** * Main GUI frame. - * */ public class LightSheetManagerFrame extends JFrame { private final Studio studio_; - private final CMMCore core_; - private TabPanel tabPanel_; private final LightSheetManager model_; @@ -36,7 +31,6 @@ public class LightSheetManagerFrame extends JFrame { public LightSheetManagerFrame(final LightSheetManager model, final boolean isLoaded) { model_ = Objects.requireNonNull(model); studio_ = model_.studio(); - core_ = studio_.core(); // save window position WindowPositioning.setUpBoundsMemory( @@ -140,15 +134,6 @@ private void createUserInterface() { } - // TODO: remove when there is a better method to stop polling from acq engine - public NavigationPanel getNavigationPanel() { - return tabPanel_.getNavigationTab().getNavigationPanel(); - } - - public Studio getStudio_() { - return studio_; - } - /** * Detect settings after the model is loaded, * ask to change settings with dialogs. diff --git a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java index 771f113..fa42694 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java +++ b/src/main/java/org/micromanager/lightsheetmanager/LightSheetManagerPlugin.java @@ -12,7 +12,7 @@ public class LightSheetManagerPlugin implements MenuPlugin, SciJavaPlugin { public static final String copyright = "Applied Scientific Instrumentation (ASI), 2022-2026"; public static final String description = "A plugin to control various types of light sheet microscopes."; public static final String menuName = "Light Sheet Manager"; - public static final String version = "0.5.6"; + public static final String version = "0.5.7"; private Studio studio_; private LightSheetManager model_; diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettingsSCAPE.java b/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettingsSCAPE.java index 0b28275..281c3a3 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettingsSCAPE.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/AcquisitionSettingsSCAPE.java @@ -33,18 +33,25 @@ interface Builder> extends AcquisitionS T cameraMode(final CameraMode cameraMode); /** - * Sets the first camera used in an acquisition. + * Sets the imaging camera order. * - * @param primaryCamera the primary camera + * @param cameraOrder the imaging camera order */ - T primaryCamera(final String primaryCamera); + T imagingCameraOrder(final String[] cameraOrder); + + /** + * Sets the active imaging cameras. + * + * @param camerasActive the active imaging cameras + */ + T imagingCamerasActive(final boolean[] camerasActive); /** * Sets the acquisition to acquire from multiple simultaneous cameras. * * @param state true if acquiring from both sides */ - T isAcqFromBothSides(final boolean state); + T useSimultaneousCameras(final boolean state); /** * Sets the acquisition to use channels. @@ -202,18 +209,25 @@ interface Builder> extends AcquisitionS CameraMode cameraMode(); /** - * Returns the name of the primary camera. + * Returns the imaging camera order. + * + * @return the imaging camera order + */ + String[] imagingCameraOrder(); + + /** + * Returns an array of active imaging cameras. * - * @return the name of the primary camera + * @return an array of active imaging cameras */ - String primaryCamera(); + boolean[] imagingCamerasActive(); /** * Returns true if acquiring from both imaging cameras. * * @return true if acquiring from both imaging cameras */ - boolean isAcqFromBothSides(); + boolean isUsingSimultaneousCameras(); /** * Returns true if using channels. diff --git a/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java b/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java index 171b4e9..4b09163 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java +++ b/src/main/java/org/micromanager/lightsheetmanager/api/internal/DefaultAcquisitionSettingsSCAPE.java @@ -19,8 +19,9 @@ public static class Builder extends DefaultAcquisitionSettings.Builder private AcquisitionMode acquisitionMode_ = AcquisitionMode.NO_SCAN; private CameraMode cameraMode_ = CameraMode.EDGE; - private String primaryCamera_ = ""; - private boolean isAcqFromBothSides_ = false; + private String[] imagingCameraOrder_ = {}; + private boolean[] imagingCamerasActive_ = {}; + private boolean useSimultaneousCameras_ = true; private boolean useChannels_ = false; private boolean useTimePoints_ = false; @@ -56,8 +57,9 @@ public Builder(final DefaultAcquisitionSettingsSCAPE acqSettings) { } acquisitionMode_ = acqSettings.acquisitionMode_; cameraMode_ = acqSettings.cameraMode_; - primaryCamera_ = acqSettings.primaryCamera_; - isAcqFromBothSides_ = acqSettings.isAcqFromBothSides_; + imagingCameraOrder_ = acqSettings.imagingCameraOrder_; + imagingCamerasActive_ = acqSettings.imagingCamerasActive_; + useSimultaneousCameras_ = acqSettings.useSimultaneousCameras_; useChannels_ = acqSettings.useChannels_; useTimePoints_ = acqSettings.useTimePoints_; useAutofocus_ = acqSettings.useAutofocus_; @@ -100,13 +102,24 @@ public Builder cameraMode(final CameraMode cameraMode) { } /** - * Sets the first camera used in an acquisition. + * Sets the imaging camera order. * - * @param primaryCamera the primary camera + * @param cameraOrder the imaging camera order */ @Override - public Builder primaryCamera(final String primaryCamera) { - primaryCamera_ = primaryCamera; + public Builder imagingCameraOrder(final String[] cameraOrder) { + imagingCameraOrder_ = cameraOrder; + return this; + } + + /** + * Sets the active imaging cameras. + * + * @param camerasActive the active imaging cameras + */ + @Override + public Builder imagingCamerasActive(final boolean[] camerasActive) { + imagingCamerasActive_ = camerasActive; return this; } @@ -116,8 +129,8 @@ public Builder primaryCamera(final String primaryCamera) { * @param state true if acquiring from both sides */ @Override - public Builder isAcqFromBothSides(final boolean state) { - isAcqFromBothSides_ = state; + public Builder useSimultaneousCameras(final boolean state) { + useSimultaneousCameras_ = state; return this; } @@ -309,8 +322,9 @@ public String toString() { private final AcquisitionMode acquisitionMode_; private final CameraMode cameraMode_; - private final String primaryCamera_; - private final boolean isAcqFromBothSides_; + private final String[] imagingCameraOrder_; + private final boolean[] imagingCamerasActive_; + private final boolean useSimultaneousCameras_; private final boolean useChannels_; private final boolean useTimePoints_; @@ -341,8 +355,9 @@ private DefaultAcquisitionSettingsSCAPE(Builder builder) { } acquisitionMode_ = builder.acquisitionMode_; cameraMode_ = builder.cameraMode_; - primaryCamera_ = builder.primaryCamera_; - isAcqFromBothSides_ = builder.isAcqFromBothSides_; + imagingCameraOrder_ = builder.imagingCameraOrder_; + imagingCamerasActive_ = builder.imagingCamerasActive_; + useSimultaneousCameras_ = builder.useSimultaneousCameras_; useChannels_ = builder.useChannels_; useTimePoints_ = builder.useTimePoints_; useAutofocus_ = builder.useAutofocus_; @@ -486,23 +501,33 @@ public CameraMode cameraMode() { } /** - * Returns the name of the primary camera. + * Returns the imaging camera order. + * + * @return the imaging camera order + */ + @Override + public String[] imagingCameraOrder() { + return imagingCameraOrder_; + } + + /** + * Returns an array of active imaging cameras. * - * @return the name of the primary camera + * @return an array of active imaging cameras */ @Override - public String primaryCamera() { - return primaryCamera_; + public boolean[] imagingCamerasActive() { + return imagingCamerasActive_; } /** - * Returns true if acquiring from both imaging cameras. + * Returns true if acquiring from all active imaging cameras on a single view. * - * @return true if acquiring from both imaging cameras + * @return true if acquiring from all active imaging cameras on a single view */ @Override - public boolean isAcqFromBothSides() { - return isAcqFromBothSides_; + public boolean isUsingSimultaneousCameras() { + return useSimultaneousCameras_; } /** diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/components/RadioButton.java b/src/main/java/org/micromanager/lightsheetmanager/gui/components/RadioButton.java index 6ce78c6..4ce2f23 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/components/RadioButton.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/components/RadioButton.java @@ -31,11 +31,11 @@ public RadioButton(final String[] names, final String selected) { setSelected(selected, true); } - public RadioButton(final String[] names, final String selected, final int type, final int alignment) { + public RadioButton(final String[] names, final String selected, final int alignment) { setMigLayout("", "", ""); buttonGroup = new ButtonGroup(); buttons = new ArrayList<>(); - setLayoutStyle(type, alignment); + setLayoutStyle(alignment, LEFT); addButtons(names); setSelected(selected, true); } diff --git a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/CameraTab.java b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/CameraTab.java index b06d8b7..8451f8f 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/CameraTab.java +++ b/src/main/java/org/micromanager/lightsheetmanager/gui/tabs/CameraTab.java @@ -9,10 +9,13 @@ import org.micromanager.lightsheetmanager.gui.components.ListeningPanel; import org.micromanager.lightsheetmanager.gui.components.Panel; import org.micromanager.lightsheetmanager.LightSheetManager; +import org.micromanager.lightsheetmanager.gui.components.RadioButton; import org.micromanager.lightsheetmanager.model.devices.cameras.CameraBase; +import javax.swing.JLabel; import java.awt.Font; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; public class CameraTab extends Panel implements ListeningPanel { @@ -24,9 +27,11 @@ public class CameraTab extends Panel implements ListeningPanel { private Button btnEigthROI_; private Button btnCustomROI_; private Button btnGetCurrentROI_; + private ComboBox cmbCameraTriggerMode_; - private ComboBox cmbPrimaryCamera_; - private CheckBox cbxAcquireFromBothSides_; + private RadioButton radPrimaryCamera_; + private CheckBox cbxUseSimultaneousCameras_; + private List cbxActiveCameras_; private final TabPanel tabPanel_; private final LightSheetManager model_; @@ -43,20 +48,20 @@ private void createUserInterface() { final Panel pnlROI = new Panel("Imaging ROI"); final Panel pnlCameraTrigger = new Panel("Camera Trigger Mode"); - final Panel pnlPrimaryCamera = new Panel("First Camera"); + final Panel pnlPrimaryCamera = new Panel("Simultaneous Cameras"); final Label lblXOffset = new Label("X Offset:"); final Label lblYOffset = new Label("Y Offset:"); final Label lblWidth = new Label("Width:"); final Label lblHeight = new Label("Height:"); - btnUnchangedROI_ = new Button("Unchanged", 120, 30); - btnFullROI_ = new Button("Full", 60, 30); - btnHalfROI_ = new Button("1/2", 60, 30); - btnQuarterROI_ = new Button("1/4", 60, 30); - btnEigthROI_ = new Button("1/8", 60, 30); - btnCustomROI_ = new Button("Custom", 120, 30); - btnGetCurrentROI_ = new Button("Get Current ROI", 120, 30); + btnUnchangedROI_ = new Button("Unchanged", 140, 30); + btnFullROI_ = new Button("Full", 70, 30); + btnHalfROI_ = new Button("1/2", 70, 30); + btnQuarterROI_ = new Button("1/4", 70, 30); + btnEigthROI_ = new Button("1/8", 70, 30); + btnCustomROI_ = new Button("Custom", 140, 30); + btnGetCurrentROI_ = new Button("Get Current ROI", 140, 30); // get the imaging camera library final CameraBase camera = model_.devices().firstImagingCamera(); @@ -67,18 +72,39 @@ private void createUserInterface() { // validate that the logical device name exists final String[] cameraNames = model_.devices().imagingCameraNames(); - String primaryCamera = model_.acquisitions().settings().primaryCamera(); - if (!Arrays.asList(cameraNames).contains(primaryCamera)) { - model_.acquisitions().settingsBuilder().primaryCamera(cameraNames[0]); - primaryCamera = cameraNames[0]; // use first device as default - model_.studio().logs().logMessage( - "Logical device name " + primaryCamera + " not found, use " + cameraNames[0] + " instead."); + cbxActiveCameras_ = new ArrayList<>(cameraNames.length); + + // the first element of the list is the primary camera + String primaryCamera = ""; + final String[] cameraOrder = model_.acquisitions().settings().imagingCameraOrder(); + if (cameraOrder.length > 0) { + primaryCamera = cameraOrder[0]; } // simultaneous camera settings - cmbPrimaryCamera_ = new ComboBox(cameraNames, primaryCamera); - cbxAcquireFromBothSides_ = new CheckBox("Acquire from both sides simultaneously", - model_.acquisitions().settings().isAcqFromBothSides()); + radPrimaryCamera_ = new RadioButton(cameraNames, primaryCamera, RadioButton.VERTICAL); + cbxUseSimultaneousCameras_ = new CheckBox("Acquire from all active cameras simultaneously", + model_.acquisitions().settings().isUsingSimultaneousCameras()); + + // active check boxes + final Panel pnlCheckboxes = new Panel(); + pnlCheckboxes.setMigLayout( + "", + "", + "[]6[]" + ); + + final boolean[] activeCameras = model_.acquisitions().settings().imagingCamerasActive(); + for (int i = 0; i < cameraNames.length; i++) { + final CheckBox checkBox = new CheckBox("Active", activeCameras[i]); + pnlCheckboxes.add(checkBox, "wrap"); + cbxActiveCameras_.add(checkBox); + } + + final Panel pnlCameraSelectionRow = new Panel(); + pnlCameraSelectionRow.add(new JLabel("Select Primary Camera"), "wrap"); + pnlCameraSelectionRow.add(radPrimaryCamera_, ""); + pnlCameraSelectionRow.add(pnlCheckboxes, "gaptop 2px"); pnlROI.add(btnUnchangedROI_, "span 2, wrap"); pnlROI.add(btnFullROI_, ""); @@ -95,13 +121,15 @@ private void createUserInterface() { pnlCameraTrigger.add(cmbCameraTriggerMode_, ""); add(lblTitle, "wrap"); - add(pnlROI, "wrap"); - add(pnlCameraTrigger, "wrap"); if (model_.devices().adapter().numSimultaneousCameras() > 1) { - pnlPrimaryCamera.add(cmbPrimaryCamera_, "wrap"); + pnlPrimaryCamera.add(pnlCameraSelectionRow, "wrap"); + pnlPrimaryCamera.add(cbxUseSimultaneousCameras_, "wrap"); + add(pnlROI, "growx"); add(pnlPrimaryCamera, "wrap"); - add(cbxAcquireFromBothSides_, ""); + } else { + add(pnlROI, "wrap"); } + add(pnlCameraTrigger, "growx"); } private void createEventHandlers() { @@ -114,19 +142,48 @@ private void createEventHandlers() { tabPanel_.swapSetupPathPanels(cameraMode); }); + // TODO(Brandon): uses simple ordering that works for 2 cameras, + // but needs additional work to support 4. Use a reorderable JTable? + // select primary camera - cmbPrimaryCamera_.registerListener(e -> - model_.acquisitions().settingsBuilder().primaryCamera( - cmbPrimaryCamera_.getSelected())); + radPrimaryCamera_.registerListener(e -> { + final String selected = radPrimaryCamera_.getSelectedButtonText(); + final String[] cameraNames = model_.devices().imagingCameraNames(); + final List cameraOrder = new ArrayList<>(); + // the first array index is the primary camera + cameraOrder.add(selected); + for (String cameraName : cameraNames) { + if (!selected.equals(cameraName)) { + cameraOrder.add(cameraName); + } + } + model_.acquisitions().settingsBuilder() + .imagingCameraOrder(cameraOrder.toArray(String[]::new)); + }); - // use both cameras - cbxAcquireFromBothSides_.registerListener(e -> - model_.acquisitions().settingsBuilder().isAcqFromBothSides( - cbxAcquireFromBothSides_.isSelected())); + // active camera check boxes + for (CheckBox cbx : cbxActiveCameras_) { + cbx.registerListener(e -> + model_.acquisitions().settingsBuilder() + .imagingCamerasActive(activeCameras())); + } + + // use all active cameras + cbxUseSimultaneousCameras_.registerListener(e -> + model_.acquisitions().settingsBuilder() + .useSimultaneousCameras(cbxUseSimultaneousCameras_.isSelected())); //model_.studio().core().setROI(); } + private boolean[] activeCameras() { + boolean[] active = new boolean[cbxActiveCameras_.size()]; + for (int i = 0; i < cbxActiveCameras_.size(); i++) { + active[i] = cbxActiveCameras_.get(i).isSelected(); + } + return active; + } + @Override public void selected() { @@ -136,4 +193,4 @@ public void selected() { public void unselected() { } -} \ No newline at end of file +} diff --git a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineSCAPE.java b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineSCAPE.java index 9e23bc3..5cbf21d 100644 --- a/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineSCAPE.java +++ b/src/main/java/org/micromanager/lightsheetmanager/model/acquisitions/AcquisitionEngineSCAPE.java @@ -543,10 +543,10 @@ public void close() { final LightSheetDeviceManager adapter = model_.devices().adapter(); if (adapter.numSimultaneousCameras() > 1 && adapter.numImagingPaths() == 1) { // multiple simultaneous cameras - if (model_.acquisitions().settings().isAcqFromBothSides()) { + if (model_.acquisitions().settings().isUsingSimultaneousCameras()) { // use 2 cameras String secondCamera = "ImagingCamera2"; - final String primaryCamera = model_.acquisitions().settings().primaryCamera(); + final String primaryCamera = "ImagingCamera1";//model_.acquisitions().settings().primaryCamera(); if (primaryCamera.equals(secondCamera)) { secondCamera = "ImagingCamera1"; } @@ -556,7 +556,7 @@ public void close() { }; } else { // use 1 camera - final String camera = model_.acquisitions().settings().primaryCamera(); + final String camera = "ImagingCamera1"; // model_.acquisitions().settings().primaryCamera(); cameraNames = new String[] { model_.devices().device(camera).getDeviceName(), };