Search code examples
kivydragandroid-relativelayout

KIVY DragBehavior Custom Widget


I am trying to use DragBehavior to help in moving my custom widget across RelativeLayout. Find below sample code. Why my widget is not moving on Drag action please. For simplicity I had included only rectangle in my custom widget MyPaintWidget

from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.graphics import Color, Rectangle

Builder.load_string("""
<MyPaintWidget>:
    # Define the properties for the DragLabel
    drag_rectangle: self.x, self.y, self.width, self.height
    drag_timeout: 10000000
    drag_distance: 0
""")


class MyPaintWidget(DragBehavior, Scatter):

    def __init__(self, **kwargs) :
        self.selected = None
        self.touched = False
        super(MyPaintWidget, self).__init__(**kwargs)

    def create_figure(self,  **kwargs):
        print ('position is {}'.format(self.pos))
        print ('width Height {}'.format(self.to_parent(self.width, self.height)))
        self.canvas.add(Rectangle(pos  = self.pos, size = self.size))
        return self

    def on_touch_move(self, touch):
        print('Started to move x: {} y: {}'.format(touch.x, touch.y))
        return super(MyPaintWidget, self).on_touch_move(touch)


class MyPaintApp(App):

    def build(self):
        parent = RelativeLayout()
        self.painter = MyPaintWidget(pos_hint={"center_x": 0.5, 'center_y':0.5}, size_hint=(.2,.1))

        parent.add_widget(self.painter.create_figure())
        return parent

if __name__ == '__main__':
    MyPaintApp().run()

Solution

  • The DragBehavior works by adjusting the pos of your MyPaintWidget, but you have set pos_hint on the MyPaintWidget. The pos_hint takes precedence over pos, so while the drag changes pos, it is ignored because there is a pos_hint. Also, the Rectangle that you draw in create_figure has its size and pos set when that method is called, and there is no mechanism to change it when the MyPaintWidget is moved. So, even if the Widget was being dragged, the Rectangle would not move.

    Here is a version of your code with those problems corrected:

    from kivy.app import App
    from kivy.uix.scatter import Scatter
    from kivy.uix.relativelayout import RelativeLayout
    from kivy.uix.behaviors import DragBehavior
    from kivy.lang import Builder
    
    Builder.load_string("""
    <MyPaintWidget>:
        # Define the properties for the DragLabel
        drag_rectangle: self.x, self.y, self.width, self.height
        drag_timeout: 10000000
        drag_distance: 0
        canvas:
            Color:
                rgba: 1,0,0,1
            Rectangle:
                pos: 0,0  # only do this for RelativeLayout
                size: self.size
    """)
    
    
    class MyPaintWidget(DragBehavior, Scatter):
    
        def __init__(self, **kwargs) :
            self.selected = None
            self.touched = False
            super(MyPaintWidget, self).__init__(**kwargs)
    
        def on_touch_move(self, touch):
            print('Started to move x: {} y: {}'.format(touch.x, touch.y))
            return super(MyPaintWidget, self).on_touch_move(touch)
    
    
    class MyPaintApp(App):
    
        def build(self):
            parent = RelativeLayout()
            self.painter = MyPaintWidget( pos=(240, 200), size_hint=(.2,.1))
    
            parent.add_widget(self.painter)
            return parent
    
    if __name__ == '__main__':
        MyPaintApp().run()