10

I thought to make one simple circular progress bar using kivy and python. I searched online documentation and GitHub repertoires, but not found single proper example explaining the concept of circular progress bar .

Like the image attached below. I thought to develop. Please anyone help me in this issue. enter image description here

jpyams
  • 4,030
  • 9
  • 41
  • 66

1 Answers1

20

New, better version

This post has received much more attention than I expected, therefore I've decided to put a little bit of effort into creating an upgraded version of this widget. It is now available on GitHub. Here is a sample of what can be done with it (the progress bars are actually transparent, I have added a black background to the gif to make everything visible):

Animated, better circular progress bar

I will keep the old post for reference below, if anyone is interested.

Old post

I've made a widget to represent what you want to achieve. There are some limitations however, precisely:

  1. Instead of just setting the progress bar value using .value, you need to call the set_value method. I am very unsure what should be done to achieve the same behaviour as in original ProgressBar class;

  2. You have to specify the size to achieve a circle, because the object itself is an ellipse.

Here is the code, also with example usage:

from kivy.app import App
from kivy.uix.progressbar import ProgressBar
from kivy.core.text import Label as CoreLabel
from kivy.lang.builder import Builder
from kivy.graphics import Color, Ellipse, Rectangle
from kivy.clock import Clock


class CircularProgressBar(ProgressBar):

    def __init__(self, **kwargs):
        super(CircularProgressBar, self).__init__(**kwargs)

        # Set constant for the bar thickness
        self.thickness = 40

        # Create a direct text representation
        self.label = CoreLabel(text="0%", font_size=self.thickness)

        # Initialise the texture_size variable
        self.texture_size = None

        # Refresh the text
        self.refresh_text()

        # Redraw on innit
        self.draw()

    def draw(self):

        with self.canvas:
            
            # Empty canvas instructions
            self.canvas.clear()

            # Draw no-progress circle
            Color(0.26, 0.26, 0.26)
            Ellipse(pos=self.pos, size=self.size)

            # Draw progress circle, small hack if there is no progress (angle_end = 0 results in full progress)
            Color(1, 0, 0)
            Ellipse(pos=self.pos, size=self.size,
                    angle_end=(0.001 if self.value_normalized == 0 else self.value_normalized*360))

            # Draw the inner circle (colour should be equal to the background)
            Color(0, 0, 0)
            Ellipse(pos=(self.pos[0] + self.thickness / 2, self.pos[1] + self.thickness / 2),
                    size=(self.size[0] - self.thickness, self.size[1] - self.thickness))

            # Center and draw the progress text
            Color(1, 1, 1, 1)
            #added pos[0]and pos[1] for centralizing label text whenever pos_hint is set
            Rectangle(texture=self.label.texture, size=self.texture_size,
                  pos=(self.size[0] / 2 - self.texture_size[0] / 2 + self.pos[0], self.size[1] / 2 - self.texture_size[1] / 2 + self.pos[1]))


    def refresh_text(self):
        # Render the label
        self.label.refresh()

        # Set the texture size each refresh
        self.texture_size = list(self.label.texture.size)

    def set_value(self, value):
        # Update the progress bar value
        self.value = value

        # Update textual value and refresh the texture
        self.label.text = str(int(self.value_normalized*100)) + "%"
        self.refresh_text()

        # Draw all the elements
        self.draw()


class Main(App):

    # Simple animation to show the circular progress bar in action
    def animate(self, dt):
        if self.root.value < 80:
            self.root.set_value(self.root.value + 1)
        else:
            self.root.set_value(0)

    # Simple layout for easy example
    def build(self):
        container = Builder.load_string(
            '''CircularProgressBar:
    size_hint: (None, None)
    height: 200
    width: 200
    max: 80''')

        # Animate the progress bar
        Clock.schedule_interval(self.animate, 0.1)
        return container


if __name__ == '__main__':
    Main().run()

Output:

Animated circular progress bar

Community
  • 1
  • 1
Kacperito
  • 1,277
  • 1
  • 10
  • 27
  • If possible will you suggest me how to make separate file for kivy and python for the above single python file. Unchanging the functionality – Nandakishor Jahagirdar May 28 '18 at 05:08
  • Is it possible to set the value of the circular progress bar manually instead of the self updating, For instance defining slider inside the kivy string and corresponding to the value of the slider, circular progress bar has to move and change the text inside the circular progress bar. – Nandakishor Jahagirdar May 28 '18 at 05:58
  • You can use my widget the same way you use `ProgressBar`, except you have to call set_value(value) whenever you want to set one (check the example I've provided). This will automatically update both circular and text progression. Regarding the separation, just make a new module to put the class there and import it wherever you want to use it. As far as I'm aware to use it in your .kv layout you have to also import it in the module with your main App class. Experiment a bit and see what can you do with it! – Kacperito May 28 '18 at 10:23
  • 1
    @NandakishorJahagirdar - please see the newer version of the widget if you are still interested in using it. Perhaps it will prove to be more useful! – Kacperito Sep 04 '19 at 18:28