Fanhe Kong
Fanhe Kong

Reputation: 29

How to use Kivy Rotate in pure Python code (not kvlang)?

I want to rotate a button with the pure python code instead of the kvlang.

With kvlang we can rotate a button as shown in the example. The button is rotated 45 degree around its own center. Below is the original code:

from kivy.app import App
from kivy.lang import Builder
kv = '''
FloatLayout:
    Button:
        text: 'hello world'
        size_hint: None, None
        pos_hint: {'center_x': .5, 'center_y': .5}
        canvas.before:
            PushMatrix
            Rotate:
                angle: 45
                origin: self.center
        canvas.after:
            PopMatrix
'''
class RotationApp(App):
    def build(self):
        return Builder.load_string(kv)
RotationApp().run()

But when I try to rewrite this example with the pure python code as below, the button is rotated around somewhere else, as shown here:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import PushMatrix, PopMatrix, Rotate
class MyButton(Button):
    def __init__(self):
        super().__init__()
        self.text = 'hello world'
        self.size_hint = (None, None)
        self.pos_hint = {'center_x': .5, 'center_y': .5}
        with self.canvas.before:
            PushMatrix()
            Rotate(origin=self.center, angle=45)
        with self.canvas.after:
            PopMatrix()
class RotationApp(App):
    def __init__(self):
        super().__init__()
        self.layout = FloatLayout()
        self.button = MyButton()
        self.layout.add_widget(self.button)
    def build(self):
        return self.layout
RotationApp().run()

The above two pieces of code are not producing the same result. Is there anything we did wrong?

UPDATE:

The puzzle is solved as suggested by @inclement, solution as below:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import PushMatrix, PopMatrix, Rotate
class MyButton(Button):
    def __init__(self):
        super().__init__()
        self.text = 'hello world'
        self.size_hint = (None, None)
        self.pos_hint = {'center_x': .5, 'center_y': .5}
        with self.canvas.before:
            PushMatrix()
            
            # Rotate(origin=self.center, angle=45)  # previous approach
            self.rotation = Rotate(origin=self.center, angle=45)
            self.bind(center=lambda _, value: setattr(self.rotation, "origin", value))
            
        with self.canvas.after:
            PopMatrix()
class RotationApp(App):
    def __init__(self):
        super().__init__()
        self.layout = FloatLayout()
        self.button = MyButton()
        self.layout.add_widget(self.button)
    def build(self):
        return self.layout
RotationApp().run()

Upvotes: 2

Views: 496

Answers (1)

inclement
inclement

Reputation: 29450

In your Python code, self.center is evaluated just once during the __init__. In the kv code, a binding is automatically created to reset the Rotate instruction's origin property every time it changes.

You need to put that missing functionality in the Python code, something like self.rotation = Rotate(...) and self.bind(center=lambda instance, value: setattr(self.rotation, "origin", value)) (although I'm sure you can think of a nicer way to set that up, that's just the inline example).

Upvotes: 1

Related Questions