Ferd
Ferd

Reputation: 1331

Rounded corners with Kivy (using Python only)

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.

Upvotes: 4

Views: 7806

Answers (1)

eyllanesc
eyllanesc

Reputation: 243947

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()

Upvotes: 4

Related Questions