magicgui icon indicating copy to clipboard operation
magicgui copied to clipboard

Callable widget example is broken or maybe the widget

Open OlaFosheimGrostad opened this issue 9 months ago • 2 comments

Example code: https://pyapp-kit.github.io/magicgui/generated_examples/applications/callable/

Version on macports: py313-magicgui @0.10.0 (python)

Problem: Shows double run buttons.

When I write my own example with default set to None, it shows the "run" button initially, but it disappears if I select "f" then None. A better behaviour would be for the run button to be disabled on None or remove it initially.

My testcode:

from magicgui import magicgui

def f(x:int, y:str) -> str:
    return f"f({x},{y})"

def g(x:int, y:int) -> str:
    return f"g({x},{y})"

@magicgui(call_button=True, func={"choices": ["f", "g"]})
def example(func=None):
    pass


def update(name: str):
    if len(example) > 1:
        del example[1]
    if name is not None:
        example.insert(1, magicgui(globals()[name]))

def main():
    example.func.changed.connect(update)
    example.show(run=True)

if __name__ == '__main__':
    main()

OlaFosheimGrostad avatar Jun 03 '25 10:06 OlaFosheimGrostad

I agree, that example is a little wonky... Honestly, (in retrospect) I think it's a convoluted usage of the @magicgui call there just to make a container that isn't really, itself a function. I think i would probably write it more like this now (this includes your enable/disable run button behavior)

from magicgui import magicgui, widgets


def f(x: int, y: str = "a string") -> None:
    """Example function F."""
    print(f"f({x}, {y})")


def g(x: int = 6, y: str = "another string") -> None:
    """Example function G."""
    print(f"g({x}, {y})")


combo = widgets.ComboBox(name="func", choices=["f", "g"], nullable=True)
run = widgets.Button(text="Run")
run.enabled = False
example = widgets.Container(widgets=[combo, run])


@run.clicked.connect
def run_func() -> None:
    """Run the selected function."""
    func_widget = example[1]
    if callable(func_widget):
        func_widget()


@combo.changed.connect
def update(func_name: str | None) -> None:
    """Update function."""
    if func_name is None:
        run.enabled = False
        if len(example) > 2:
            del example[1]
    else:
        run.enabled = True
        func = globals()[func_name]
        example.insert(1, magicgui(func, call_button=False))


example.show(run=True)

tlambert03 avatar Jun 03 '25 18:06 tlambert03

if you were more interested in the concept of a callable that is itself dynamic, let me know... but if you're more interested in a dynamic UI/widget that has a (stable) underlying callable behavior, that's how I'd do it

tlambert03 avatar Jun 03 '25 18:06 tlambert03