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

Button can be clicked while disabled #5076

Closed
def1127 opened this issue Oct 2, 2024 · 5 comments
Closed

Button can be clicked while disabled #5076

def1127 opened this issue Oct 2, 2024 · 5 comments

Comments

@def1127
Copy link

def1127 commented Oct 2, 2024

In specific situations, buttons can be clicked while disabled. Textual responds by queuing the button click and sends it when the button is next enabled.

from textual.containers import Vertical
from textual.widgets import Button, Label
from textual import on
import asyncio

class MyApp(App):
    def compose(self) -> ComposeResult:
        # Run button
        self.run_button = Button(label="Run", variant="success", id="button")
        self.text = Label("Not Running")

        # Assemble the UI
        yield Vertical(self.run_button, self.text)

    @on(Button.Pressed, "#button")
    async def on_button_pressed(self, event: Button.Pressed) -> None:
        
        # Call the long-running function
        await MyFunction(self)
    
async def MyFunction(self):
    self.text.update("Running!")
    await asyncio.sleep(5)
    self.text.update("Not Running")

# Start the application
if __name__ == "__main__":
    MyApp().run()

When I run this example, click the button rapidly, and time the output, the label doesn't return to the "Not Running" state in the 5 seconds it should, it takes a multiple of 5 seconds.

Copy link

github-actions bot commented Oct 2, 2024

Thank you for your issue. Give us a little time to review it.

PS. You might want to check the FAQ if you haven't done so already.

This is an automated reply, generated by FAQtory

@def1127
Copy link
Author

def1127 commented Oct 2, 2024

I have a suspicion that the issue originates in _button.py line 254.

async def _on_click(self, event: events.Click) -> None:
        event.stop()
        if not self.has_class("-active"):
            self.press()

    def press(self) -> Self:
        """Animate the button and send the [Pressed][textual.widgets.Button.Pressed] message.

        Can be used to simulate the button being pressed by a user.

        Returns:
            The button instance.
        """
        if self.disabled or not self.display:
            return self
        # Manage the "active" effect:
        self._start_active_affect()
        # ...and let other components know that we've just been clicked:
        self.post_message(Button.Pressed(self))
        return self

This code suggests that you can't do button.press when the button is disabled, but the async event _on_click can still create a message, and seems to wait to process the message until the button is enabled. Suggest either a check in _on_click() for if the button is disabled, or where that is inherited from widget.py

@TomJGooding
Copy link
Contributor

I'm not sure I understand what you mean by a button being disabled in this context? Perhaps the docs for Async handlers help explain this behaviour?

@willmcgugan
Copy link
Collaborator

Your MyFunction doesn't return for 5 seconds. This means that the app's message loop will not be able to process any additional messages until that returns. Input events are queued, which is desirable -- you don't want the app to not respond to input if it ever becomes too busy.

The solution is highly dependent on what you want to implement (I don't know what work your async sleep is designed to replicate). You might want to investigate workers, but there are also timers and call_later.

If you don't want the button to be clicked while the app is busy you can set button.disabled=True

I'd also encourage you to read the guide on Events and Messages.

Copy link

github-actions bot commented Oct 5, 2024

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants