Skip to content

Commit

Permalink
Merge branch 'main' into with-cfg
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 authored Jul 28, 2023
2 parents d7fe6fb + 60f13ca commit dc3fbf3
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 41 deletions.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ dynamic = ["version"]
dependencies = [
"fonticon-materialdesignicons6",
"napari >=0.4.13",
"pymmcore-plus >=0.6.3",
"pymmcore-widgets >=0.3.0",
"pymmcore-plus >=0.7.1",
"pymmcore-widgets >=0.4.0",
"superqt >=0.3.1",
"tifffile",
"useq-schema >=0.1.0",
Expand Down
74 changes: 55 additions & 19 deletions src/napari_micromanager/_gui_objects/_mda_widget.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
from __future__ import annotations

import warnings
from pathlib import Path
from typing import TYPE_CHECKING, cast

from pymmcore_widgets import MDAWidget
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QCheckBox, QGridLayout, QSizePolicy, QVBoxLayout, QWidget
from qtpy.QtWidgets import (
QCheckBox,
QGridLayout,
QMessageBox,
QSizePolicy,
QVBoxLayout,
QWidget,
)
from useq import MDASequence

from napari_micromanager._mda_meta import SEQUENCE_META_KEY, SequenceMeta

from ._save_widget import SaveWidget

if TYPE_CHECKING:
from pathlib import Path

from pymmcore_plus import CMMCorePlus


Expand All @@ -25,36 +32,35 @@ def __init__(
) -> None:
super().__init__(include_run_button=True, parent=parent, mmcore=mmcore)

# add save widget
v_layout = cast(QVBoxLayout, self._central_widget.layout())
self._save_groupbox = SaveWidget()
self._save_groupbox.setSizePolicy(
QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed
)
self._save_groupbox.setChecked(False)

v_layout = cast(QVBoxLayout, self.layout())
self._save_groupbox.toggled.connect(self._on_save_toggled)
self._save_groupbox._directory.textChanged.connect(self._on_save_toggled)
self._save_groupbox._fname.textChanged.connect(self._on_save_toggled)
v_layout.insertWidget(0, self._save_groupbox)

self.channel_groupbox.setMinimumHeight(230)
# add split channel checkbox
self.channel_widget.setMinimumHeight(230)
self.checkBox_split_channels = QCheckBox(text="Split Channels")
self.checkBox_split_channels.toggled.connect(self._toggle_split_channel)
g_layout = cast(QGridLayout, self.channel_groupbox.layout())
g_layout = cast(QGridLayout, self.channel_widget.layout())
g_layout.addWidget(self.checkBox_split_channels, 1, 0)

# TODO: stage_pos_groupbox should have a valueChanged signal
# and that should be connected to _toggle_checkbox_save_pos
self._save_groupbox.toggled.connect(self._toggle_checkbox_save_pos)
self.position_groupbox.valueChanged.connect(self._toggle_checkbox_save_pos)
self.channel_groupbox.valueChanged.connect(self._toggle_split_channel)
self.channel_widget.valueChanged.connect(self._toggle_split_channel)

def _toggle_split_channel(self) -> None:
if not self.channel_groupbox.value():
self.checkBox_split_channels.setChecked(False)

def _toggle_checkbox_save_pos(self) -> None:
if (
self.position_groupbox.isChecked()
and len(self.position_groupbox.value()) > 0
not self.channel_widget.value()
or self.channel_widget._table.rowCount() == 1
):
self.checkBox_split_channels.setChecked(False)

def _on_save_toggled(self) -> None:
if self.position_widget.value():
self._save_groupbox._split_pos_checkbox.setEnabled(True)

else:
Expand Down Expand Up @@ -89,3 +95,33 @@ def set_state(self, state: dict | MDASequence | str | Path) -> None:

self.checkBox_split_channels.setChecked(meta.split_channels)
self._save_groupbox.set_state(meta)

def _on_run_clicked(self) -> None:
if (
self._save_groupbox.isChecked()
and not self._save_groupbox._directory.text()
):
warnings.warn("Select a directory to save the data.", stacklevel=2)
return

if not Path(self._save_groupbox._directory.text()).exists():
if self._create_new_folder():
Path(self._save_groupbox._directory.text()).mkdir(parents=True)
else:
return

super()._on_run_clicked()

def _create_new_folder(self) -> bool:
"""Create a QMessageBox to ask to create directory if it doesn't exist."""
msgBox = QMessageBox()
msgBox.setWindowTitle("Create Directory")
msgBox.setIcon(QMessageBox.Icon.Question)
msgBox.setText(
f"Directory {self._save_groupbox._directory.text()} "
"does not exist. Create it?"
)
msgBox.setStandardButtons(
QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel
)
return bool(msgBox.exec() == QMessageBox.StandardButton.Ok)
9 changes: 4 additions & 5 deletions src/napari_micromanager/_gui_objects/_toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pymmcore_plus import CMMCorePlus
from pymmcore_widgets import (
CameraRoiWidget,
ChannelGroupWidget,
ChannelWidget,
ConfigurationWidget,
DefaultCameraExposureWidget,
Expand Down Expand Up @@ -212,10 +213,12 @@ def _add_channels(self) -> QToolBar:
ch_toolbar.setMinimumHeight(TOOLBAR_SIZE)

wdg = self._create_groupbox()
wdg.layout().setSpacing(5)
wdg.setStyleSheet(GROUPBOX_STYLE)

ch_lbl = QLabel(text="Channel:")
wdg.layout().addWidget(ch_lbl)
wdg.layout().addWidget(ChannelGroupWidget())
wdg.layout().addWidget(ChannelWidget())

ch_toolbar.addWidget(wdg)
Expand Down Expand Up @@ -301,7 +304,7 @@ def _make_tool_button(self, tooltip: str, btn_icon: str) -> QPushButton:
def _add_plugins_toolbar(self) -> QToolBar:
"""Add a QToolBar containing plugins QPushButtons.
e.g. MDA, Explore, ...
e.g. MDA, ...
QPushButtons are connected to the `_show_dock_widget` method.
Expand All @@ -318,11 +321,7 @@ def _add_plugins_toolbar(self) -> QToolBar:
wdg.setStyleSheet("border: 0px;")

mda = self._make_plugin_button("MDA", "MultiDimensional Acquisition")
explorer = self._make_plugin_button(
"Explorer", "MultiDimensional Grid Acquisition"
)
wdg.layout().addWidget(mda)
wdg.layout().addWidget(explorer)

plgs_toolbar.addWidget(wdg)

Expand Down
14 changes: 5 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import useq
from napari_micromanager._mda_meta import SEQUENCE_META_KEY, SequenceMeta
from napari_micromanager.main_window import MainWindow
from pymmcore_plus import CMMCorePlus, _logger
from pymmcore_plus import CMMCorePlus, configure_logging

_logger.set_log_level("CRITICAL")
configure_logging(strerr_level="CRITICAL")


# to create a new CMMCorePlus() for every test
Expand Down Expand Up @@ -45,13 +45,9 @@ def main_window(core: CMMCorePlus, make_napari_viewer):


@pytest.fixture(params=MDAS, ids=MDA_IDS)
def mda_sequence(request) -> useq.MDASequence:
meta = {
SEQUENCE_META_KEY: SequenceMeta(
mode="mda", file_name="test_mda", should_save=True
)
}
return useq.MDASequence(**request.param, metadata=meta)
def mda_sequence(request: pytest.FixtureRequest) -> useq.MDASequence:
seq_meta = SequenceMeta(mode="mda", file_name="test_mda", should_save=True)
return useq.MDASequence(**request.param, metadata={SEQUENCE_META_KEY: seq_meta})


@pytest.fixture(params=[True, False], ids=["splitC", "no_splitC"])
Expand Down
7 changes: 1 addition & 6 deletions tests/test_multid_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,7 @@ def test_saving_mda(
expected_shape.pop(mda.used_axes.find("c"))
nfiles = len(list((tmp_path / f"{meta.file_name}_000").iterdir()))
assert nfiles == 2 if multiC else 1
elif splitC:
# FIXME: from tlambert03: just doing this to make the test pass
# but this should be tested more thoroughly
assert [x.name for x in tmp_path.rglob("*.tif")] == [
f"{meta.file_name}_000_DAPI_000.tif"
]
# splitC with one channel is the same as not split
else:
assert [p.name for p in tmp_path.iterdir()] == [f"{meta.file_name}_000.tif"]
assert data_shape == expected_shape
Expand Down

0 comments on commit dc3fbf3

Please sign in to comment.