Kivy 从 ListView 拖放

1 投票
1 回答
2190 浏览
提问于 2025-04-18 07:33

我正在尝试设置一个列表视图(ListView),目的是:

1) 当选择某个项目时,这个项目会从列表视图中删除。

2) 创建一个散点(Scatter),里面有一个标签,显示选择的文本。

3) 用同一次点击,把这个散点拖动到屏幕上。

4) 当松开点击时,散点会被删除。

我在tkinter中实现了这个功能,现在想把它转到Kivy上。大部分过程都比较简单,但我遇到了一些问题。第一个问题是如何获取列表视图的选择项。列表视图的on_touch_down事件在列表视图适配器的on_selection_change事件之前触发,所以如果我绑定到on_touch_down,我得到的是之前的选择,而不是当前的选择。第二个问题是拖动散点。目标是用户在一次点击中,从列表视图中选择一个项目,散点出现后可以拖动到屏幕上,然后在松开点击时散点被移除。我尝试在绑定到列表视图的on_touch_down事件的方法中使用touch.grab()

def onPress(self, view, touch):
    if view.collide_point(touch.x, touch.y):
        self.floatLayout.add_widget(self.scatter)
        touch.grab(self.scatter)

但是当我点击列表视图时,出现了TypeError: cannot create weak reference to 'weakproxy' object的错误,尽管在我的.kv文件中有keyScatter: keyScatter.__self__,而keyScatterself.scatter的id。

有没有好的解决办法来处理这两个问题?

1 个回答

2

对于那些想要在列表视图(ListView)中实现拖放功能的人来说,有个好消息:解决办法已经存在。为了处理这个问题,我使用了与列表视图适配器的 on_selection_change 方法、列表视图的 on_touch_down 方法,以及我用来拖放的 Scatter 的 on_touch_up 方法,具体代码如下。

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

撰写回答