Kivy:与事件合作

时间:2017-11-26 22:32:24

标签: python kivy

在类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 个答案:

答案 0 :(得分:1)

canvascontentsPaint类的属性,您尝试从Palate类访问它。

如果您不想更改结构,可以执行的操作是将Paint类的引用传递给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()

我认为您应该从实现画布的类中删除对象以保留封装。另一种选择是将Callback移动到Paint类并将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

答案 1 :(得分:1)

请参阅详细说明和示例。

重载方法__init __

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

我们重载方法__init __()以便添加小部件和定义   他们的行为。人们不应该忘记给超级打电话   实现原始类被重载的功能。   另请注意,最好不要忽略** kwargs   调用超级,因为它们有时在内部使用。

Paint Class

1。添加了Kivy Properties

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

from kivy.properties import ObjectProperty, ListProperty


class Paint(Widget):
    palette = ObjectProperty(None)
    contents = ListProperty([])

2。 on_touch_down事件

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

在下面的代码片段中,我们覆盖了Widget类的on_touch_down()方法。在这里,我们检查触摸与我们的小部件的碰撞。

如果触摸属于我们的小部件,我们在画布上创建一个方块并返回True,表示我们已经消耗了触摸并且不希望它进一步传播。

最后,如果触摸不在我们的小部件之外,我们使用 super(Paint,self).on_touch_down(touch)调用原始事件并返回结果。这允许触摸事件传播继续进行     通常都会发生。

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

4。嵌套BoxLayout - 已删除

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

5。 on_press事件

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

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

def ClearContents(self, btn):

MyPaintApp类

6。构建方法

我们没有将所有内容都放在MyPaintApp类的构建方法中,而是将它们移到了类Paint中。我们还声明了ObjectProperty来连接类调色板和类画。

类绘画(BoxLayout):     palette = ObjectProperty(无)     paint = ObjectProperty(无)

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)

实施例

main.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