简体   繁体   English

Kivy:旋转可点击的图像

[英]Kivy: Rotate clickable image

I have created a clickable image, and I can rotate it, but I can't get the origin of the rotation right. 我已经创建了一个可点击的图像,可以旋转它,但是不能正确获得旋转的原点。 The following code places two images, one of which is clickable, onto a form. 下面的代码将两个图像(其中一个可单击)放置在窗体上。 I expect them to be on top of one another, with the clickable one rotated. 我希望它们彼此叠置,并旋转可点击的。 The clickable one is rotated, but the rotation origin is something other than the image center which results in it being moved, even though I specify the center as what I expect it to be. 可单击的对象被旋转了,但是旋转原点是不同于图像中心的东西,即使它指定了我希望的中心,它也会导致图像中心被移动。 Interestingly, it is clickable, but the click region is where it is supposed be: on the unclickable image. 有趣的是,它是可单击的,但单击区域应位于该位置:在不可单击的图像上。 That is, the red image turns green when you click the blue image! 也就是说,单击蓝色图像时,红色图像变为绿色!

I modified this code from some kv language I found online, and the rotation worked for that. 我用网上找到的一些kv语言修改了此代码,轮换对此起作用。 But I need to set up the button programmatically, because I will eventually have a form with many, each rotated and moved differently. 但是我需要以编程方式设置按钮,因为最终我将获得一个表单,其中有许多表单,每个表单的旋转和移动方式都不相同。

I feel like the problem is in mismatched coordinate units, but I haven't found much online and in the docs. 我觉得问题出在坐标单位不匹配,但是我在网上和文档中找不到很多。 Any help is appreciated. 任何帮助表示赞赏。 The images are just 32x32 icons, which you can replace with whatever you have handy. 图像只是32x32的图标,您可以用任何方便的图标替换它们。

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics.context_instructions import PopMatrix, PushMatrix
from kivy.graphics import Rotate
from kivy.uix.behaviors import ButtonBehavior

h = .05
w = h * 1.6

class IconButton(ButtonBehavior, Image):

    def __init__(self, angle=0, **kwargs):
        super(IconButton, self).__init__(**kwargs)

        self.canvas.before.add(PushMatrix())
        self.canvas.before.add(Rotate(angle = angle, origin = [self.get_center_x(), self.get_center_y()]))

        self.canvas.after.add(PopMatrix())

    def on_press(event):
        event.source = event.img_dn
        print '{} pressed'.format(event.id)

    def on_release(event):
        event.source = event.img_up
        print '{} released'.format(event.id)


class TestApp(App):

    def build(self):
        layout = FloatLayout()
        btn = IconButton(angle=-20, id='b1', size_hint=(h,w), pos_hint = {'center_x': .25, 'center_y': .9})
        btn.img_up = 'loudspeaker_red_32.png'
        btn.img_dn = 'loudspeaker_green_32.png'
        btn.source = btn.img_up

        layout.add_widget(btn)

        im = Image(id='i1', size_hint=(h,w), pos_hint = {'center_x': .25, 'center_y': .9})
        im.source = 'loudspeaker_blue_32.png'

        layout.add_widget(im)

        return layout

class MainApp(App):
    def build(self):
        layout = Builder.load_string(src)
        return layout


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

If you try to draw a line around your widget (a very useful debugging technique), you'll discover that when IconButton.__init__() is called pos atribute is still (0, 0) and code thinks that your button is in bottom left corner. 如果尝试在小部件周围画一条线(一种非常有用的调试技术),则会发现在IconButton.__init__() pos IconButton.__init__()仍然为(0, 0)并且代码认为您的按钮位于左下角角。

class IconButton(ButtonBehavior, Image):

    def __init__(self, angle=0, **kwargs):
        super(IconButton, self).__init__(**kwargs)

        self.canvas.before.add(PushMatrix())

        self.canvas.add(Color(rgba=(1,0,0,.5)))
        self.canvas.add(Line(rectangle=(self.x, self.y, self.width, self.height)))

        self.canvas.after.add(PopMatrix())

    def on_press(event):
        event.source = event.img_dn
        print '{} pressed'.format(event.id)

    def on_release(event):
        event.source = event.img_up
        print '{} released'.format(event.id)

This is happening beacuse pos attribute is actualized when your widget is placed inside a layout. 之所以发生这种情况,是因为将小部件放置在布局中时会实现pos属性。 When you're creating a Widget it doesn't know where it will be placed. 当您创建窗口小部件时,它不知道将其放置在何处。 That's why you need to actualize rotating origin inside on_pos method that called later, when all properties are set correctly: 这就是为什么在正确设置所有属性后,需要在稍后调用的on_pos方法中实现旋转原点的原因:

class IconButton(ButtonBehavior, Image):

    def __init__(self, angle=0, **kwargs):
        super(IconButton, self).__init__(**kwargs)

        self.rotate = Rotate(angle = angle)

        self.canvas.before.add(PushMatrix())
        self.canvas.before.add(self.rotate)
        self.canvas.after.add(PopMatrix())

        self.bind(pos=self.update_canvas)
        self.bind(size=self.update_canvas)

    def update_canvas(self, *args):
        self.rotate.origin = self.center         

    def on_press(event):
        event.source = event.img_dn
        print '{} pressed'.format(event.id)

    def on_release(event):
        event.source = event.img_up
        print '{} released'.format(event.id)

This inconvenience is an exact reason why we prefer to use .kv language in order to describe layouts. 这种不便是我们偏爱使用.kv语言来描述布局的确切原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM