Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounded corners with Kivy (using Python only)

Tags:

python

kivy

I have this little Kivy app (Python version: 3.7, Kivy version: 1.11.1):

Code#1

from kivy.app import App
from kivy.lang import Builder
from kivy.config import Config
from kivy.uix.floatlayout import FloatLayout

Config.set("graphics", "width", "500")
Config.set("graphics", "height", "300")

kv = """
<RoundedCornerLayout@FloatLayout>:
    background_color: 0,0,0,0
    canvas.before:
        Color:
            rgba: (.4,.4,.4,1)
        RoundedRectangle:
            pos: self.pos
            size: self.size
            radius: [(40, 40), (40, 40), (20, 20), (20, 20)]
"""

Builder.load_string(kv)


class RoundedCornerLayout(FloatLayout):
    def __init__(self):
        super().__init__()
        self.size_hint = (None, None)
        self.size = (400, 200)
        self.pos_hint = {"center_x": 0.5, "center_y": 0.5}


class MainApp(App):
    def build(self):
        return RoundedCornerLayout()


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

And with that code, I have the following result:

enter image description here

Cute, isn't it?

Now, let's try to get the same result using only Python. I'm trying with the following code:

Code#2

from kivy.app import App
from kivy.config import Config
from kivy.graphics import Color
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout

Config.set("graphics", "width", "500")
Config.set("graphics", "height", "300")


class RoundedCornerLayout(FloatLayout):
    def __init__(self):
        super().__init__()
        self.size_hint = (None, None)
        self.size = (400, 200)
        self.pos_hint = {"center_x": 0.5, "center_y": 0.5}

        self.background_color = (0, 0, 0, 0)
        self.canvas.before.add(Color(.4, .4, .4, 1))
        self.canvas.before.add(Rectangle(
            pos=self.pos,
            size=self.size,
            radius=[(40, 40), (40, 40), (20, 20), (20, 20)]))


class MainApp(App):
    def build(self):
        return RoundedCornerLayout()


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

Fair enough, I thought.

But then I got this result:

enter image description here

As far as my knowledge goes, both instructions (Code#1 and Code#2) are saying the same, but in different ways. Scientifically proven, it's not like that.

...So what I'm trying to understand here, and the point of my question, is: what's the functional difference between Code#1 and Code#2? Why are they displaying different results? And what would be the correct way to "translate" Code#1 into Python-only code?

Ignore the fact that just keeping the kivy code is the easiest solution. What I need here is to understand this behaviour, explaining my reasons would unnecessarily extend this question, let's just say you can only control what you understand.

like image 338
Ferd Avatar asked Oct 27 '25 08:10

Ferd


1 Answers

You have 2 errors:

  • The item is not a Rectangle but a RoundedRectangle.
  • In .kv the canvas is repainted if a property used in the painting changes as there is a binding, however in Python you have to make that binding explicitly.
from kivy.app import App
from kivy.config import Config
from kivy.graphics import Color, RoundedRectangle
from kivy.uix.floatlayout import FloatLayout

Config.set("graphics", "width", "500")
Config.set("graphics", "height", "300")


class RoundedCornerLayout(FloatLayout):
    def __init__(self):
        super().__init__()

        with self.canvas.before:
            Color(0.4, 0.4, 0.4, 1)
            self.rect = RoundedRectangle(
                pos=self.pos,
                size=self.size,
                radius=[(40, 40), (40, 40), (20, 20), (20, 20)],
            )
        self.bind(pos=lambda obj, pos: setattr(self.rect, "pos", pos))
        self.bind(size=lambda obj, size: setattr(self.rect, "size", size))

        self.size_hint = (None, None)
        self.size = (400, 200)
        self.pos_hint = {"center_x": 0.5, "center_y": 0.5}
        self.background_color = 0, 0, 0, 1


class MainApp(App):
    def build(self):
        return RoundedCornerLayout()


if __name__ == "__main__":
    MainApp().run()
like image 70
eyllanesc Avatar answered Oct 28 '25 20:10

eyllanesc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!