Question

What is the preferred way to create rounded corners for buttons in Kivy?

Are there other equally viable ways to perform this task?

Was it helpful?

Solution

This is a tricky one. As far as I am concern Widgets are always rectangles. But we can change the background and put a couple of images for the normal and down states using the background_normal and background_down properties respectively. Also you will need to understand the border property.

With this two images called normal.png and down.png, you can start adding your round borders.

enter image description here enter image description here

Here is the piece of code, which is very straight forward (I try to explain the border property below):

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.lang import Builder

Builder.load_string("""
<Base>:
    Button:
        background_normal: 'normal.png'
        background_down: 'down.png'
        border: 30,30,30,30
""")


class Base(FloatLayout):
    pass

class ButtonsApp(App):
    def build(self):
        return Base()

if __name__ == "__main__":
    ButtonsApp().run()

The way I understand this (and I might be wrong) is this. The values in border: 30,30,30,30 tells how many pixels on the top, right, bottom and left are going to be used for the border of the background's button. The rest is going to be filled with the middle part. I am not sure here. By the way, If you want to see something cool, see for example border: 150,150,150,150. The reason is that we are picking up a border bigger than the actual image.

The caveat: Widgets are still rectangles. That means that even if you click on the rounded corners, the button still receive the event. I guess it is a fair price. If you want to do something better, maybe I can help you but we will need to use some maths to collide the points. One of the tricks with the Pong Game tutorial in the documentation is that actually the ball is a square. I posted a related question here, but you will need to use the Canvas

OTHER TIPS

If you are looking only for good looks, and aren't picky about the corners, though rounded, are still touch points, you can do it simply, as shown in this sample program (This has large radius for this sample):

from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.base import runTouchApp

kv="""
<RoundedButton@Button>:
    background_color: 0,0,0,0  # the last zero is the critical on, make invisible
    canvas.before:
        Color:
            rgba: (.4,.4,.4,1) if self.state=='normal' else (0,.7,.7,1)  # visual feedback of press
        RoundedRectangle:
            pos: self.pos
            size: self.size
            radius: [50,]
"""
class RoundedButton(Button):
    pass

Builder.load_string(kv)

runTouchApp(RoundedButton(text="Hit Me!"))

How about a self-made class with canvas items?

You can change the radius for each layout.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics import *

root=Widget()

class RoundCorner(RelativeLayout):
    def __init__(self,r=50,**kwargs):
        super(RoundCorner, self).__init__(**kwargs)
        self.surf=FloatLayout(); self.add_widget(self.surf)
        with self.canvas:
            Color(.3,0,3,0.3)
            Rectangle(pos=[-r,0],size=[r,self.size[1]])
            Rectangle(pos=[0,-r],size=[self.size[0],self.size[1]+2*r])
            Rectangle(pos=[self.size[0],0],size=[r,self.size[1]])

            Color(0,.3,0,.5)
            Ellipse(pos=[-r,-r],size=[2*r,2*r])
            Ellipse(pos=[self.size[0]-r,-r],size=[2*r,2*r])
            Ellipse(pos=[-r,self.size[1]-r],size=[2*r,2*r])
            Ellipse(pos=[self.size[0]-r,self.size[1]-r],size=[2*r,2*r])

            Color(1,1,1,0.3)
            self.bg=Rectangle(size=self.size)

root.add_widget(RoundCorner(size=[300,400],pos=[320,100]))
root.add_widget(RoundCorner(size=[100,100],pos=[100,200]))

class MyApp(App):
    def __init__(self):
        super(MyApp, self).__init__()
    def build(self):
        return root

if __name__=="__main__":
    MyApp().run()

I picked a relative layout, because it is easier to write this thing. It can be adopted to a widget by adding its global positions to canvas items, but for simplicity, I made like that. And colors selected like this to make the logic visible. Here is the example of the code above:

Enter image description here

Just adjust the Color in the code, and you are ready to go.

Enter image description here

This is better, since resolution isn't a thing (I may be wrong - I am not an expert in OpenGL).

Well, this is not a complete button. It is just a layout, with on touch down or press, and it can be used like a real button. I didn't included it in the code. There are a lot of stuff about events on the official site.

Using the material extension KivyMD there are many new Button classes with rounded corners:

A simple example

In Python using KV language:

from kivymd.app import MDApp

from kivy.lang import Builder

KV = '''
Screen:

    MDRoundFlatButton:
       text: "MDROUNDFLATBUTTON"
'''


class Example(MDApp):
    def build(self):
        return Builder.load_string(KV)


Example().run()

You could also import and extend abstract class kivymd.uix.button.MDRoundFlatButton.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top