代码之家  ›  专栏  ›  技术社区  ›  jasan

Kivy:处理事件

  •  2
  • jasan  · 技术社区  · 8 年前

    类中的Palete是按钮,它与用于在类Paint中删除画布内容的方法绑定。我认为,解决方法是使用自定义事件。我没有事件方面的经验,也没有了解《kivy手册》中的描述。我需要保留类的结构。你能帮我吗?

    from kivy.app import App
    from kivy.uix.widget import Widget
    
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.button import Button
    
    from kivy.graphics import Color, Rectangle
    from functools import partial
    
    
    class Paint(Widget):
    
        def __init__(self, palete,**kwargs):
            # make sure we aren't overriding any important functionality
            super(Paint, self).__init__(**kwargs)
            self.palete = palete
            self.contents = []
    
        def on_touch_down(self, touch):
            color = self.palete.act_col
            with self.canvas:
                Color(color[0], color[1], color[2])
                sqr = Rectangle(pos = (touch.x, touch.y), size=(20, 40))
                self.contents.append(sqr)
    
    class Palete(BoxLayout):
        def __init__(self, **kwargs):
            super(Palete, self).__init__(**kwargs)
    
            self.act_col= (1, 0, 0)
    
            self.licol =[]
            self.licol.append((1, 0, 0))
            self.licol.append((0, 1, 0))
    
            self.layout = BoxLayout(size_hint=(1, None), height=50)
    
            for i in range(0, len(self.licol)):
                but = Button( id = str(i))
                col = self.licol[i]
                but.background_normal = ''
                but.background_color = (col[0], col[1], col[2], 1)
    
    
                act = partial(self.SetColor, but.id)
                but.bind(on_press=act)
                self. layout.add_widget(but)
    
            but = Button(text="<--",on_press = self.ClearContents)
            self.layout.add_widget(but)
    
        def SetColor(self,_col, h):
            ind = int(_col)
            self.act_col = self.licol[ind]
            pass
    
        def ClearContents(self, obj):
            if len(self.contents)!= 0:
                self.canvas.remove(self.contents[-1])
                self.contents = self.contents[:-1]
    
    
    class MyPaintApp(App):
    
        def build(self):
            root = BoxLayout(orientation='vertical')
            self.palete = Palete()
    
            self.paint =Paint(self.palete)
            root.add_widget(self.paint)
    
            root.add_widget(self.palete.layout)
    
            return root
    
    
    if __name__ == '__main__':
        MyPaintApp().run()
    
    2 回复  |  直到 8 年前
        1
  •  2
  •   FJSevilla    8 年前

    canvas contents Paint 类,您正试图从 Palate

    如果你不想改变结构,你可以做的是传递你的 油漆 类到 Palete 类别:

    from kivy.app import App
    from kivy.uix.widget import Widget
    
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.button import Button
    
    from kivy.graphics import Color, Rectangle
    from functools import partial
    
    
    class Paint(Widget):
    
        def __init__(self, palete,**kwargs):
            # make sure we aren't overriding any important functionality
            super(Paint, self).__init__(**kwargs)
            self.palete = palete
            self.palete.paint = self
            self.contents = []
    
    
        def on_touch_down(self, touch):
            color = self.palete.act_col
            with self.canvas:
                Color(color[0], color[1], color[2])
                sqr = Rectangle(pos = (touch.x, touch.y), size=(20, 40))
                self.contents.append(sqr)
    
    class Palete(BoxLayout):
        def __init__(self, **kwargs):
            super(Palete, self).__init__(**kwargs)
            self.act_col= (1, 0, 0)
            self.licol =[]
            self.licol.append((1, 0, 0))
            self.licol.append((0, 1, 0))
            self.paint = None
    
            self.layout = BoxLayout(size_hint=(1, None), height=50)
    
            for i in range(0, len(self.licol)):
                but = Button( id = str(i))
                col = self.licol[i]
                but.background_normal = ''
                but.background_color = (col[0], col[1], col[2], 1)
    
    
                act = partial(self.SetColor, but.id)
                but.bind(on_press=act)
                self. layout.add_widget(but)
    
            but = Button(text="<--",on_press = self.ClearContents)
            self.layout.add_widget(but)
    
        def SetColor(self,_col, h):
            ind = int(_col)
            self.act_col = self.licol[ind]
            pass
    
        def ClearContents(self, obj):
            if not self.paint:
                return
            if self.paint.contents:
                self.paint.canvas.remove(self.paint.contents.pop())
    
    
    class MyPaintApp(App):
    
        def build(self):
            root = BoxLayout(orientation='vertical')
            self.palete = Palete()
    
            self.paint = Paint(self.palete)
            root.add_widget(self.paint)
    
            root.add_widget(self.palete.layout)
    
            return root
    
    
    if __name__ == '__main__':
        MyPaintApp().run()
    

    我认为应该从实现画布的类中删除对象,以保留封装。另一个选项是将回调移动到 油漆 类并绑定 on_press 事件(来自Paint类):

    from kivy.app import App
    from kivy.uix.widget import Widget
    
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.button import Button
    
    from kivy.graphics import Color, Rectangle
    from functools import partial
    
    
    class Paint(Widget):
    
        def __init__(self, palete,**kwargs):
            # make sure we aren't overriding any important functionality
            super(Paint, self).__init__(**kwargs)
            self.palete = palete
            self.contents = []
            self.palete.clear_btn.bind(on_press = self.clear_contents)
    
        def on_touch_down(self, touch):
            color = self.palete.act_col
            with self.canvas:
                Color(color[0], color[1], color[2])
                sqr = Rectangle(pos = (touch.x, touch.y), size=(20, 40))
                self.contents.append(sqr)
    
        def clear_contents(self, obj):
            if self.contents:
                self.canvas.remove(self.contents.pop())
    
    class Palete(BoxLayout):
        def __init__(self, **kwargs):
            super(Palete, self).__init__(**kwargs)
            self.act_col= (1, 0, 0)
            self.licol =[]
            self.licol.append((1, 0, 0))
            self.licol.append((0, 1, 0))
            self.paint = None
    
            self.layout = BoxLayout(size_hint=(1, None), height=50)
    
            for i in range(0, len(self.licol)):
                but = Button( id = str(i))
                col = self.licol[i]
                but.background_normal = ''
                but.background_color = (col[0], col[1], col[2], 1)
    
    
                act = partial(self.set_color, but.id)
                but.bind(on_press=act)
                self. layout.add_widget(but)
    
            self.clear_btn = Button(text="<--")
            self.layout.add_widget(self.clear_btn)
    
        def set_color(self,_col, h):
            ind = int(_col)
            self.act_col = self.licol[ind]
    
    
    class MyPaintApp(App):
    
        def build(self):
            root = BoxLayout(orientation='vertical')
            self.palete = Palete()
    
            self.paint = Paint(self.palete)
            root.add_widget(self.paint)
    
            root.add_widget(self.palete.layout)
    
            return root
    
    
    if __name__ == '__main__':
        MyPaintApp().run()
    

    使用 list.pop 而不是切片以从列表中删除最后一项。它更具可读性,不要每次都复制列表。

    enter image description here

        2
  •  2
  •   ikolim    8 年前

    重载方法\uu init__

    def __init__(self, **kwargs):
        super(Paint, self).__init__(**kwargs)
    

    我们重载方法\uu init\uuu()以添加小部件并定义 他们的行为。一个人不应该忘记打电话给super,以便 实现被重载的原始类的功能。 还要注意的是,最好不要在 调用super,因为它们有时在内部使用。

    油漆类

    1、增加Kivy属性

    由于您将访问调色板和内容,我们声明如下:

    from kivy.properties import ObjectProperty, ListProperty
    
    
    class Paint(Widget):
        palette = ObjectProperty(None)
        contents = ListProperty([])
    

    2.on_touch_down事件

    默认情况下,触摸事件被调度到所有当前显示的小部件。这意味着小部件接收触摸事件,无论它是否发生在其物理区域内。Kivy将触摸事件发送给所有小部件,并让它们决定如何对其做出反应。如果您只想响应小部件/按钮内的触摸事件,则必须使用self进行检查。collide_point方法。当它发生碰撞时,您应该只得到一个小部件/按钮。

    在下面的代码段中,我们重写了Widget类的on\u touch\u down()方法。在这里,我们检查触摸和小部件的冲突。

    如果触摸在我们的小部件中,我们在画布上创建一个正方形并返回True,表示我们已经使用了触摸,不希望它进一步传播。

    最后,如果触摸不在我们的小部件内,我们使用 超级(绘画,自我)。on\u touch\u down(触摸) 并返回结果。这允许触摸事件传播继续 通常都会发生。

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            color = self.palette.act_col
            with self.canvas:
                Color(color[0], color[1], color[2])
                sqr = Rectangle(pos=(touch.x, touch.y), size=(20, 40))
                self.contents.append(sqr)
            return True
        return super(Paint, self).on_touch_down(touch)
    

    调色板类

    3.act_col-ListProperty

    由于act_col将在类Paint中访问,因此我们将其声明为Kivy ListProperty。

    act_col = ListProperty([1, 0, 0])
    

    由于Palette类是一个BoxLayout,我们不需要嵌套的BoxLayout。因此,它被删除。

    5.on_press事件

    传递给回调的唯一参数是对象,即我们绑定到的按钮。

    def SetColor(self, btn):
        self.act_col = self.licol[int(btn.id)]
    
    def ClearContents(self, btn):
    

    MyPaintApp类

    6、构建方法

    课堂绘画(BoxLayout): 调色板=对象属性(无) 绘制=对象属性(无)

    def __init__(self, **kwargs):
        super(Painting, self).__init__(**kwargs)
        self.orientation = "vertical"
    
        self.palette = Palette()
        self.paint = Paint(palette=self.palette)
    
        self.add_widget(self.paint)
        self.add_widget(self.palette)
    

    实例

    主要的py公司

    from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.button import Button
    from kivy.graphics import Color, Rectangle
    from kivy.properties import ObjectProperty, ListProperty
    
    
    class Paint(Widget):
        palette = ObjectProperty(None)
        contents = ListProperty([])
    
        def __init__(self, palette, **kwargs):
            # make sure we aren't overriding any important functionality
            super(Paint, self).__init__(**kwargs)
            self.palette = palette
    
        def on_touch_down(self, touch):
            """
            We override the on_touch_down() method of the Widget class. Here, we check for
            collision of the touch with our widget.
    
            If the touch falls inside our widget, we create a square on the canvas and return True,
            indicating that we have consumed the touch and don't want it to propagate any further.
    
            Finally, if the touch falls outside our widget, we call the original event using super(...)
            and return the result. This allows the touch event propagation to continue as it would
            normally have occurred.
    
            :param touch:
            :return:
            """
            if self.collide_point(*touch.pos):
                color = self.palette.act_col
                with self.canvas:
                    Color(color[0], color[1], color[2])
                    sqr = Rectangle(pos=(touch.x, touch.y), size=(20, 40))
                    self.contents.append(sqr)
                return True
            return super(Paint, self).on_touch_down(touch)
    
    
    class Palette(BoxLayout):
        act_col = ListProperty([1, 0, 0])
    
        def __init__(self, **kwargs):
            super(Palette, self).__init__(**kwargs)
            self.licol = []
            self.licol.append((1, 0, 0))
            self.licol.append((0, 1, 0))
    
            self.size_hint = (1, None)
            self.height = 50
    
            for i in range(0, len(self.licol)):
                but = Button(id=str(i))
                col = self.licol[i]
                but.background_normal = ''
                but.background_color = (col[0], col[1], col[2], 1)
                but.bind(on_press=self.SetColor)
                self.add_widget(but)
    
            but = Button(text="<--", on_press=self.ClearContents)
            self.add_widget(but)
    
        # The only argument passed to the callback is the
        # object (i.e. button) which we have bound to
        def SetColor(self, btn):
            self.act_col = self.licol[int(btn.id)]
    
        # The only argument passed to the callback is the
        # object (i.e. button) which we have bound to
        def ClearContents(self, btn):
            if len(self.parent.paint.contents) != 0:
            # list.pop() - removes and returns the last item in the list
                self.parent.paint.canvas.remove(self.parent.paint.contents.pop())
    
    
    class Painting(BoxLayout):
        palette = ObjectProperty(None)
        paint = ObjectProperty(None)
    
        def __init__(self, **kwargs):
            super(Painting, self).__init__(**kwargs)
            self.orientation = "vertical"
    
            self.palette = Palette()
            self.paint = Paint(palette=self.palette)
    
            self.add_widget(self.paint)
            self.add_widget(self.palette)
    
    
    class MyPaintApp(App):
    
        def build(self):
            return Painting()
    
    
    if __name__ == '__main__':
        MyPaintApp().run()
    

    输出

    Figure 1 - App Startup enter image description here enter image description here enter image description here