Skip to content

feat: Add MDA progress widget #268

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
25 changes: 20 additions & 5 deletions examples/mda_demo.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import useq
from pymmcore_plus import CMMCorePlus
from qtpy.QtWidgets import (
QApplication,
Expand Down Expand Up @@ -29,7 +30,7 @@ class MDA(QWidget):
events to print out the current state of the acquisition.
"""

def __init__(self) -> None:
def __init__(self, seq: useq.MDASequence | None = None) -> None:
super().__init__()
# get the CMMCore instance and load the default config
self.mmc = CMMCorePlus.instance()
Expand All @@ -43,6 +44,9 @@ def __init__(self) -> None:

# instantiate the MDAWidget, and a couple labels for feedback
self.mda = MDAWidget()
if seq:
self.mda.setValue(seq)

self.mda.valueChanged.connect(self._update_sequence)
self.current_sequence = QLabel('... enter info and click "Run"')
self.current_event = QLabel("... current event info will appear here")
Expand All @@ -60,7 +64,8 @@ def __init__(self) -> None:

def _update_sequence(self) -> None:
"""Called when the MDA sequence starts."""
self.current_sequence.setText(self.mda.value().yaml(exclude_defaults=True))
mda_seq = self.mda.value()
self.current_sequence.setText(mda_seq.yaml(exclude_defaults=True))

def _on_frame(self, image: np.ndarray, event: MDAEvent) -> None:
"""Called each time a frame is acquired."""
Expand All @@ -84,6 +89,16 @@ def _on_pause(self, state: bool) -> None:

if __name__ == "__main__":
app = QApplication([])
frame = MDA()
frame.show()
app.exec_()

seq = useq.MDASequence(
time_plan=useq.TIntervalLoops(interval=1, loops=4),
z_plan=useq.ZRangeAround(range=2, step=0.5),
channels=[
{"config": "DAPI", "exposure": 10},
{"config": "FITC", "exposure": 20},
],
)
wdg = MDA(seq)
wdg.show()

app.exec()
20 changes: 0 additions & 20 deletions examples/z_plan_widget.py

This file was deleted.

3 changes: 2 additions & 1 deletion src/pymmcore_widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"InstallWidget",
"LiveButton",
"MDAWidget",
"MDAProgressWidget",
"MDASequenceWidget",
"ObjectivesWidget",
"ObjectivesPixelConfigurationWidget",
Expand Down Expand Up @@ -66,7 +67,7 @@
from .device_properties import PropertiesWidget, PropertyBrowser, PropertyWidget
from .hcs import HCSWizard
from .hcwizard import ConfigWizard
from .mda import MDAWidget
from .mda import MDAProgressWidget, MDAWidget
from .useq_widgets import (
ChannelTable,
GridPlanWidget,
Expand Down
3 changes: 2 additions & 1 deletion src/pymmcore_widgets/mda/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""MDA widgets."""

from ._core_mda import MDAWidget
from ._mda_progress_widget import MDAProgressWidget

__all__ = ["MDAWidget"]
__all__ = ["MDAWidget", "MDAProgressWidget"]
25 changes: 23 additions & 2 deletions src/pymmcore_widgets/mda/_core_mda.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from qtpy.QtCore import QSize, Qt
from qtpy.QtWidgets import (
QBoxLayout,
QFrame,
QHBoxLayout,
QMessageBox,
QPushButton,
Expand All @@ -26,6 +27,7 @@
from ._core_grid import CoreConnectedGridPlanWidget
from ._core_positions import CoreConnectedPositionTable
from ._core_z import CoreConnectedZPlanWidget
from ._mda_progress_widget import MDAProgressBars
from ._save_widget import SaveGroupBox


Expand Down Expand Up @@ -76,6 +78,18 @@ class MDAWidget(MDASequenceWidget):
By default, None. If not specified, the widget will use the active
(or create a new)
[`CMMCorePlus.instance`][pymmcore_plus.core._mmcore_plus.CMMCorePlus.instance].

Attributes
----------
progress_bars : MDAProgressBars
The progress bars widget that shows the progress of the MDA sequence, it
also provides a visual representation of the size of each dimension.
Can be hidden with `self.progress_bars.hide()`.
save_info : SaveGroupBox
The save widget that allows the user to specify the save directory and
file name. Can be hidden with `self.save_info.hide()`.
control_btns : _MDAControlButtons
The run, pause, and cancel buttons at the bottom of the MDA Widget.
"""

def __init__(
Expand All @@ -86,6 +100,9 @@ def __init__(

super().__init__(parent=parent, tab_widget=CoreMDATabs(None, self._mmc))

self.progress_bars = MDAProgressBars()
self.progress_bars.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Raised)

self.save_info = SaveGroupBox(parent=self)
self.save_info.valueChanged.connect(self.valueChanged)
self.control_btns = _MDAControlButtons(self._mmc, self)
Expand All @@ -97,11 +114,11 @@ def __init__(
# ------------ layout ------------

layout = cast("QBoxLayout", self.layout())
layout.insertWidget(0, self.save_info)
layout.insertWidget(0, self.progress_bars)
layout.insertWidget(1, self.save_info)
layout.addWidget(self.control_btns)

# ------------ connect signals ------------

self.control_btns.run_btn.clicked.connect(self.run_mda)
self.control_btns.pause_btn.released.connect(self._mmc.mda.toggle_pause)
self.control_btns.cancel_btn.released.connect(self._mmc.mda.cancel)
Expand Down Expand Up @@ -249,6 +266,10 @@ def run_mda(self) -> None:

# ------------------- private Methods ----------------------

def _on_value_change(self) -> None:
super()._on_value_change()
self.progress_bars.prepare_sequence(self.value())

def _on_sys_config_loaded(self) -> None:
# TODO: connect objective change event to update suggested step
self.z_plan.setSuggestedStep(_guess_NA(self._mmc) or 0.5)
Expand Down
Loading
Loading