2

I'm trying to create a Royal game of Ur with python using Kivy libs.

What I'm trying to do is to create a board (Which is done, but more complicated work is ahead) and 7 pieces (figures) for each player. I managed to create all 7, but had no idea how to manipulate them independently or move at all.

I found a code that allowed me to move an object by using mouse, but I need to only move the object that my mouse is on top of. Sort of like grabbing only the chess piece you need, and not the queen all the time.

Code:

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

Also, I forgot to edit the positions of all figures to be next to each other, but that's not a hard task.

Any help with moving them one by one?

Zackyy
  • 41
  • 5
  • Are you asking how to do drag and drop? If you are, consider making your movable pieces extend [DragBehavior](https://kivy.org/doc/stable/api-kivy.uix.behaviors.drag.html). – John Anderson Sep 25 '18 at 23:42

2 Answers2

2

class CircleWidget

  1. Draw an Ellipse on the canvas.
  2. Bind the pos and size to a callback, redraw method.
  3. Implement on_touch_down, on_touch_up, and on_touch_move methods for each Circlewidget.

Example

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

Output

Img01 - App startup Img02 - Moved multiple widgets independently

ikolim
  • 15,721
  • 2
  • 19
  • 29
0

In your case you are overwriting self.canvas, so after the loop self.canvas will only be the last element, so you notice that when you move an element and want to move another the item that moved earlier moves.

I recommend you avoid doing the dirty work, kivy has many of those functionalities implemented through Behaviors, in this case the correct thing is to use DragBehavior, in my solution I will implement the one drawn in the .kv since being a declarative language the binding is simple saving lines like on_pos and other binding like the one of drag_rectangle.

On the other hand the CircleWidget must implement a single circle, not the 7 circles since the position of the circles will be different, in your case you are making them look like one.

Solution:

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • @MartinGontšarov In your example that you point out, I do not see anything about color, what part do you mean? what color? – eyllanesc Sep 26 '18 at 14:12