在Kivy中独立移动多个小部件

时间:2018-09-25 22:59:56

标签: python kivy

我正在尝试使用Kivy库使用python创建Ur的皇家游戏。

我想做的是创建一个棋盘(已经完成,但是还需要做更复杂的工作),并为每个玩家创建7个棋子(图)。我设法创建了全部7个,但不知道如何独立地操作它们或完全不动。

我找到了一个代码,该代码允许我使用鼠标移动对象,但是只需要移动鼠标位于其上方的对象即可。有点像只抓住需要的棋子,而不是一直吸引女王。

代码:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Ellipse
from kivy.clock import Clock
from random import random

class CircleWidget(Widget):
    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
        self.size = (50,50)
        for i in range(0, 7):
            self.circle = Ellipse(pos = self.pos, size = self.size)
            self.canvas.add(self.circle)

    # handle position change
    def on_pos(self, obj, new_pos):
        self.circle.pos = new_pos # when widget moves, so does the graphic instruction

class RootWidget(Widget):

    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
        self.cw = CircleWidget()
        self.add_widget(self.cw)

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            touch.grab(self)
            # do whatever else here

    def on_touch_move(self, touch):
        if touch.grab_current is self:
            print("This prints all the time...")
            self.cw.pos = (touch.x,touch.y)

    def on_touch_up(self, touch):
        if touch.grab_current is self:
            touch.ungrab(self)
            # and finish up here

    def update(self, dt):
        print("No idea why I need this")

class MyApp(App):
    def build(self):
        rw = RootWidget()
        # call update() every second
        Clock.schedule_interval(rw.update, 1.0)
        return rw

MyApp().run()

此外,我忘了将所有图形的位置都编辑为彼此相邻,但这并不是一项艰巨的任务。

任何帮助他们一步一步地移动吗?

2 个答案:

答案 0 :(得分:0)

在您的情况下,您将覆盖self.canvas,因此在循环之后self.canvas将仅是最后一个元素,因此您会注意到,当您移动一个元素并想移动另一个元素时,先前移动的项目就会移动。

我建议您避免进行肮脏的工作,kivy具有许多通过Behavior实现的功能,在这种情况下,正确的做法是使用DragBehavior,我的解决方案将实现在.kv中绘制的代码,因为作为一种声明性语言,该绑定很容易保存行,如on_pos和其他绑定(如drag_rectangle)。

另一方面,CircleWidget必须实现一个圆圈,而不是7个圆圈,因为圆圈的位置会有所不同,在您的情况下,您使它们看起来像一个圆圈。

解决方案:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.behaviors import DragBehavior
from kivy.clock import Clock
from kivy.lang import Builder

kv = '''
<CircleWidget>:
    size: 50, 50
    drag_rectangle: self.x, self.y, self.width, self.height
    drag_timeout: 10000000
    drag_distance: 0
    canvas:
        Ellipse:
            pos: root.pos
            size: root.size
'''

Builder.load_string(kv)

class CircleWidget(DragBehavior, Widget):
    pass

class RootWidget(Widget):
    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
        for i in range(7):
            cw = CircleWidget()
            self.add_widget(cw)

    def update(self, dt):
        print("No idea why I need this")

class MyApp(App):
    def build(self):
        rw = RootWidget()
        # call update() every second
        Clock.schedule_interval(rw.update, 1.0)
        return rw

MyApp().run()

enter image description here

答案 1 :(得分:0)

CircleWidget类

  1. 在画布上绘制一个椭圆
  2. pos size 绑定到回调 redraw 方法。
  3. 为每个 Circlewidget 实施
  4. on_touch_down , on_touch_up on_touch_move 方法。

示例

main.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Ellipse, Color
from kivy.core.window import Window
from random import randint


class CircleWidget(Widget):
    def __init__(self, **kwargs):
        super(CircleWidget, self).__init__(**kwargs)
        self.size = (50, 50)
        with self.canvas:
            Color(0, 0, 1, 0.5)
            self.circle = Ellipse(pos=self.pos, size=self.size)
        self.bind(pos=self.redraw, size=self.redraw)

    def redraw(self, *args):
        self.circle.size = self.size
        self.circle.pos = self.pos

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            # if the touch collides with our widget, let's grab it
            touch.grab(self)

            # and accept the touch.
            return True

        return super(CircleWidget, self).on_touch_down(touch)

    def on_touch_up(self, touch):
        # check if it's a grabbed touch event
        if touch.grab_current is self:
            # don't forget to ungrab ourself, or you might have side effects
            touch.ungrab(self)

            # and accept the last up
            return True

        return super(CircleWidget, self).on_touch_up(touch)

    def on_touch_move(self, touch):
        # check if it's a grabbed touch event
        if touch.grab_current is self:
            self.pos = touch.pos

            # and accept the last move
            return True

        return super(CircleWidget, self).on_touch_move(touch)


class RootWidget(Widget):

    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(**kwargs)
        for i in range(8):
            self.add_widget(CircleWidget(pos=(randint(0, Window.width - 50), randint(0, Window.height - 50))))


class MyApp(App):
    def build(self):
        return RootWidget()


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

输出

Img01 - App startup Img02 - Moved multiple widgets independently