用Tkinter打造GUI开发工具(47)在Tkinter中实现文件和目录的拖拽功能
Python Tkinter是免费开源的,因此有许多程序员在上面开发了很多新功能模块.
tkdnd是tk扩展的Python包装器。tkdnd扩展提供了本机特定于平台的拖放机制的接口。在Unix下,使用的拖放协议是XDND协议版本5(也由Qt工具箱以及KDE和GNOME桌面使用)。在Windows下,使用OLE2拖放界面。在Macintosh下,使用Cocoa拖放界面。
一旦安装了TkinterDnD2软件包,就可以安全地进行操作:
from TkinterDnD2 import *
这将添加TkinterDnD类。Tk 和TkinterDnD。TixTk 到全局名称空间,再加上以下常量:
PRIVATE,NONE,ASK,COPY,MOVE,LINK,REFUSE_DROP,
DND_TEXT,DND_FILES,DND_ALL,CF_UNICODETEXT,CF_TEXT,CF_HDROP,
FileGroupDescriptor,FileGroupDescriptorW拖放为应用程序然后可以通过使用中的一个被使能
的类TkinterDnD.Tk()或(如果应使用tix 扩展名)TkinterDnD.TixTk()作为应用程序主窗口,而不是常规的tkinter.Tk()窗口。这会将拖放特定的方法添加到“Tk” 窗口及其所有后代中。
上面是对原作者介绍的翻译,简单说,用root=TkinterDnD.Tk()来代替root=tkinter.Tk()创建应用程序主窗口,在这个应用程序中使用tkinter或者ttk的控件。如果要用tix,则需要用root=TkinterDnD.TixTk()来代替root=tix.Tk()。如果读者不明白可以看我出版的书《零基础搭建量化投资系统――以Python为工具》的第8章 Tkinter模块。https://item.jd.com/61567375505.html
第8章 Tkinter模块 245
8.1 Tkinter的使用 245
8.2 Tkinter控件的属性 250
8.3 Tkinter主窗口 260
8.4 Toplevel顶层子窗口 263
8.5 创建窗口菜单条 264
8.6 创建弹出菜单 266
8.7 控件的几何布局管理方法 269
8.8 Tkinter常用控件 274
8.9 Tkinter的事件和绑定 299
8.10 Ttk控件 304
8.11 Tix控件 312
安装TkinterDnD2分两步。
一、下载安装tkdnd2.8
下载网址如下:
https://sourceforge.net/projects/tkdnd/files/Windows%20Binaries/
下载解包后,复制文件夹tkdnd2.8,放在Anaconda/tcl/tcl8.6或Python3.8/tcl/tcl8.6中。
二、下载安装TkinterDnD2
作者网页http://tkinterdnd.sourceforge.net/
下载解包后,复制TkinterDnD2位置如下
Anaconda3/Lib/site-packages/TkinterDnD2
或Python3.8/Lib/site-packages/TkinterDnD2。
以上两步安装完成后就可以使用tkinter中文件拖拽功能了。
如果原网址不好下载,也可以到我的网盘tkdnd目录中下载。
https://pan.baidu.com/s/1jxSaB8JzOu6hNvFipqfGzQ
下面我做了一个简单的演示程序,根据用户拖拽的文件类型,例如doc或py文件,以及目录,显示出不同的图标。
读者不难根据程序示例,用Tkinter做类似windows的文件目录显示和管理器。下面直接给出全部源代码。
# -*- coding: utf-8 -*-
import os
from TkinterDnD2 import *
from tkinter import *
import PIL
#独狼荷蒲qq:2886002
#通通小白python量化群:524949939
#微信公众号:独狼股票分析
root = TkinterDnD.Tk()
root.withdraw()
root.title('TkinterDnD2 拖拽演示')
root.grid_rowconfigure(1, weight=1, minsize=250)
root.grid_columnconfigure(0, weight=1, minsize=300)
Label(root, text='拖拽文件或目录到这里:').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)
folder_icon= PhotoImage(file="ico/folder.png")
word_icon= PhotoImage(file="ico/word.png")
py_icon= PhotoImage(file="ico/py.png")
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
file2,type2=os.path.splitext(filename)
if os.path.isdir(filename):
icon = folder_icon
elif type2=='.doc' or type2=='.docx':
icon = word_icon
elif type2=='.py' or type2=='.PY':
icon = py_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()
程序运行结果如下:
欢迎继续关注我的博客。