Forum Archive

Making a progress bar?

chriscioffi

Context: I’m pretty experienced with Python, but new to Pythonista and iOS dev.

I have a SiriShortcut that needs a UI. It’s a yoga timer that takes a list off poses and times, and reads each pose and waits for the duration. I’d like to switch to using Pythonista in order to have the pose name visible.

I can make the basic UI and process the list, but I’m not sure how to make a visible progress bar. It seems like this should be a standard UI element but I’m not finding anything in the docs.

Can anyone give me a pointer? Thanks!

JonB

There is not a built in progress bar.

What you probably want to do is take a custom View class, with a custom draw() methos, which strokes a rect outline, then fills a narrower rect, based on a fractional width value. Or, draw N unfilled and M filled rectangles, etc. You would have a value property which when set, sets the internal attribute, then calls set_needs_display.

You could also install ui2 and use ProgressPathView. This allows any sort of path to be used as a progress bar.

Or, this a wrapper to the iOS objc UIProgressBar.

cvp

@chriscioffi I use two ui.Labels, one in the back with a text and a second one without text but with an half transparent background with the same height as the first label and with a width varying in function of the progress...

cvp

@chriscioffi other one like @jonb said, with only 2 pixels height

mikael

@chriscioffi, for a quick and dirty solution, use a disabled Slider as a progress bar.

There are also a couple of other recent threads for apps that might have relevant ”personal trainer” ideas.

chriscioffi

Thanks all! I’ll figure something out when I have a little more spare time.

reticulated

Here is a small snippet - far from comprehensive however it showcases using two views (one as container, one as the bar), with a label

# -*- coding: utf-8 -*-
"""
This was thrown together from various bits of unfinished code.

I was planning on making it a lot better (working on a bunch of UI stuff, checkboxes, drop down, radio, etc)

It works for my limited use case. 
"""
import ui

class ProgressBar(ui.View):

    def __init__(self, position=0, total=100, frame=(0,0, 250, 30)):
        self._position = position
        self.total = total
        self.frame = frame
        self.background_color = 'white'
        self.border_color = 'black'
        self.border_width = 0.5
        self._label = None

        self._progressbar = ui.View(
            frame=self.frame,
            background_color = 'green',
            width = 0
        )

        self.label_properties = dict(
            flex = 'WH',
            alignment=ui.ALIGN_CENTER,
            frame = self.frame
        )
        self.label = '0 %'

        self.add_subview(self._progressbar)
        self.add_subview(self._label)

    @property
    def label(self):
        return self._label

    @label.setter
    def label(self, value):
        assert isinstance(value, str), 'Use string value only'

        self.label_properties['text'] = value
        self._label = ui.Label(**self.label_properties)

    @property
    def position(self):
        return self._position

    @position.setter
    def position(self, value):
        if value > self.total:
            value = self.total

        self._position = value
        self._progressbar.width = (
            self.percent_of(
                self.percent_is(value, self.total),
                self.width
            )
        )
        self._label.text = '{} %'.format(
            int(self.percent_is(value, self.total))
        )


    @staticmethod
    def percent_is(part, whole):
        return 100.0 * float(part)/float(whole)

    @staticmethod
    def percent_of(percent, whole):
        return (percent * whole) / 100.0

    def set_percent(self, percent):
        self.position = self.percent_of(percent, self.total)

    def next(self):
        self.position += 1

    def draw(self):
        pass

if __name__ == '__main__':
    v = ui.View()
    v.background_color = 'white'
    x = ProgressBar()
    x.position = 90
    x.flex = ''
    x.frame = (80, 100, 250,30)
    v.add_subview(x)
    v.present()
    import time
    for i in range(11):
        ui.delay(x.next, i)

chriscioffi

Thanks, @reticulated ! That helped me understand what I was missing.

Mine is not at all generic, but it works.

C

chriscioffi

Thought I'd share my "final" version:

gist of asna.py

It's not very sophisticated but I think it will work nicely for me. All documentation needed should be in the header comment.