安装tkdnd库
sudo apt-get install tkdnd
安装tkinterDnD库
- 下载库,https://jaist.dl.sourceforge.net/project/tkinterdnd/TkinterDnD2/TkinterDnD2-0.3.zip
- 复制到python按钮目录的site-packages目录下,比如我的是:
cp -vr TkinterDnD2/ /home/bkd/.local/lib/python3.5/site-packages
实例代码
demo_canvas.py
# -*- coding: utf-8 -*-
import os
from TkinterDnD2 import *
try:
from Tkinter import *
except ImportError:
from tkinter import *
root = TkinterDnD.Tk()
root.withdraw()
root.title('TkinterDnD Canvas demo')
root.grid_rowconfigure(1, weight=1, minsize=250)
root.grid_columnconfigure(0, weight=1, minsize=300)
Label(root, text='Drag and drop files here:').grid(
row=0, column=0, padx=10, pady=5)
buttonbox = Frame(root)
buttonbox.grid(row=2, column=0, columnspan=2, pady=5)
Button(buttonbox, text='Quit', command=root.quit).pack(
side=LEFT, padx=5)
file_data = ('R0lGODlhGAAYAKIAANnZ2TMzM////wAAAJmZmf///////////yH5BAEAAAAALAA'
'AAAAYABgAAAPACBi63IqgC4GiyxwogaAbKLrMgSKBoBoousyBogEACIGiyxwoKgGAECI'
'4uiyCExMTOACBosuNpDoAGCI4uiyCIkREOACBosutSDoAgSI4usyCIjQAGCi63Iw0ACE'
'oOLrMgiI0ABgoutyMNAAhKDi6zIIiNAAYKLrcjDQAISg4usyCIjQAGCi63Iw0AIGiiqP'
'LIyhCA4CBosvNSAMQKKo4ujyCIjQAGCi63Iw0AIGiy81IAxCBpMu9GAMAgKPL3QgJADs'
'=')
folder_data = ('R0lGODlhGAAYAKECAAAAAPD/gP///////yH+EUNyZWF0ZWQgd2l0aCBHSU1QA'
'CH5BAEKAAIALAAAAAAYABgAAAJClI+pK+DvGINQKhCyztEavGmd5IQmYJXmhi7UC8frH'
'EL0Hdj4rO/n41v1giIgkWU8cpLK4dFJhAalvpj1is16toICADs=')
file_icon = PhotoImage(data=file_data)
folder_icon = PhotoImage(data=folder_data)
canvas = Canvas(root, name='dnd_demo_canvas', bg='white', relief='sunken',
bd=1, highlightthickness=1, takefocus=True, width=600)
canvas.grid(row=1, column=0, padx=5, pady=5, sticky='news')
# store the filename associated with each canvas item in a dictionary
canvas.filenames = {}
# store the next icon's x and y coordinates in a list
canvas.nextcoords = [50, 20]
# add a boolean flag to the canvas which can be used to disable
# files from the canvas being dropped on the canvas again
canvas.dragging = False
def add_file(filename):
icon = file_icon
if os.path.isdir(filename):
icon = folder_icon
id1 = canvas.create_image(canvas.nextcoords[0], canvas.nextcoords[1],
image=icon, anchor='n', tags=('file',))
id2 = canvas.create_text(canvas.nextcoords[0], canvas.nextcoords[1] + 30,
text=os.path.basename(filename), anchor='n',
justify='center', width=90)
def select_item(ev):
canvas.select_from(id2, 0)
canvas.select_to(id2, 'end')
canvas.tag_bind(id1, '<ButtonPress-1>', select_item)
canvas.tag_bind(id2, '<ButtonPress-1>', select_item)
canvas.filenames[id1] = filename
canvas.filenames[id2] = filename
if canvas.nextcoords[0] > 450:
canvas.nextcoords = [50, canvas.nextcoords[1] + 80]
else:
canvas.nextcoords = [canvas.nextcoords[0] + 100, canvas.nextcoords[1]]
# drop methods
def drop_enter(event):
event.widget.focus_force()
print('Entering %s' % event.widget)
return event.action
def drop_position(event):
return event.action
def drop_leave(event):
print('Leaving %s' % event.widget)
return event.action
def drop(event):
if canvas.dragging:
# the canvas itself is the drag source
return REFUSE_DROP
if event.data:
files = canvas.tk.splitlist(event.data)
for f in files:
add_file(f)
return event.action
canvas.drop_target_register(DND_FILES)
canvas.dnd_bind('<<DropEnter>>', drop_enter)
canvas.dnd_bind('<<DropPosition>>', drop_position)
canvas.dnd_bind('<<DropLeave>>', drop_leave)
canvas.dnd_bind('<<Drop>>', drop)
# drag methods
def drag_init(event):
data = ()
sel = canvas.select_item()
if sel:
# in a decent application we should check here if the mouse
# actually hit an item, but for now we will stick with this
data = (canvas.filenames[sel],)
canvas.dragging = True
return ((ASK, COPY), (DND_FILES, DND_TEXT), data)
else:
# don't start a dnd-operation when nothing is selected; the
# return "break" here is only cosmetical, return "foobar" would
# probably do the same
return 'break'
def drag_end(event):
# reset the "dragging" flag to enable drops again
canvas.dragging = False
canvas.drag_source_register(1, DND_FILES)
canvas.dnd_bind('<<DragInitCmd>>', drag_init)
canvas.dnd_bind('<<DragEndCmd>>', drag_end)
root.update_idletasks()
root.deiconify()
root.mainloop()
demo_files_and_text.py
# -*- coding: utf-8 -*-
import os
import platform
from TkinterDnD2 import *
try:
from Tkinter import *
from ScrolledText import ScrolledText
except ImportError:
from tkinter import *
from tkinter.scrolledtext import ScrolledText
root = TkinterDnD.Tk()
root.withdraw()
root.title('TkinterDnD demo')
root.grid_rowconfigure(1, weight=1, minsize=250)
root.grid_columnconfigure(0, weight=1, minsize=300)
root.grid_columnconfigure(1, weight=1, minsize=300)
def print_event_info(event):
print('\nAction:', event.action)
print('Supported actions:', event.actions)
print('Mouse button:', event.button)
print('Type codes:', event.codes)
print('Current type code:', event.code)
print('Common source types:', event.commonsourcetypes)
print('Common target types:', event.commontargettypes)
print('Data:', event.data)
print('Event name:', event.name)
print('Supported types:', event.types)
print('Modifier keys:', event.modifiers)
print('Supported source types:', event.supportedsourcetypes)
print('Operation type:', event.type)
print('Source types:', event.sourcetypes)
print('Supported target types:', event.supportedtargettypes)
print('Widget:', event.widget, '(type: %s)' % type(event.widget))
print('X:', event.x_root)
print('Y:', event.y_root, '\n')
Label(root, text='Drag and drop files here:').grid(
row=0, column=0, padx=10, pady=5)
Label(root, text='Drag and drop text here:').grid(
row=0, column=1, padx=10, pady=5)
buttonbox = Frame(root)
buttonbox.grid(row=2, column=0, columnspan=2, pady=5)
Button(buttonbox, text='Quit', command=root.quit).pack(
side=LEFT, padx=5)
##############################################################################
###### Basic demo window: a Listbox to drag & drop files ##
###### and a Text widget to drag & drop text ##
##############################################################################
listbox = Listbox(root, name='dnd_demo_listbox',
selectmode='extended', width=1, height=1)
listbox.grid(row=1, column=0, padx=5, pady=5, sticky='news')
text = Text(root, name='dnd_demo_text', wrap='word', undo=True, width=1, height=1)
text.grid(row=1, column=1, pady=5, sticky='news')
listbox.insert(END, os.path.abspath(__file__))
info = 'TkinterDnD demo\nDetected versions:\n'
info += ' Python: %s\n' % platform.python_version()
info += ' Tk : %f\n' % TkVersion
info += ' Tkdnd : %s\n' % TkinterDnD.TkdndVersion
info += 'Use mouse button 3 to drag hightlighted text from the text box.\n'
text.insert(END, info)
# Drop callbacks can be shared between the Listbox and Text;
# according to the man page these callbacks must return an action type,
# however they also seem to work without
def drop_enter(event):
event.widget.focus_force()
print('Entering widget: %s' % event.widget)
#print_event_info(event)
return event.action
def drop_position(event):
print('Position: x %d, y %d' %(event.x_root, event.y_root))
#print_event_info(event)
return event.action
def drop_leave(event):
print('Leaving %s' % event.widget)
#print_event_info(event)
return event.action
def drop(event):
if event.data:
print('Dropped data:\n', event.data)
#print_event_info(event)
if event.widget == listbox:
# event.data is a list of filenames as one string;
# if one of these filenames contains whitespace characters
# it is rather difficult to reliably tell where one filename
# ends and the next begins; the best bet appears to be
# to count on tkdnd's and tkinter's internal magic to handle
# such cases correctly; the following seems to work well
# at least with Windows and Gtk/X11
files = listbox.tk.splitlist(event.data)
for f in files:
if os.path.exists(f):
print('Dropped file: "%s"' % f)
listbox.insert('end', f)
else:
print('Not dropping file "%s": file does not exist.' % f)
elif event.widget == text:
# calculate the mouse pointer's text index
bd = text[‘bd’] + text[‘highlightthickness’]
x = event.x_root – text.winfo_rootx() – bd
y = event.y_root – text.winfo_rooty() – bd
index = text.index('@%d,%d' % (x,y))
text.insert(index, event.data)
else:
print('Error: reported event.widget not known')
return event.action
# now make the Listbox and Text drop targets
listbox.drop_target_register(DND_FILES, DND_TEXT)
text.drop_target_register(DND_TEXT)
for widget in (listbox, text):
widget.dnd_bind('<<DropEnter>>', drop_enter)
widget.dnd_bind('<<DropPosition>>', drop_position)
widget.dnd_bind('<<DropLeave>>', drop_leave)
widget.dnd_bind('<<Drop>>', drop)
#widget.dnd_bind('<<Drop:DND_Files>>', drop)
#widget.dnd_bind('<<Drop:DND_Text>>', drop)
# define drag callbacks
def drag_init_listbox(event):
print_event_info(event)
# use a tuple as file list, this should hopefully be handled gracefully
# by tkdnd and the drop targets like file managers or text editors
data = ()
if listbox.curselection():
data = tuple([listbox.get(i) for i in listbox.curselection()])
print('Dragging :', data)
# tuples can also be used to specify possible alternatives for
# action type and DnD type:
return ((ASK, COPY), (DND_FILES, DND_TEXT), data)
def drag_init_text(event):
print_event_info(event)
# use a string if there is only a single text string to be dragged
data = ''
sel = text.tag_nextrange(SEL, '1.0')
if sel:
data = text.get(*sel)
print('Dragging :\n', data)
# if there is only one possible alternative for action and DnD type
# we can also use strings here
return (COPY, DND_TEXT, data)
def drag_end(event):
#print_event_info(event)
# this callback is not really necessary if it doesn't do anything useful
print('Drag ended for widget:', event.widget)
# finally make the widgets a drag source
listbox.drag_source_register(1, DND_TEXT, DND_FILES)
text.drag_source_register(3, DND_TEXT)
listbox.dnd_bind('<<DragInitCmd>>', drag_init_listbox)
listbox.dnd_bind('<<DragEndCmd>>', drag_end)
text.dnd_bind('<<DragInitCmd>>', drag_init_text)
# skip the useless drag_end() binding for the text widget
root.update_idletasks()
root.deiconify()
root.mainloop()
demo_megawidgets.py
# -*- coding: utf-8 -*-
import os
import platform
from TkinterDnD2 import *
try:
from Tkinter import *
from ScrolledText import ScrolledText
except ImportError:
from tkinter import *
from tkinter.scrolledtext import ScrolledText
root = TkinterDnD.Tk()
root.withdraw()
root.title('TkinterDnD megawidget demo')
root.grid_rowconfigure(1, weight=1, minsize=250)
root.grid_columnconfigure(0, weight=1, minsize=300)
Label(root, text='Drop text here:').grid(
row=0, column=0, padx=10, pady=5)
buttonbox = Frame(root)
buttonbox.grid(row=2, column=0, columnspan=2, pady=5)
Button(buttonbox, text='Quit', command=root.quit).pack(
side=LEFT, padx=5)
msg = 'Dropping onto compound widgets works only with the latest versions ' +\
'of tkdnd.\nIf you can drop text here, the installed version of ' +\
'tkdnd already supports this feature.\n'
lf = LabelFrame(root, text='"Megawidget" text box')
lf.grid(row=1, column=0, padx=5, pady=5, sticky='news')
text = ScrolledText(lf)
text.pack(fill='both', expand=1)
text.insert('end', msg)
# make the text box a drop target:
def drop_enter(event):
event.widget.focus_force()
print('Entering %s' % event.widget)
return event.action
def drop_position(event):
print('Position: x %d, y %d' %(event.x_root, event.y_root))
return event.action
def drop_leave(event):
print('Leaving %s' % event.widget)
return event.action
def drop(event):
if event.data:
text.insert('end', event.data)
return event.action
text.drop_target_register(DND_TEXT)
text.dnd_bind('<<DropEnter>>', drop_enter)
text.dnd_bind('<<DropPosition>>', drop_position)
text.dnd_bind('<<DropLeave>>', drop_leave)
text.dnd_bind('<<Drop>>', drop)
root.update_idletasks()
root.deiconify()
root.mainloop()
demo_simple_text.py
# -*- coding: utf-8 -*-
import os
import platform
from TkinterDnD2 import *
try:
from Tkinter import *
from ScrolledText import ScrolledText
except ImportError:
from tkinter import *
from tkinter.scrolledtext import ScrolledText
root = TkinterDnD.Tk()
root.withdraw()
root.title('TkinterDnD simple text demo')
root.grid_rowconfigure(1, weight=1, minsize=250)
root.grid_columnconfigure(0, weight=1, minsize=300)
Label(root, text='Drag and drop text here:').grid(
row=0, column=0, padx=10, pady=5)
buttonbox = Frame(root)
buttonbox.grid(row=2, column=0, columnspan=2, pady=5)
Button(buttonbox, text='Quit', command=root.quit).pack(
side=LEFT, padx=5)
msg = "You can drop text onto the Label to append it to the Label's text "+\
"string.\nYou can drag the Label's text string into other " +\
"applications.\n"
label = Label(root, name='dnd_demo_label', bg='white', relief='sunken',
bd=1, highlightthickness=1, text=msg, takefocus=True,
justify='left', anchor='nw', padx=5, pady=5)
label.grid(row=1, column=0, padx=5, pady=5, sticky='news')
# make the Label a drop target:
def drop_enter(event):
event.widget.focus_force()
print('Entering %s' % event.widget)
return event.action
def drop_position(event):
print('Position: x %d, y %d' %(event.x_root, event.y_root))
return event.action
def drop_leave(event):
print('Leaving %s' % event.widget)
return event.action
def drop(event):
if event.data:
label.configure(text=label[‘text’] + event.data)
return event.action
label.drop_target_register(DND_TEXT)
label.dnd_bind('<<DropEnter>>', drop_enter)
label.dnd_bind('<<DropPosition>>', drop_position)
label.dnd_bind('<<DropLeave>>', drop_leave)
label.dnd_bind('<<Drop>>', drop)
# make the Label a drag source:
def drag_init(event):
data = label[‘text’]
return (COPY, DND_TEXT, data)
label.drag_source_register(DND_TEXT)
label.dnd_bind('<<DragInitCmd>>', drag_init)
root.update_idletasks()
root.deiconify()
root.mainloop()