Øyvind
Øyvind

Reputation: 139

Center of Canvas in Python vs kivy language

I have made an easy app where i try to show my issue. When using python to draw a line in kivy (using the with self.canvas method) the line gets drawn from a center_x and center_y of 50.

Using kivy Lang draws the line correctly in the center. Here is a simple code example:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line


class TestWidget(Widget):

    def draw(self):
        with self.canvas:
            Line(points=[self.center_x,self.center_y,self.center_x,self.center_y+100], width=2)


class TestApp(App):

    def build(self):
        test = TestWidget()
        test.draw()
        return test


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

and the corresponding test.kv file:

#:kivy 1.11.1

<TestWidget>:
    canvas:      
        Line:
            width: 5
            points: (self.center_x,self.center_y,self.center_x,self.center_y+100)

result is like this:

enter image description here

Any idea why using python code is not working?

Upvotes: 2

Views: 248

Answers (2)

eyllanesc
eyllanesc

Reputation: 243897

Both codes are not equivalent, in the case of python you are only establishing that the position of the line is based on the center of the widget at that time, that is, at the beginning, instead in .kv it is indicating that the line position is always it will be with respect to the center of the Widget.

TL; DR;

Explanation:

In kv the binding is a natural and implicit process that in python is not doing it, besides that its implementation is simpler, for example the following code:

Foo:
    property_i: other_object.property_j

In that case its equivalent in python is:

foo = ...
other_object = ...
other_object.bind(property_j=foo.setter("property_i"))

And it is not:

foo.property_i = other_object.property_j

Since with the bind you are indicating before a change of property_j update property_i with that value, but in the last code you only indicate at this moment copy the value of property_j in property_i.

In your particular case, the center you take is before displaying the widget and kivy taking default configurations and other properties into consideration, changes the geometry of the widget after it is displayed.

Solution

Making the necessary modifications taking into account the previous explanation the following code is an equivalent of the .kv code

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line


class TestWidget(Widget):
    def __init__(self, **kwargs):
        super(TestWidget, self).__init__(**kwargs)
        with self.canvas:
            self.line = Line(width=2)
        self.bind(center=lambda *args: self.draw())

    def draw(self):
        self.line.points = [
            self.center_x,
            self.center_y,
            self.center_x,
            self.center_y + 100,
        ]


class TestApp(App):
    def build(self):
        test = TestWidget()
        return test


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

Upvotes: 2

inclement
inclement

Reputation: 29450

With kv language your declarations automatically update when their dependencies change. That means that when the widget's centre goes from (50, 50) to something else, the line is redrawn in the new location.

Your Python code doesn't do this, it just draws the line once using whatever its centre is at the moment the code runs. That value is (50, 50), since that's the default before positioning has taken place.

The solution is to write code in Python that updates the line when the widget centre changes, something along the lines of declaring the line with self.line = Line(...) and self.bind(centre=some_function_that_updates_self_dot_line).

Upvotes: 1

Related Questions