napari / napari-plugin-template

A template repo used to make new napari plugins
BSD 3-Clause "New" or "Revised" License
7 stars 5 forks source link

Best practice examples for long running computation widgets #10

Open GenevieveBuckley opened 1 year ago

GenevieveBuckley commented 1 year ago

I'd like this repository to include one or two examples of best practice widgets for long running computations, using thread workers and progress bars.

There are a few different combinations of situations we should demonstrate:

  1. blocking function, plus progress bar of known total length
  2. blocking function, plus indeterminate progress bar
  3. thread worker (non-blocking), plus progress bar of known total length
  4. thread worker (non-blocking), plus indeterminate progress bar

Re: progress bars It's very important for users to have reassurance that the program is doing something. So important, that I think we should include this info in the demo examples, so plugin authors will be much more likely to include this wherever it's appropriate.

Re: thread workers There are plenty of situations where plugin authors might want to start some long computation that will write the results to an output file. This is a great candidate for using a thread worker, so that users can still pan/zoom/interact with the images while the results are being computed and written to disk.

Maybe it would make the most sense to add another file, called _widget_with_progressbar.py, and a corresponding test file. Both _widget.py and _widget_with_progressbar.py should get added to the new template if the user selects "yes" when the cookiecutter asks if they want to include widgets in their plugin.

GenevieveBuckley commented 1 year ago

Here's an example for (4) a thread worker (non-blocking), plus indeterminate progress bar

from time import sleep
from magicgui import magic_factory
from magicgui.tqdm import tqdm

@magic_factory(
    threshold={"widget_type": "FloatSlider", "max": 20},
    call_button="Calculate!",
)
def example_magic_widget(
    img_layer: "napari.layers.Image",
    threshold: "float"
) -> "napari.types.LabelsData":
    with tqdm() as pbar:
        @thread_worker(connect={"finished": lambda: pbar.progressbar.hide()})
        def long_running():
            sleep(5)  # let's pretend we don't know how long this function is going to take

        long_running()
Czaki commented 1 year ago

We may put this in documentation, but I prefer not to store this in the template itself as it may confuse manny people with multiple selections.

lucyleeow commented 8 months ago

+1 for adding a section in the docs!