def test_chained_emitters(): """Chained emitters""" source = type("Source", (), {})() em1 = EventEmitter(source=None, type="test_event1") em2 = EventEmitter(source=source, type="test_event2") em1.connect(em2) em1.connect(record_event) record_event.result = None ev = em1() record_event.assert_result(event=ev, event_class=Event, source=None, type="test_event1", sources=[None]) # sources look different from second emitter, but type is the same. em1.disconnect(record_event) em2.connect(record_event) record_event.result = None ev = em1() record_event.assert_result( event=ev, event_class=Event, source=source, type="test_event1", sources=[None, source], )
def test_event_connect_order(): """Test event connection order""" def a(e): return def b(e): return def c(e): return def d(e): return def e(e): return def f(e): return em = EventEmitter(type="test_event") assert_raises(ValueError, em.connect, c, before=["c", "foo"]) assert_raises(ValueError, em.connect, c, position="foo") assert_raises(TypeError, em.connect, c, ref=dict()) em.connect(c, ref=True) assert_equal((c, ), tuple(em.callbacks)) em.connect(c) assert_equal((c, ), tuple(em.callbacks)) em.connect(d, ref=True, position="last") assert_equal((c, d), tuple(em.callbacks)) em.connect(b, ref=True) # position='first' assert_equal((b, c, d), tuple(em.callbacks)) assert_raises(RuntimeError, em.connect, a, before="c", after="d") # can't em.connect(a, ref=True, before=["c", "d"]) # first possible pos == 0 assert_equal((a, b, c, d), tuple(em.callbacks)) em.connect(f, ref=True, after=["c", "d"]) assert_equal((a, b, c, d, f), tuple(em.callbacks)) em.connect(e, ref=True, after="d", before="f") assert_equal(("a", "b", "c", "d", "e", "f"), tuple(em.callback_refs)) em.disconnect(e) em.connect(e, ref=True, after="a", before="f", position="last") assert_equal(("a", "b", "c", "d", "e", "f"), tuple(em.callback_refs)) em.disconnect(e) em.connect(e, ref="e", after="d", before="f", position="last") assert_equal(("a", "b", "c", "d", "e", "f"), tuple(em.callback_refs)) em.disconnect(e) em.connect(e, after="d", before="f", position="first") # no name assert_equal(("a", "b", "c", "d", None, "f"), tuple(em.callback_refs)) em.disconnect(e) assert_raises(ValueError, em.connect, e, ref="d") # duplicate name em.connect(e, ref=True, after=[], before="f", position="last") assert_equal(("a", "b", "c", "d", "e", "f"), tuple(em.callback_refs)) assert_equal((a, b, c, d, e, f), tuple(em.callbacks)) old_e = e def e(): # type: ignore return assert_raises(ValueError, em.connect, e, ref=True) # duplicate name em.connect(e) assert_equal((None, "a", "b", "c", "d", "e", "f"), tuple(em.callback_refs)) assert_equal((e, a, b, c, d, old_e, f), tuple(em.callbacks))
def test_disconnect(): """Emitter disconnection""" em = EventEmitter(type="test_event") def cb1(ev): record_event.result = 1 def cb2(ev): record_event.result = 2 em.connect((record_event, "__call__")) # type: ignore em.connect(cb1) em.connect(cb2) record_event.result = None em.disconnect(cb2) em.disconnect(cb2) # should pass silently ev = em() record_event.assert_result(event=ev) record_event.result = None em.disconnect((record_event, "__call__")) # type: ignore ev = em() assert record_event.result == 1 record_event.result = None em.connect(cb1) em.connect(cb2) em.connect((record_event, "__call__")) # type: ignore em.disconnect() em() assert record_event.result is None
def __init__( self, function: Callable[..., _R], call_button: bool | str | None = None, layout: str = "vertical", labels: bool = True, tooltips: bool = True, app: AppRef = None, visible: bool = None, auto_call: bool = False, result_widget: bool = False, param_options: dict[str, dict] | None = None, name: str = None, persist: bool = False, **kwargs, ): if not callable(function): raise TypeError( "'function' argument to FunctionGui must be callable.") # consume extra Widget keywords extra = set(kwargs) - {"annotation", "gui_only"} if extra: s = "s" if len(extra) > 1 else "" raise TypeError( f"FunctionGui got unexpected keyword argument{s}: {extra}") if param_options is None: param_options = {} elif not isinstance(param_options, dict): raise TypeError("'param_options' must be a dict of dicts") sig = magic_signature(function, gui_options=param_options) self.return_annotation = sig.return_annotation if tooltips: _inject_tooltips_from_docstrings(function.__doc__, sig) self.persist = persist self._function = function self.__wrapped__ = function # it's conceivable that function is not actually an instance of FunctionType # we can still support any generic callable, but we need to be careful not to # access attributes (like `__name__` that only function objects have). # Mypy doesn't seem catch this at this point: # https://github.com/python/mypy/issues/9934 self._callable_name = (getattr(function, "__name__", None) or f"{function.__module__}.{function.__class__}") super().__init__( layout=layout, labels=labels, visible=visible, widgets=list(sig.widgets(app).values()), name=name or self._callable_name, ) self._param_options = param_options self.called = EventEmitter(self, type="called") self._result_name = "" self._call_count: int = 0 # a deque of Progressbars to be created by (possibly nested) tqdm_mgui iterators self._tqdm_pbars: Deque[ProgressBar] = deque() # the nesting level of tqdm_mgui iterators in a given __call__ self._tqdm_depth: int = 0 if call_button is None: call_button = not auto_call self._call_button: PushButton | None = None if call_button: text = call_button if isinstance(call_button, str) else "Run" self._call_button = PushButton(gui_only=True, text=text, name="call_button") if not auto_call: # (otherwise it already gets called) def _disable_button_and_call(val): # disable the call button until the function has finished self._call_button = cast(PushButton, self._call_button) self._call_button.enabled = False try: self.__call__() finally: self._call_button.enabled = True self._call_button.changed.connect(_disable_button_and_call) self.append(self._call_button) self._result_widget: LineEdit | None = None if result_widget: self._result_widget = LineEdit(gui_only=True, name="result") self._result_widget.enabled = False self.append(self._result_widget) if persist: self._load(quiet=True) self._auto_call = auto_call self.changed.connect(self._on_change)
def test_emitter_block2(): state = [False, False] def a(ev): state[0] = True def b(ev): state[1] = True e = EventEmitter(source=None, type="event") e.connect(a) e.connect(b) def assert_state(a, b): assert state == [a, b] state[0] = False state[1] = False e() assert_state(True, True) # test global blocking e.block() e() assert_state(False, False) e.block() e() assert_state(False, False) # test global unlock, multiple depth e.unblock() e() assert_state(False, False) e.unblock() e() assert_state(True, True) # test unblock failure try: e.unblock() raise Exception("Expected RuntimeError") except RuntimeError: pass # test single block e.block(a) e() assert_state(False, True) e.block(b) e() assert_state(False, False) e.block(b) e() assert_state(False, False) # test single unblock e.unblock(a) e() assert_state(True, False) e.unblock(b) e() assert_state(True, False) e.unblock(b) e() assert_state(True, True) # Test single unblock failure try: e.unblock(a) raise Exception("Expected RuntimeError") except RuntimeError: pass # test global blocker with e.blocker(): e() assert_state(False, False) # test nested blocker with e.blocker(): e() assert_state(False, False) e() assert_state(False, False) e() assert_state(True, True) # test single blocker with e.blocker(a): e() assert_state(False, True) # test nested gloabel blocker with e.blocker(): e() assert_state(False, False) e() assert_state(False, True) # test nested single blocker with e.blocker(a): e() assert_state(False, True) with e.blocker(b): e() assert_state(False, False) e() assert_state(False, True) e() assert_state(True, True)
def _post_init(self): super()._post_init() self.changed = EventEmitter(source=self, type="changed") self._widget._mgui_bind_change_callback(self._on_value_change)
def _post_init(self): from magicgui.events import EventEmitter self.changed = EventEmitter(source=self, type="changed") self._widget._mgui_bind_change_callback( lambda *x: self.changed(value=self.value))