有人能指出我在哪里可以找到有关制作列表框的信息,能够拖放项目进行重新安排吗?我发现了一些与Perl相关的东西,但我对这种语言一无所知,而且我对tkinter很新,所以这很让人困惑。我知道如何生成列表框,但我不确定如何通过拖放重新排序。
答案 0 :(得分:5)
以下是Recipe 11.4的代码:
import Tkinter
class DragDropListbox(Tkinter.Listbox):
""" A Tkinter listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = Tkinter.SINGLE
Tkinter.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
def setCurrent(self, event):
self.curIndex = self.nearest(event.y)
def shiftSelection(self, event):
i = self.nearest(event.y)
if i < self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i+1, x)
self.curIndex = i
elif i > self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i-1, x)
self.curIndex = i
答案 1 :(得分:2)
在this link处的食谱11.4显示了一个例子。
答案 2 :(得分:1)
如果您将MULTIPLE
作为selectmode
(而不是SINGLE
)处理,则此处是修改后的食谱。
所做的更改:
self.curState
位,用于跟踪点击项目的状态是否最初被选中。当你拖动它时,它不会失去它的状态。Button-1
将两个事件绑定到add='+'
事件,但只需将其全部保存在setCurrent
下即可避免。activestyle
等于'none'
。Listbox
tk.MULTIPLE
代替tk.SINGLE
。以下是代码:
class Drag_and_Drop_Listbox(tk.Listbox):
""" A tk listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = tk.MULTIPLE
kw['activestyle'] = 'none'
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.getState, add='+')
self.bind('<Button-1>', self.setCurrent, add='+')
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
self.curState = None
def setCurrent(self, event):
''' gets the current index of the clicked item in the listbox '''
self.curIndex = self.nearest(event.y)
def getState(self, event):
''' checks if the clicked item in listbox is selected '''
i = self.nearest(event.y)
self.curState = self.selection_includes(i)
def shiftSelection(self, event):
''' shifts item up or down in listbox '''
i = self.nearest(event.y)
if self.curState == 1:
self.selection_set(self.curIndex)
else:
self.selection_clear(self.curIndex)
if i < self.curIndex:
# Moves up
x = self.get(i)
selected = self.selection_includes(i)
self.delete(i)
self.insert(i+1, x)
if selected:
self.selection_set(i+1)
self.curIndex = i
elif i > self.curIndex:
# Moves down
x = self.get(i)
selected = self.selection_includes(i)
self.delete(i)
self.insert(i-1, x)
if selected:
self.selection_set(i-1)
self.curIndex = i
示例演示:
root = tk.Tk()
listbox = Drag_and_Drop_Listbox(root)
for i,name in enumerate(['name'+str(i) for i in range(10)]):
listbox.insert(tk.END, name)
if i % 2 == 0:
listbox.selection_set(i)
listbox.pack(fill=tk.BOTH, expand=True)
root.mainloop()
答案 3 :(得分:0)
下面的类是具有EXTENDED
选择模式的列表框,该列表框允许在多个选定的项目周围拖动。
B1‑Motion
事件,因此需要额外的鼠标移动才能继续滚动。在较长的列表框中感觉越野车。上面的意思是只拖动一个项目,需要先选择它,然后再次单击并拖动。
import tkinter as tk;
class ReorderableListbox(tk.Listbox):
""" A Tkinter listbox with drag & drop reordering of lines """
def __init__(self, master, **kw):
kw['selectmode'] = tk.EXTENDED
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<Control-1>', self.toggleSelection)
self.bind('<B1-Motion>', self.shiftSelection)
self.bind('<Leave>', self.onLeave)
self.bind('<Enter>', self.onEnter)
self.selectionClicked = False
self.left = False
self.unlockShifting()
self.ctrlClicked = False
def orderChangedEventHandler(self):
pass
def onLeave(self, event):
# prevents changing selection when dragging
# already selected items beyond the edge of the listbox
if self.selectionClicked:
self.left = True
return 'break'
def onEnter(self, event):
#TODO
self.left = False
def setCurrent(self, event):
self.ctrlClicked = False
i = self.nearest(event.y)
self.selectionClicked = self.selection_includes(i)
if (self.selectionClicked):
return 'break'
def toggleSelection(self, event):
self.ctrlClicked = True
def moveElement(self, source, target):
if not self.ctrlClicked:
element = self.get(source)
self.delete(source)
self.insert(target, element)
def unlockShifting(self):
self.shifting = False
def lockShifting(self):
# prevent moving processes from disturbing each other
# and prevent scrolling too fast
# when dragged to the top/bottom of visible area
self.shifting = True
def shiftSelection(self, event):
if self.ctrlClicked:
return
selection = self.curselection()
if not self.selectionClicked or len(selection) == 0:
return
selectionRange = range(min(selection), max(selection))
currentIndex = self.nearest(event.y)
if self.shifting:
return 'break'
lineHeight = 15
bottomY = self.winfo_height()
if event.y >= bottomY - lineHeight:
self.lockShifting()
self.see(self.nearest(bottomY - lineHeight) + 1)
self.master.after(500, self.unlockShifting)
if event.y <= lineHeight:
self.lockShifting()
self.see(self.nearest(lineHeight) - 1)
self.master.after(500, self.unlockShifting)
if currentIndex < min(selection):
self.lockShifting()
notInSelectionIndex = 0
for i in selectionRange[::-1]:
if not self.selection_includes(i):
self.moveElement(i, max(selection)-notInSelectionIndex)
notInSelectionIndex += 1
currentIndex = min(selection)-1
self.moveElement(currentIndex, currentIndex + len(selection))
self.orderChangedEventHandler()
elif currentIndex > max(selection):
self.lockShifting()
notInSelectionIndex = 0
for i in selectionRange:
if not self.selection_includes(i):
self.moveElement(i, min(selection)+notInSelectionIndex)
notInSelectionIndex += 1
currentIndex = max(selection)+1
self.moveElement(currentIndex, currentIndex - len(selection))
self.orderChangedEventHandler()
self.unlockShifting()
return 'break'