Skip to content

Instantly share code, notes, and snippets.

@tshirtman
Created November 2, 2013 19:55
Show Gist options
  • Save tshirtman/7282822 to your computer and use it in GitHub Desktop.
Save tshirtman/7282822 to your computer and use it in GitHub Desktop.
Drag'n drop example between a gridlayout and a floatlayout, using magnet for nicer animations
from kivy.app import App
from kivy.garden.magnet import Magnet
from kivy.uix.image import Image
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.clock import Clock
from os import listdir
IMAGEDIR = '/usr/share/icons/hicolor/32x32/apps/'
IMAGES = filter(
lambda x: x.endswith('.png'),
listdir(IMAGEDIR))
kv = '''
FloatLayout:
BoxLayout:
GridLayout:
id: grid_layout
cols: int(self.width / 32)
FloatLayout:
id: float_layout
'''
class DraggableImage(Magnet):
img = ObjectProperty(None, allownone=True)
app = ObjectProperty(None)
def on_img(self, *args):
self.clear_widgets()
if self.img:
Clock.schedule_once(lambda *x: self.add_widget(self.img), 0)
def on_touch_down(self, touch, *args):
if self.collide_point(*touch.pos):
touch.grab(self)
self.remove_widget(self.img)
self.app.root.add_widget(self.img)
self.center = touch.pos
self.img.center = touch.pos
return True
return super(DraggableImage, self).on_touch_down(touch, *args)
def on_touch_move(self, touch, *args):
grid_layout = self.app.root.ids.grid_layout
float_layout = self.app.root.ids.float_layout
if touch.grab_current == self:
self.img.center = touch.pos
if grid_layout.collide_point(*touch.pos):
grid_layout.remove_widget(self)
float_layout.remove_widget(self)
for i, c in enumerate(grid_layout.children):
if c.collide_point(*touch.pos):
grid_layout.add_widget(self, i - 1)
break
else:
grid_layout.add_widget(self)
else:
if self.parent == grid_layout:
grid_layout.remove_widget(self)
float_layout.add_widget(self)
self.center = touch.pos
return super(DraggableImage, self).on_touch_move(touch, *args)
def on_touch_up(self, touch, *args):
if touch.grab_current == self:
self.app.root.remove_widget(self.img)
self.add_widget(self.img)
touch.ungrab(self)
return True
return super(DraggableImage, self).on_touch_up(touch, *args)
class DnDMagnet(App):
def build(self):
self.root = Builder.load_string(kv)
for i in IMAGES:
image = Image(source=IMAGEDIR + i, size=(32, 32),
size_hint=(None, None))
draggable = DraggableImage(img=image, app=self,
size_hint=(None, None),
size=(32, 32))
self.root.ids.grid_layout.add_widget(draggable)
return self.root
if __name__ == '__main__':
DnDMagnet().run()
@balex1
Copy link

balex1 commented Sep 27, 2015

I'm using more of a vertical grid, but I think the results are much improved by changing line 61 to grid_layout.add_widget(self, i + 1)

@balex1
Copy link

balex1 commented Sep 27, 2015

Also, I changed line 60 to if c.collision_point(*touch.pos):

Then, later in the class, I define the function:

def collision_point(self, x, y):
x_left = self.center_x - (self.width / 1.5)
x_right = self.center_x + (self.width / 1.5)
y_bottom = self.center_y - (self.height / 1.5)
y_top = self.center_y + (self.height / 1.5)
return x_left <= x <= x_right and y_bottom <= y <= y_top

I think this gives much smoother transitions to the drag and drop functionality

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment