def test_state_device_widget(qtbot, global_mmcore: CMMCorePlus): for label in global_mmcore.getLoadedDevicesOfType(DeviceType.StateDevice): wdg: StateDeviceWidget = DeviceWidget.for_device(label) qtbot.addWidget(wdg) wdg.show() assert wdg.deviceLabel() == label # assert wdg.deviceName() == "DObjective" assert global_mmcore.getStateLabel(label) == wdg._combo.currentText() assert global_mmcore.getState(label) == wdg._combo.currentIndex() start_state = wdg.state() next_state = (wdg.state() + 1) % len(wdg.stateLabels()) with qtbot.waitSignal(global_mmcore.events.propertyChanged): global_mmcore.setState(label, next_state) assert wdg.state() != start_state assert wdg.state() == global_mmcore.getState( label) == wdg._combo.currentIndex() assert (wdg.stateLabel() == global_mmcore.getStateLabel(label) == wdg._combo.currentText()) wdg._disconnect() # once disconnected, core changes shouldn't call out to the widget global_mmcore.setState(label, start_state) assert global_mmcore.getStateLabel(label) != wdg._combo.currentText()
def test_search_paths(core: CMMCorePlus): """Make sure search paths get added to path""" core.setDeviceAdapterSearchPaths(["test_path"]) assert "test_path" in os.getenv("PATH") with pytest.raises(TypeError): core.setDeviceAdapterSearchPaths("test_path")
def test_metadata(core: CMMCorePlus): core.startContinuousSequenceAcquisition(10) core.stopSequenceAcquisition() image, md = core.getLastImageMD() assert isinstance(md, Metadata) assert md["Height"] == "512" assert "ImageNumber" in md.keys() assert ("Binning", "1") in md.items() assert "GRAY16" in md.values() assert "Camera" in md md["Camera"] = "new" assert md["Camera"] == "new" == md.get("Camera") cpy = md.copy() assert cpy == md del md["Camera"] assert "Camera" not in md assert "Camera" in cpy assert md.get("", 1) == 1 # default md.clear() assert not md assert isinstance(md.json(), str)
def test_prop_browser(qtbot): from pymmcore_plus import CMMCorePlus mmcore = CMMCorePlus() cfg = Path(__file__).parent / "test_config.cfg" mmcore.loadSystemConfiguration(str(cfg)) pb = PropBrowser(mmcore) qtbot.addWidget(pb) pb.show()
def _creat_prop_widget(core: CMMCorePlus, dev: str, prop: str) -> PPropValueWidget: """The type -> widget selection part used in the above function.""" if core.isPropertyReadOnly(dev, prop): return ReadOnlyWidget() ptype = core.getPropertyType(dev, prop) if allowed := core.getAllowedPropertyValues(dev, prop): if ptype is PropertyType.Integer and set(allowed) == {"0", "1"}: return IntBoolWidget() return ChoiceWidget(core, dev, prop)
def test_mda(core: CMMCorePlus, qtbot: "QtBot"): """Test signal emission during MDA""" mda = MDASequence( time_plan={ "interval": 0.1, "loops": 2 }, stage_positions=[(1, 1, 1)], z_plan={ "range": 3, "step": 1 }, channels=[{ "config": "DAPI", "exposure": 1 }], ) fr_mock = MagicMock() ss_mock = MagicMock() sf_mock = MagicMock() xystage_mock = MagicMock() stage_mock = MagicMock() exp_mock = MagicMock() core.mda._events.frameReady.connect(fr_mock) core.mda._events.sequenceStarted.connect(ss_mock) core.mda._events.sequenceFinished.connect(sf_mock) core.events.XYStagePositionChanged.connect(xystage_mock) core.events.stagePositionChanged.connect(stage_mock) core.events.exposureChanged.connect(exp_mock) with qtbot.waitSignal(core.mda._events.sequenceFinished): core.run_mda(mda) assert fr_mock.call_count == len(list(mda)) for event, _call in zip(mda, fr_mock.call_args_list): assert isinstance(_call.args[0], np.ndarray) assert _call.args[1] == event ss_mock.assert_called_once_with(mda) sf_mock.assert_called_once_with(mda) xystage_mock.assert_called_with("XY", 1.0, 1.0) exp_mock.assert_called_with("Camera", 1.0) stage_mock.assert_has_calls([ call("Z", -0.5), call("Z", 0.5), call("Z", 1.5), call("Z", 2.5), call("Z", -0.5), call("Z", 0.5), call("Z", 1.5), call("Z", 2.5), ])
def test_lock_and_callbacks(core: CMMCorePlus, qtbot): if not isinstance(core.events, QObject): pytest.skip( reason="Skip lock tests on psygnal until we can remove qtbot.") # when a function with a lock triggers a callback # that callback should be able to call locked functions # without hanging. # do some threading silliness here so we don't accidentally hang our # test if things go wrong have to use *got_lock* to check because we # can't assert in the function as theads don't throw their exceptions # back into the calling thread. got_lock = False def cb(*args, **kwargs): nonlocal got_lock got_lock = core.lock.acquire(timeout=0.1) if got_lock: core.lock.release() core.events.XYStagePositionChanged.connect(cb) def trigger_cb(): core.setXYPosition(4, 5) th = Thread(target=trigger_cb) with qtbot.waitSignal(core.events.XYStagePositionChanged): th.start() assert got_lock got_lock = False core.mda._events.frameReady.connect(cb) mda = MDASequence( time_plan={ "interval": 0.1, "loops": 2 }, stage_positions=[(1, 1, 1)], z_plan={ "range": 3, "step": 1 }, channels=[{ "config": "DAPI", "exposure": 1 }], ) with qtbot.waitSignal(core.mda._events.sequenceFinished): core.run_mda(mda) assert got_lock
def test_guess_channel_group(core: CMMCorePlus): chan_group = core.getChannelGroup() assert chan_group == "Channel" assert core.getOrGuessChannelGroup() == ["Channel"] with patch.object(core, "getChannelGroup", return_value=""): assert core.getOrGuessChannelGroup() == ["Channel"] with pytest.raises(TypeError): core.channelGroup_pattern = 4 # assign a new regex that won't match Channel using a str # this will return all the mm groups, but that's because this a bad regex # to use core.channelGroup_pattern = "^((?!(Channel)).)*$" assert core.getOrGuessChannelGroup() == [ "Camera", "LightPath", "Objective", "System", ] # assign new using a pre-compile pattern core.channelGroup_pattern = re.compile("Channel") chan_group = core.getOrGuessChannelGroup() assert chan_group == ["Channel"]
def test_mda_pause_cancel(core: CMMCorePlus, qtbot: "QtBot"): """Test signal emission during MDA with cancelation""" mda = MDASequence( time_plan={ "interval": 0.25, "loops": 10 }, stage_positions=[(1, 1, 1)], z_plan={ "range": 3, "step": 1 }, channels=[{ "config": "DAPI", "exposure": 1 }], ) pause_mock = MagicMock() cancel_mock = MagicMock() sf_mock = MagicMock() ss_mock = MagicMock() core.mda._events.sequenceStarted.connect(ss_mock) core.mda._events.sequencePauseToggled.connect(pause_mock) core.mda._events.sequenceCanceled.connect(cancel_mock) core.mda._events.sequenceFinished.connect(sf_mock) _fcount = 0 @core.mda._events.frameReady.connect def _onframe(frame, event): nonlocal _fcount _fcount += 1 if _fcount == 1: core.mda.toggle_pause() pause_mock.assert_called_with(True) core.mda.toggle_pause() pause_mock.assert_called_with(False) elif _fcount == 2: core.mda.cancel() with qtbot.waitSignal(core.mda._events.sequenceFinished): core.run_mda(mda) ss_mock.assert_called_once_with(mda) cancel_mock.assert_called_once_with(mda) assert _fcount < len(list(mda)) sf_mock.assert_called_once_with(mda)
def test_core(core: CMMCorePlus): assert isinstance(core, CMMCorePlus) assert isinstance(core, CMMCore) # because the fixture tries to find micromanager, this should be populated assert core.getDeviceAdapterSearchPaths() assert isinstance(core.events.propertyChanged, (psygnal.SignalInstance, QSignalInstance)) assert isinstance(core.mda.events.frameReady, (psygnal.SignalInstance, QSignalInstance)) assert not core.mda._canceled assert not core.mda._paused # because the fixture loadsSystemConfig 'demo' assert len(core.getLoadedDevices()) == 12 assert "CMMCorePlus" in repr(core)
def test_device_type_overrides(core: CMMCorePlus): dt = core.getDeviceType("Camera") assert isinstance(dt, DeviceType) assert str(dt) == "Camera" assert int(dt) == 2 assert dt == DeviceType["Camera"] assert dt == DeviceType["CameraDevice"] assert dt == DeviceType(2)
def test_mmproperty(core: CMMCorePlus): for prop in core.iterProperties(as_object=True): assert prop.isValid() assert prop.dict() if prop.isReadOnly(): with pytest.warns(UserWarning): prop.value = "asdf"
def test_load_system_config(core: CMMCorePlus): with pytest.raises(FileNotFoundError): core.loadSystemConfiguration("nonexistent") config_path = Path(__file__).parent / "local_config.cfg" core.loadSystemConfiguration(str(config_path)) assert core.getLoadedDevices() == ( "DHub", "Camera", "Dichroic", "Emission", "Excitation", "Objective", "Z", "Path", "XY", "Shutter", "Autofocus", "Core", )
def test_setContext(core: CMMCorePlus): # should work with either leading capitalization with core.setContext(shutterOpen=False): assert not core.getShutterOpen() with core.setContext(ShutterOpen=False): assert not core.getShutterOpen() # if we set an invalid value make sure initial state is still restored with pytest.raises(TypeError): with core.setContext(autoShutter=False, shutterOpen="sadfsd"): assert not core.getAutoShutter() assert core.getAutoShutter() with pytest.raises(ValueError): with core.setContext(autoShutter=False): raise ValueError assert core.getAutoShutter()
def test_configuration(core: CMMCorePlus): state = core.getSystemState() assert isinstance(state, Configuration) assert not isinstance(core.getSystemState(native=True), Configuration) assert str(state) tup = tuple(state) assert isinstance(tup, tuple) assert all(isinstance(x, tuple) and len(x) == 3 for x in tup) with pytest.raises(TypeError): assert state["Camera"] == 1 with pytest.raises(TypeError): assert "Camera" in state assert state["Camera", "Binning"] == "1" assert PropertySetting("Camera", "Binning", "1") in state assert state in state assert ("Camera", "Binning") in state
def global_mmcore(request): _core._SESSION_CORE = CMMCorePlus() # refresh singleton if request.param == "remote": from pymmcore_plus import server server.try_kill_server() mmc = _core.get_core_singleton(remote=request.param == "remote") if len(mmc.getLoadedDevices()) < 2: mmc.loadSystemConfiguration( str(Path(__file__).parent / "test_config.cfg")) return mmc
def test_prop_browser_core_reset(global_mmcore: CMMCorePlus, qtbot): """test that loading and resetting doesn't cause errors.""" global_mmcore.unloadAllDevices() pb = PropertyBrowser(mmcore=global_mmcore) qtbot.addWidget(pb) global_mmcore.loadSystemConfiguration() global_mmcore.reset()
def test_objective_widget_changes_objective(global_mmcore: CMMCorePlus, qtbot): obj_wdg = MMObjectivesWidget() qtbot.addWidget(obj_wdg) start_z = 100.0 global_mmcore.setPosition("Z", start_z) stage_mock = Mock() obj_wdg._mmc.events.stagePositionChanged.connect(stage_mock) assert obj_wdg._combo.currentText() == "Nikon 10X S Fluor" with pytest.raises(ValueError): obj_wdg._combo.setCurrentText("10asdfdsX") assert global_mmcore.getCurrentPixelSizeConfig() == "Res10x" new_val = "Nikon 40X Plan Fluor ELWD" with qtbot.waitSignal(global_mmcore.events.propertyChanged): obj_wdg._combo.setCurrentText(new_val) stage_mock.assert_has_calls([call("Z", 0), call("Z", start_z)]) assert obj_wdg._combo.currentText() == new_val assert global_mmcore.getStateLabel(obj_wdg._objective_device) == new_val assert global_mmcore.getCurrentPixelSizeConfig() == "Res40x" assert global_mmcore.getPosition("Z") == start_z
def test_register_mda_engine(core: CMMCorePlus, qtbot: "QtBot"): orig_engine = core.mda registered_mock = MagicMock() core.events.mdaEngineRegistered.connect(registered_mock) # fake that mda is running # with an actual mda the threading and timing is # such that this ends up being a flaky test if we # use `core.run_mda` core.mda._running = True new_engine = MDAEngine(core) with pytest.raises(ValueError): core.register_mda_engine(new_engine) core.mda._running = False with qtbot.waitSignal(core.events.mdaEngineRegistered): core.register_mda_engine(new_engine) assert core._mda_engine is new_engine # invalid engine class nonconforming_engine: pass with pytest.raises(TypeError): core.register_mda_engine(nonconforming_engine()) registered_mock.assert_called_once_with(new_engine, orig_engine)
def test_device_callbacks(core: CMMCorePlus): dev = Device("Camera", core) mock = Mock() mock2 = Mock() # regular connection dev.propertyChanged.connect(mock) dev.propertyChanged("Gain").connect(mock2) core.setProperty("Camera", "Gain", "6") mock.assert_called_once_with("Gain", "6") mock2.assert_called_once_with("6") mock.reset_mock() mock2.reset_mock() core.setProperty("Camera", "Binning", "2") mock.assert_called_once_with("Binning", "2") mock2.assert_not_called() # regular disconnection mock.reset_mock() mock2.reset_mock() dev.propertyChanged.disconnect(mock) dev.propertyChanged("Gain").disconnect(mock2) core.setProperty("Camera", "Gain", "4") mock.assert_not_called() mock2.assert_not_called()
def test_md_overrides(core: CMMCorePlus): core.startContinuousSequenceAcquisition(10) core.stopSequenceAcquisition() image, md = core.getNBeforeLastImageMD(0) assert isinstance(md, Metadata) image, md = core.popNextImageMD() assert isinstance(md, Metadata)
def get_core_singleton(remote=False) -> CMMCorePlus: """Retrieve the MMCore singleton for this session. The first call to this function determines whether we're running remote or not. perhaps a temporary function for now... """ global _SESSION_CORE if _SESSION_CORE is None: if remote: from pymmcore_plus import RemoteMMCore _SESSION_CORE = RemoteMMCore() # type: ignore # it has the same interface. else: _SESSION_CORE = CMMCorePlus.instance() return _SESSION_CORE
def test_new_position_methods(core: CMMCorePlus): x1, y1 = core.getXYPosition() z1 = core.getZPosition() core.setRelativeXYZPosition(1, 1, 1) x2, y2 = core.getXYPosition() z2 = core.getZPosition() assert round(x2, 2) == x1 + 1 assert round(y2, 2) == y1 + 1 assert round(z2, 2) == z1 + 1
def test_device_object(core: CMMCorePlus): for device in core.iterDevices(as_object=True): device.wait() assert not device.isBusy() assert device.schema() assert device.description() lib = device.library() assert lib if device.type() is not DeviceType.Core else not lib assert device.name() assert repr(device) assert device.core is core assert device.isLoaded() if device.supportsDetection(): assert device.detect() is not DeviceDetectionStatus.Unimplemented else: assert device.detect() is DeviceDetectionStatus.Unimplemented if device.type() is not DeviceType.Core: device.unload() assert not device.isLoaded() assert "NOT LOADED" in repr(device)
def test_get_objectives(core: CMMCorePlus): devices = core.guessObjectiveDevices() assert len(devices) == 1 assert devices[0] == "Objective" with pytest.raises(TypeError): core.objective_device_pattern = 4 # assign a new regex that won't match Objective using a str core.objective_device_pattern = "^((?!Objective).)*$" assert "Objective" not in core.guessObjectiveDevices() # assign new using a pre-compile pattern core.objective_device_pattern = re.compile("Objective") devices = core.guessObjectiveDevices() assert len(devices) == 1 assert devices[0] == "Objective"
from pymmcore_plus import CMMCorePlus # see https://github.com/tlambert03/useq-schema sequence = MDASequence( channels=["DAPI", { "config": "FITC", "exposure": 50 }], time_plan={ "interval": 2, "loops": 5 }, z_plan={ "range": 4, "step": 0.5 }, axis_order="tpcz", ) mmc = CMMCorePlus.instance() mmc.loadSystemConfiguration() @mmc.mda.events.frameReady.connect def new_frame(img, event): print(img.shape) mda_thread = mmc.run_mda(sequence)
import pytest from pymmcore_plus import CMMCorePlus, PropertyType from micromanager_gui._core_widgets import PropertyWidget # not sure how else to parametrize the test without instantiating here at import ... CORE = CMMCorePlus() CORE.loadSystemConfiguration() dev_props = [(dev, prop) for dev in CORE.getLoadedDevices() for prop in CORE.getDevicePropertyNames(dev)] def _assert_equal(a, b): try: assert float(a) == float(b) except ValueError: assert str(a) == str(b) @pytest.mark.parametrize("dev, prop", dev_props) def test_property_widget(dev, prop, qtbot): wdg = PropertyWidget(dev, prop, core=CORE) qtbot.addWidget(wdg) if CORE.isPropertyReadOnly(dev, prop) or prop in ( "SimulateCrash", "Trigger", "AsyncPropertyLeader", ): return start_val = CORE.getProperty(dev, prop)
Whether to force a change away from tracking the default camera. """ if not force: raise RuntimeError( "Setting the camera on a DefaultCameraExposureWidget " "may cause it to malfunction. Either use *force=True* " " or create an ExposureWidget" ) return super().setCamera(camera) def _camera_updated(self, value: str): # This will not always fire # see https://github.com/micro-manager/mmCoreAndDevices/issues/181 self._camera = value # this will result in a double call of _on_load if this callback # was triggered by a configuration load. But I don't see an easy way around that # fortunately _on_load should be low cost self._on_load() if __name__ == "__main__": # pragma: no cover import sys from pymmcore_plus import CMMCorePlus # noqa CMMCorePlus.instance().loadSystemConfiguration() app = QtW.QApplication(sys.argv) win = DefaultCameraExposureWidget() win.show() sys.exit(app.exec_())
def iter_dev_props(mmc: CMMCorePlus) -> Iterator[Tuple[str, str]]: for dev in mmc.getLoadedDevices(): for prop in mmc.getDevicePropertyNames(dev): yield dev, prop
from __future__ import annotations from pathlib import Path from typing import TYPE_CHECKING import pytest from pymmcore_plus import CMMCorePlus from micromanager_gui._core_widgets import DefaultCameraExposureWidget if TYPE_CHECKING: from pytestqt.qtbot import QtBot # not sure how else to parametrize the test without instantiating here at import ... CORE = CMMCorePlus() CORE.loadSystemConfiguration(Path(__file__).parent.parent / "test_config.cfg") def test_exposure_widget(qtbot: QtBot): CORE.setExposure(15) wdg = DefaultCameraExposureWidget(core=CORE) qtbot.addWidget(wdg) # check that it get's whatever core is set to. assert wdg.spinBox.value() == 15 with qtbot.waitSignal(CORE.events.exposureChanged): CORE.setExposure(30) assert wdg.spinBox.value() == 30 with qtbot.wait_signal(CORE.events.exposureChanged): wdg.spinBox.setValue(45)