简介
环境:Win10 x64
python 3.9
Tkinter是py3自带的库,可以实现GUI,但是要实现拖放就需要TkDND和TkinterDnD2了
在TkinterDnD2的官方上,有说明在使用前要先安装tkDND2
If you want to use TkinterDnD2 you will of course have to install tkDnD2 first. Windows binaries as well as the sources can be found here.
下载资源
由于总所周知的原因,国外服务器经常多灾多难下载慢的一批,so把下载好的资源放在这里
链接: https://pan.baidu.com/s/1Q76uLiyYbFuc_tNtbCWFqQ
提取码: kbcb
TkDND
- TkDND下载
ix86是32位,x86_64是64位的,不知道自己几位的:右键我的电脑 - 属性
下错了据说无法运行
TkinterDnD2
- TkinterDnD2下载,截至2020.10.17,上面最新版为2.8
2.9的test版本在tkdnd-release-github
安装
tkdnd2.8
tkdnd2.8-win32-xxx.tar.gz 里面是个 tkdnd2.8 文件夹, 该文件夹应该复制到 pythton/tcl/tcl8.6/ 路径, 或者/Anaconda2/envs/python35/tcl/tclx.x/ 下面,下面我下载不到2.8x64的。。就下载了2.9试验版的
TkinterDnD2
TkinterDnD2(不是TkinterDnD2-0.3哦)则是放到pyxx\Lib\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 = ('R0lGODlhGAAYAKIAANnZ2TMzMwAAAJmZmf///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_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()
目标:simple_file.py
拖放文件获取路径demo
import tkinter
from TkinterDnD2 import *
def drop(event):
entry_sv.set(event.data)
root = TkinterDnD.Tk()
entry_sv = tkinter.StringVar()
entry = tkinter.Entry(root, textvar=entry_sv, width=80)
entry.pack(fill=tkinter.X)
entry.drop_target_register(DND_FILES)
entry.dnd_bind('<<Drop>>', drop)
root.mainloop()
如果你想和我一样把这玩意打包成exe,pyinstaller了解一下