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

update_smooth blocks with sleep #5

Closed
puttehi opened this issue Aug 7, 2022 · 6 comments
Closed

update_smooth blocks with sleep #5

puttehi opened this issue Aug 7, 2022 · 6 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@puttehi
Copy link

puttehi commented Aug 7, 2022

for i in range(distance):
del i
self.completion += 1
self._update_bar()
time.sleep(0.005)

Using update_smooth will slow down your program. Every time you call it, you add 5 ms to your program execution per percent of progress. It should be non-blocking to have an use case.

One idea would be to have the bar in its own thread and have the context manager hide the ugly thread details from the user, keeping the same or similar interface.

@AnnikaV9
Copy link
Owner

AnnikaV9 commented Aug 7, 2022

I realized this, and thats why i packed both update() and update_smooth(). So a program that takes speed and latency seriously, or one that spawns multiple loading bars could use update(). But I can see that 5ms per percentage can be a bit too much.

I initially experimented with threading, since that allowed me to also add in some animations like a spinning bar, but it came with its own set of issues that required quite some additional code and increased complexity.

For example, one issue was; calling log() while the thread still hasn't completed it's update_smooth() call caused display issues that required a few delays and checks to be implemented, which actually ended up increasing the total time used.

But I'm open to a good solution that doesn't increase code complexity too much

@puttehi
Copy link
Author

puttehi commented Aug 7, 2022

Threads will increase complexity for sure and fighting race conditions can be difficult but as the project is in such an early stage, it might be quite doable to architect for that.

Alternatively, one could use a delta time approach to keep the printing operations in the main thread but have the timer in a background thread, using that as the progress value between the previous and the target percentage. Then you could also quite easily give the user the option to configure the animation duration.
Just have to handle multiple overlapping calls somehow, perhaps by updating the target percentage, and then setting the current time in the timer to the delta calculated from previous progress so the animation just continues on with the new settings 🤔

I think there's also ready-made solutions for tweening but you still need a time value but without a concrete update loop, a thread is needed to implement that update loop independent of main. (I think)

Food for thought 😄

@AnnikaV9
Copy link
Owner

AnnikaV9 commented Aug 7, 2022

Yeah, I'm sure there's a solution, but it's just about finding and implementing the right one. Will look into the delta time approach.

@AnnikaV9 AnnikaV9 closed this as completed Aug 7, 2022
@AnnikaV9 AnnikaV9 reopened this Aug 7, 2022
@AnnikaV9
Copy link
Owner

AnnikaV9 commented Aug 7, 2022

I have added a disclaimer in the Usage section of README.md stating the delay caused by update_smooth(), so users will know they shouldn't use it for now if they value execution speed

@AnnikaV9 AnnikaV9 added enhancement New feature or request help wanted Extra attention is needed labels Aug 7, 2022
@AnnikaV9
Copy link
Owner

AnnikaV9 commented Aug 8, 2022

@puttehi I have implemented something simple in threading, and pushed it to the non-blocking-testing branch. In this implementation, using update_smooth() and letting it finish will be completely non-blocking. However if another lowbar function is called (log(), update_smooth(), update() and clear()) it will block it until the smooth update is complete. I feel like this is a good middle ground between complete blocking and complete non-blocking, as it keeps the code pretty simple.

    def __init__(self, bar_load_fill: str="#", bar_blank_fill: str="-"):
       
        ...
       
        self.bar_is_smoothing: bool = False
    
    ...
    
    def _update_bar_smooth(self, percentage: int):

        self.bar_is_smoothing = True
        distance: int = percentage - self.completion
        for i in range(distance):
            del i
            self.completion += 1
            self._update_bar()
            time.sleep(0.005)
        self.bar_is_smoothing = False
 
    def _block_when_smoothing(self):

        """
        Blocks the main thread if bar is still running _update_bar_smooth()
        Only used if another function call is performed. If the bar is
        left to smooth properly without calling other functions, it will
        be non-blocking
        """

        while True:
            if not self.bar_is_smoothing:
                break

    def update_smooth(self, percentage: int):

        if not isinstance(percentage, int):
            raise TypeError("percentage should be type int")

        self._block_when_smoothing()

        threading.Thread(target=self._update_bar_smooth, args=[percentage]).start()

_block_when_smoothing() is called in other lowbar functions before they perform their actual task.

What do you think about this implementation?

@AnnikaV9
Copy link
Owner

AnnikaV9 commented Aug 9, 2022

I have tested and pushed the changes to the master branch, closing this issue as completed.

@AnnikaV9 AnnikaV9 closed this as completed Aug 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants