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

Delay or after function #1415

Closed
GiorgiMatcharashvili opened this issue Jan 27, 2022 · 9 comments
Closed

Delay or after function #1415

GiorgiMatcharashvili opened this issue Jan 27, 2022 · 9 comments
Labels
enhancement New features, or improvements to existing features. good first issue Is this your first time contributing? This could be a good place to start!

Comments

@GiorgiMatcharashvili
Copy link

GiorgiMatcharashvili commented Jan 27, 2022

Is your feature request related to a problem? Please describe.
I have been trying to make flashing text with Toga. I can make it using button, when you click the button text will disappear and when you click it again text will appear again. It looks like this:

def startup(self):
    self.text_appearance = True

    def flash(widget):
        if self.text_appearance:
            main_box.remove(text)
            self.text_appearance = False
        else:
            main_box.add(text)
            self.text_appearance = True

    text = toga.Label("This text will disappear if you click the button")
    button = toga.Button("click me", on_press=flash)

    # Add components to the main box
    main_box = toga.Box(style=Pack(direction=COLUMN))
    main_box.add(button)
    main_box.add(text)

    # Show main window
    self.main_window = toga.MainWindow(title=self.formal_name)
    self.main_window.content = main_box
    self.main_window.show()

But I am unable to find the function which will waits, for example, 2 seconds and then runs the flash function. Like the time.sleep(), Or to be more clear and get help from other GUI framework, like the after function from Tkinter.
The solution of my problem in Tkinter: https://stackoverflow.com/questions/27533244/how-to-make-a-flashing-text-box-in-tkinter

Describe the solution you'd like
To solve this problem, I would like to be able to do flashing without button, for example, something like this:

def startup(self):
    self.text_appearance = True

    def flash(widget):
        if self.text_appearance:
            main_box.remove(text)
            self.text_appearance = False
        else:
            main_box.add(text)
            self.text_appearance = True
        self.main_window.after(2000, flash)

    text = toga.Label("This text is flashing")

    # Add components to the main box
    main_box = toga.Box(style=Pack(direction=COLUMN))
    main_box.add(text)

    # Show main window
    self.main_window = toga.MainWindow(title=self.formal_name)
    self.main_window.content = main_box
    self.main_window.show()

    flash()

Additional context
Please inform me if there is a solution already. if not, please add this feature in Toga. I planed to do many things using this feature and I would love to see it.

@GiorgiMatcharashvili GiorgiMatcharashvili added the enhancement New features, or improvements to existing features. label Jan 27, 2022
@GiorgiMatcharashvili GiorgiMatcharashvili changed the title delay or after function Delay or after function Jan 27, 2022
@t-arn
Copy link
Contributor

t-arn commented Jan 27, 2022

You could try this: App.add_background_task()

@freakboy3742
Copy link
Member

Thanks for the suggestion! As @t-arn noted, you can do this today with App.add_background_task():

class MyApp:
    def some_method(self):
        ...
        self.add_background_task(run_later)
        self.add_background_task(run_later_async)

    def run_later(self):
        yield 5
        self.main_window.title = 'It is later'

    async def run_later_async(self):
        await asyncio.sleep(10)
        self.main_window.title = "It is even later"

That said - "run this code in N seconds" is a common enough design pattern that it would be worth adding a utility method run_later(timeout, method, *args, **kwargs) that wraps a call to add_background_task() that implements what I've described above.

@freakboy3742 freakboy3742 added good first issue Is this your first time contributing? This could be a good place to start! up-for-grabs labels Jan 28, 2022
@GiorgiMatcharashvili
Copy link
Author

I tried with App.add_background_task(), and It worked perfectly, Text is flashing, I can do also windows changing and all of this staff. This is the source code if it will needed in the future:

def startup(self):
    self.text_appearance = True

    # Add components to the main box
    self.main_box = toga.Box(style=Pack(direction=COLUMN))
    self.label = toga.Label("this text is flashing")
    self.main_box.add(self.label)

    # Show main window
    self.main_window = toga.MainWindow(title=self.formal_name)
    self.main_window.content = self.main_box
    self.main_window.show()

    self.add_background_task(self.flash)

def flash(self, widget):
    yield 0.2
    if self.text_appearance:
        self.main_box.remove(self.label)
        self.text_appearance = False
    else:
        self.main_box.add(self.label)
        self.text_appearance = True
    self.add_background_task(self.flash)

Also thanks for your work, I am using Toga like a month, and already love it. keep it up!

@VahilaK
Copy link

VahilaK commented Jan 30, 2022

Can I work on this?

@freakboy3742
Copy link
Member

@VahilaK Absolutely!

One consideration to keep in mind is that we will want to have a single "run_later" method that can handle any function, generator or coroutine (i.e., we need to be able to handle a simple function, a function that has a yield N in it, and a function that is declared async def). You may want to look at handlers.py in toga.core to see how we handle event callbacks to achieve the same thing.

You'll also need to add a demonstration case; there is a handlers demo in the example folder that will need to be extended to include an example of your new method being used in different ways.

@VahilaK
Copy link

VahilaK commented Feb 19, 2022

@freakboy3742 I added a function 'run_later' in app.py from toga.core. I updated the handlers demo but when running the handlers example, I get an error "HandlerApp object has no attribute run_later". How can I use the 'run_later' function in handlers example? If that's not possible how can I use the 'run_later' function as part of an example to test out it's functionality.

P.S: This seems like a simple workflow but I am not very experienced with python and I am not able to figure it out. I apologize for the inconvenience.

@freakboy3742
Copy link
Member

@VahilaK If you're getting an error that the method that you've added isn't being found, it's probably because you're not using your local (modified) version of Toga to run the app. See this guide for how to set up your environment for testing.

@VahilaK VahilaK mentioned this issue Feb 21, 2022
4 tasks
@freakboy3742 freakboy3742 removed up-for-grabs good first issue Is this your first time contributing? This could be a good place to start! labels Mar 29, 2022
@freakboy3742 freakboy3742 added the good first issue Is this your first time contributing? This could be a good place to start! label Apr 15, 2023
@mhsmith
Copy link
Member

mhsmith commented Sep 18, 2023

See related discussion starting at #2099 (comment).

@mhsmith
Copy link
Member

mhsmith commented Nov 22, 2023

Since Toga 0.4, the event loop is available as a property on the App object. So this can be done using self.loop.call_later or self.loop.call_at, where self is the App.

@mhsmith mhsmith closed this as completed Nov 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New features, or improvements to existing features. good first issue Is this your first time contributing? This could be a good place to start!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants