Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added disable context manager demo #1238

Merged
merged 10 commits into from
Aug 7, 2023
Merged

Conversation

BrianLandry
Copy link
Contributor

This adds a demo for a context manager I have been using frequently. It will disable the button for the duration of an async process. It is a useful tool to make sure the user does trigger the click a second time while something is running in the background. I thought it may be useful to add on as an example.

Copy link
Member

@rodja rodja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea. Could we do something more interesting than calling await asyncio.sleep(3)? Something which really takes some time? That would make the usage more clear in my opinion.

And we should use the on_click parameter not the generic .on('click').

Which leads me to my main thought: @falkoschindler should we disable the button by default as long as an async call is processed?

@falkoschindler
Copy link
Contributor

falkoschindler commented Jul 24, 2023

@rodja I'm not sure if we should do it by default. There are certainly use cases where you want to start background tasks that are independent of each other, like "download the current image" or "save the current state to a file". So you might want to start another task while the previous one is still running. Disabling the button would prevent that.

@rodja
Copy link
Member

rodja commented Jul 25, 2023

There are certainly use cases where you want to start background tasks that are independent of each other, like "download the current image" or "save the current state to a file". So you might want to start another task while the previous one is still running. Disabling the button would prevent that.

True. But I assume this kind of scenarios are rare. We could add a parameter to disable the default behavior:

ui.button('save state to file', on_click=..., auto_disable=False)

@falkoschindler
Copy link
Contributor

Let's discuss auto_disable on a separate feature request. As far as I understand it would introduce a breaking change, thus would have to wait for 1.4. But this PR is independent.

@BrianLandry
Copy link
Contributor Author

Thanks for the thoughts here. One potential issue with having disable as a default is that it causes the stop cursor to flash for most button clicks, since I assume most buttons would only be disabled for a few milliseconds. For example:

from asyncio import sleep
from contextlib import asynccontextmanager

from nicegui import ui
@asynccontextmanager
async def disable_button(button):
    button.disable()
    try:
        yield
    finally:
        button.enable()

async def on_click(button):
    async with disable_button(button):
        await sleep(0.01)

b= ui.button("Disable", on_click=lambda: on_click(b))
ui.run()

Also to add some examples of where I am using this:

  1. I have some data intensive processes that take ~20 seconds to run. I am sending the process off to a coroutine and disabling the button while waiting for it to finish.
  2. I am interacting with some APIs that can take 30-60 seconds to return all of their data, I am disabling the button while waiting for the response.
  3. I'm accessing 10,000 rows from a database that takes 10 seconds to return all of their data, I am disabling the button while waiting for the response.

Unfortunately none of those examples lend themselves easily to demo code. I'll see if I can think of something better other than sleep to stick into this demo.

Copy link
Contributor

@falkoschindler falkoschindler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added some remarks about the code.

Regarding a more realistic example: Maybe we can use something like https://httpbin.org/delay/1 which returns an HTTP response after a certain delay (1 second in this case).

You mentioned a flash when disabling the button for a very short time. This only happens if the delay is awaited asynchronously. For short synchronous event handlers, the disable() call won't be sent to the client before enable() is called. But I'm still not sure how to integrate this feature into the existing API. I tend towards an opt-in like ui.button(..., disable_while_click_handling: bool = False).

website/more_documentation/button_documentation.py Outdated Show resolved Hide resolved
website/more_documentation/button_documentation.py Outdated Show resolved Hide resolved
website/more_documentation/button_documentation.py Outdated Show resolved Hide resolved
website/more_documentation/button_documentation.py Outdated Show resolved Hide resolved
website/more_documentation/button_documentation.py Outdated Show resolved Hide resolved
website/more_documentation/button_documentation.py Outdated Show resolved Hide resolved
@falkoschindler falkoschindler added this to the 1.3.7 milestone Jul 27, 2023
@falkoschindler falkoschindler added the documentation Improvements or additions to documentation label Jul 27, 2023
@falkoschindler falkoschindler modified the milestones: 1.3.7, 1.3.8 Aug 2, 2023
@falkoschindler
Copy link
Contributor

@BrianLandry Are you still working on this PR? Or should we take over? It's up to you, just let us know. 🙂

Copy link
Contributor Author

@BrianLandry BrianLandry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry for the slow response, this got lost on my todo list.

Thank you for all the feedback! I'm new to async and learned a lot from your comments. I think I addressed everything, but I'm heading off on a vacation, so please feel free to take over the PR from here.

Copy link
Contributor

@falkoschindler falkoschindler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @BrianLandry! Looks all good to me. Let's merge! 🥳

@falkoschindler falkoschindler merged commit eb716f7 into zauberzeug:main Aug 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants