magicgui icon indicating copy to clipboard operation
magicgui copied to clipboard

Asynchronous choices callable in CategoricalWidget

Open aeisenbarth opened this issue 3 years ago • 1 comments

When you want to create a categorical widget with choices that are produced by a long-running function (e.g. network request), the process freezes the user interface. Usually one would run such functions outside of the UI thread and update the UI once finished. But the choices function expects an immediate return value.

I was wondering, do others also encounter this use case for feeding ComboBoxes from Web APIs? Are there best practices?

I can think of several ways to handle this:

  • Add MagicGUI support for choices callables that return futures. Then the developers still have to wrap long-running functions into a thread and return a future.
  • Add a switch to MagicGUI CategoricalWidget indicating that the choices callable should be run in a thread. Then MagicGUI would take care of connecting to the thread's returned signal and update the widget afterwards.
    This is close to my current implementation (a subclass of Combobox). A particularity is how the widget should behave while choices are being loaded. Very likely the old choices are not valid, and keeping displaying them could be inconsistent with other widget values. I chose to immediately return an empty list of choices and later set the actual choices in the thread's callback.
  • Work-around: Set a fixed list of choices on the combobox instance. Make the source event which would trigger the choices reset instead trigger the asynchronous function, whose returned callback will apply a new list of fixed choices to the combobox. In this solution the combobox would not be fully self-contained (manage its data), and it would break as soon as reset_choices is called and the choices are cleared.

aeisenbarth avatar Feb 17 '22 17:02 aeisenbarth

good one. I haven't encountered yet, but makes perfect sense. My first thought/preference is probably number your first option. Even though Qt is currently the only backend (and who knows, may be for a long time), at this point, we still have full isolation between the backend and magicgui. So, if we use the threadworker object with signals from superqt, we necessarily have to depend on qt.

The first seems to me to be pretty easy to implement with a simple isinstance(return_value, Future) check, that throws an add_done_callback onto the Future, and maybe deactivates the combobox in the meantime, and we're done with it. Yes, it means that the end-user needs to opt-in to that behavior by making their own thread and Future (but that's already pretty easy if they're using superqt).

Would you want to take a crack at implementing this in a PR?

tlambert03 avatar Feb 17 '22 21:02 tlambert03