I'm attempting to set up a ListView so that
1) When selection is made, that selection is deleted from the ListView
2) A Scatter is created holding a label with the text of the selection
3) Using the same click, the Scatter is dragged across the screen
4) The Scatter is deleted once the click is released.
I did this in tkinter, and I'm trying to transition this to Kivy. For the most part, this is pretty straightforward, but I've encountered a couple of issues. The first issue I have is getting the selection of the ListView. The on_touch_down
event of the ListView gets fired before the on_selection_change
event of the ListView's adapter, so if I bind to on_touch_down
, I get what the previous selection was, not the current one. The second issue is dragging the Scatter. The goal is for the user, in 1 click, to make a selection from the ListView, have a Scatter appear and drag it across the screen, and then have the Scatter be removed when the click is released. I've tried to use touch.grab()
in the following method that was bound to the ListView's on_touch_down
def onPress(self, view, touch):
if view.collide_point(touch.x, touch.y):
self.floatLayout.add_widget(self.scatter)
touch.grab(self.scatter)
But when I click on the ListView I get a TypeError: cannot create weak reference to 'weakproxy' object
error, despite having keyScatter: keyScatter.__self__
in my .kv file, and keyScatter
is the id for self.scatter
.
Is there a good fix for either issue?
To anyone who attempts to do drag and drop from a ListView, there is good news: a solution exists. To solve this problem, I used methods bound to the on_selection_change
of the ListView's adapter, the on_touch_down
of the ListView, and the on_touch_up
of the Scatter that I was being used for the drag and drop, as in the following code block.
self.listview.bind(on_touch_down=self.press)
self.scatter.bind(on_touch_up=self.release)
self.adapter.bind(on_selection_change=self.selectionChange)
def selectionChange(self, adapter):
if adapter.selection: #Sometimes the selection was [], so a check doesn't hurt
names = adapter.data
self.scatter.children[0].text = adapter.selection[0].text #My scatter has a label as it's first and only child. Here, I'm changing the label's text
for j in adapter.data:
if j == adapter.selection[0].text:
break
names.pop(names.index(j))
self.listview.adapter.data = names
if(hasattr(self.listview, '_reset_spopulate')): #This is used to reset the ListView
self.listview._reset_spopulate()
def press(self, view, touch):
if view.collide_point(touch.x, touch.y) and not touch.is_mouse_scrolling:
self.scatter.center = touch.pos
self.floatLayout.add_widget(self.scatter) #The scatter appears on the click
self.scatter.on_touch_down(touch) #Needs to be called to get the scatter to be dragged
def release(self, scatter, touch):
if scatter.collide_point(touch.x, touch.y) and touch.grab_current: #Because Kivy's on_touch_up doesn't work like I think it does
#Do whatever you want on the release of the scatter
self.floatLayout.remove_widget(self.scatter) #Remove the scatter on release