Michal Czarski
Michal Czarski

Reputation: 11

How to set area where user can drag items with Kivy DragBehavior?

I'm new to Kivy and I'm looking for a way to drag Images only inside choosen Layout, not around all the window. I tried to manipulate with drag_rectangle option in kv file, but this not ended with expected behavior. Is there any way to set area when user could drag items and if it exists, how it could be done?

Thanks for your reply.

Here is my code:

from kivy.config import Config

Config.set('input', 'mouse', 'mouse,disable_multitouch')

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image


class DragLabel(DragBehavior, Image):
    pass


class Pattern(FloatLayout):

    def create_pattern(self):
        A = DragLabel(source="some.png", pos=(0, 0))
        self.add_widget(A)

class MainScreen(BoxLayout):
    pass


class AberationsApp(App):

    def build(self):
        return MainScreen()


window = AberationsApp()
window.run()

My kv file:

<DragLabel>:
    drag_rectangle: self.x/10, self.y/10, root.width, root.height
    drag_timeout: 10000000
    drag_distance: 0
    on_touch_move: print(self.x, self.y, self.size)


<Pattern>:


<MainScreen>:
    size_hint: 1, 1
    Pattern:
        size_hint: .8, 1
        id: Created_Pattern
    Button:
        size_hint: .2, 1
        text:"Load_points!"
        on_press: print(self.size, root.size)
        on_release: Created_Pattern.create_pattern()

Upvotes: 0

Views: 289

Answers (1)

Michal Czarski
Michal Czarski

Reputation: 11

Ok, I solved this.

I had to understand how DragBehavior works. Apparently, there is documentation on Kivy website, when you can find every possible interaction with draggable items. (https://kivy.org/doc/stable/_modules/kivy/uix/behaviors/drag.html)

Function on_touch_move(self, touch) solved my problem.

Additonaly I used some methods from Layouts in order to make sure, that my Images will change their position proportionally to size of my window.

Here is my code:

from kivy.config import Config

Config.set('input', 'mouse', 'mouse,disable_multitouch')

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
from kivy.metrics import sp


class DragLabel(DragBehavior, Image):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.previous_size = [1, 1]

    def do_layout(self, *args):
        self.x = self.x * self.width / self.previous_size[0]
        self.y = self.x * self.height / self.previous_size[1]
        if self.x > self.width:
            self.x = .85 * self.width
        if self.y > self.height:
            self.y = .85 * self.height
        self.previous_size = [self.width, self.height]

    def on_size(self, *args):
        self.do_layout()

    def on_touch_move(self, touch):
        if self._get_uid('svavoid') in touch.ud or \
            self._drag_touch is not touch:
            return super(DragBehavior, self).on_touch_move(touch) or \
               self._get_uid() in touch.ud
        if touch.grab_current is not self:
            return True

        uid = self._get_uid()
        ud = touch.ud[uid]
        mode = ud['mode']
        if mode == 'unknown':
            ud['dx'] += abs(touch.dx)
            ud['dy'] += abs(touch.dy)
            if ud['dx'] > sp(self.drag_distance):
                mode = 'drag'
            if ud['dy'] > sp(self.drag_distance):
                mode = 'drag'
            ud['mode'] = mode
        if mode == 'drag':
            previous_x_position = self.x
            previous_y_position = self.y
            new_x_position = previous_x_position + touch.dx
            new_y_position = previous_y_position + touch.dy
            if .85 * self.size[0] >= new_x_position >= 0 and .85 * self.size[1] >= new_y_position >= 0:
                self.x = new_x_position
                self.y = new_y_position
        return True


class Pattern(FloatLayout):

    def create_pattern(self):
        A = DragLabel(source="some.png", pos=(0, 0))
        self.add_widget(A)


class MainScreen(BoxLayout):
    pass


class AberationsApp(App):

    def build(self):
        return MainScreen()


window = AberationsApp()
window.run()

And kv file:

<DragLabel>:
    drag_rectangle: self.x, self.y, root.width, root.height

<Pattern>:

<MainScreen>:
    size_hint: 1, 1
    Pattern:
        size_hint: .8, 1
        id: Created_Pattern
    Button:
        size_hint: .2, 1
        text:"Load points!"
        on_release: Created_Pattern.create_pattern()

Upvotes: 1

Related Questions