Python / Tkinter 列表框问题

2 投票
1 回答
2867 浏览
提问于 2025-04-17 13:25

我在这里发了个问题,有人建议我重构一下代码。现在代码变得有点不同,我觉得可以重新提问了。

总之,我想给一个列表框(listbox)添加拖放功能,但我觉得先让事件绑定正常工作是个不错的第一步。目前,当我点击列表框时,会出现一个错误。列表框窗口是能显示的,但一点击就出错。

 "AttributeError: make_list instance has no attribute 'nearest'.

另外,当我在build_listbox方法中打印列表框时,输出的是一个小数 .40720520L。难道不应该打印出列表框里的值吗?毕竟它是在同一个方法里。是不是列表框没有正确创建?

from Tkinter import *
import Tkinter

class make_list:

    def move_mouse(self, event):
        self.curIndex = event.nearest(event.y)
        print self.curIndex

    def click_button(self, event):
        w= event.widget
        self.curIndex = int(w.curselection()[0])
        #print self.curIndex
        value = w.get(self.curIndex)
        print value

    def build_main_window(self):
        self.build_listbox()

    def build_listbox(self):
        listbox = Listbox()
        listbox.bind('<<ListboxSelect>>', self.click_button)
        listbox.bind('<B1-Motion>', self.move_mouse)
        for item in ["one", "two", "three", "four"]:
            listbox.insert(END, item)    
        listbox.insert(END, "a list entry")
        listbox.pack()
        print listbox
        return

if __name__ == '__main__':
    start = make_list()
    start.build_main_window()
    mainloop()

1 个回答

0

当你在Tkinter的一个小部件上使用print时,得到的值对用户来说基本上没什么意义,它只是为你创建的一个Tcl变量的名字。

现在,看来你的代码是可以工作的(通过快速浏览问题中的评论可以看出来),我建议你用另一种方式来实现拖放功能。比如说,如果我们有一个和Listbox中的值相关联的值列表,那么如果我们能更新这个列表,Listbox的显示也会随之更新,那就简单多了,对吧?可惜在Python中没有这样的东西,但目前可以考虑以下代码:

import Tkinter

def list_click(event):
    w = event.widget
    index = w.nearest(event.y)
    w._selection = index

def list_motion(event):
    w = event.widget
    if w._selection is None:
        return
    index = w.curselection()[0]
    w._var.swap(index, w._selection)
    w._selection = index

def list_clear(event):
    event.widget._selection = None

root = Tkinter.Tk()

v = ListVar(values=('one', 'two', 'three', 'four'))
v.append('a list entry')

listbox = Tkinter.Listbox(listvar=v)
listbox.pack()

listbox._selection = None
listbox._var = v
listbox.bind('<1>', list_click)
listbox.bind('<B1-Motion>', list_motion)
listbox.bind('<ButtonRelease-1>', list_clear)

root.mainloop()

这段代码的工作方式是:当你在列表框中点击鼠标时,它会把最近的项目标记为“选中”,当你移动鼠标时,它会交换项目并更新选中状态,当你松开鼠标按钮时,它会清除选中状态。缺少的就是这个ListVar的东西。这里有:

class ListVar(Tkinter.Variable):
    def __init__(self, master=None, name=None, **kwargs):
        Tkinter.Variable.__init__(self, master, kwargs.get('values'), name)

    def set(self, values):
        self._tk.call('set', self._name, values)

    def set_index(self, index, value):
        self._tk.call('lset', self._name, index, value)

    def get_index(self, index):
        return self._tk.eval('lindex $%s %d' % (self._name, index))

    def get(self, start=None, end=None):
        if start is None and end is None:
            res = self._tk.eval('lrange $%s 0 end' % self._name)
        elif end is None:
            res = self._tk.eval('lrange $%s %d end' % (self._name, start))
        else:
            res = self._tk.eval('lrange $%s %d %d' % (self._name, start, end))
        return self._tk.splitlist(res)

    def swap(self, a, b):
        if a != b:
            tmp = self.get_index(a)
            self.set_index(a, self.get_index(b))
            self.set_index(b, tmp)

    def append(self, value):
        self._tk.eval('lappend %s {%s}' % (self._name, value))

这个实现很粗糙,如果你传入像“hello{”这样的值,它会失败。这个部分可以有很多改进的空间。

撰写回答