python将资源管理器文件拖放到tkinter条目小部件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14267900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
python drag and drop explorer files to tkinter entry widget
提问by George Joseph
I'm fairly new to Python. I'm trying to input a file name (complete with full path) to a TKinter entry widget. Since the path to the file name can be very long I would like to be able to drag and drop the file directly from Windows Explorer. In Perl I have seen the following:
我对 Python 相当陌生。我正在尝试向 TKinter 条目小部件输入文件名(完整路径)。由于文件名的路径可能很长,我希望能够直接从 Windows 资源管理器中拖放文件。在 Perl 中,我看到了以下内容:
use Tk::DropSite;
.
.
my $mw = new MainWindow;
$top = $mw->Toplevel;
$label_entry = $top->Entry(-width => '45',. -background => 'ivory2')->pack();
$label_entry->DropSite(-dropcommand => \&drop,-droptypes => 'Win32',);
Is there something similar I can do using TKinter in Python?
我可以在 Python 中使用 TKinter 做类似的事情吗?
回答by mmgp
Tk does not have any command to handle that, and Python doesn't include any extra Tk extension to perform drag & drop inter-applications, therefore you need an extension to perform the operation. Tkdnd (the Tk extension at http://sourceforge.net/projects/tkdnd, not the Tkdnd.py module) works for me. To use it from Python, a wrapper is required. Quickly searching for one, it seems http://mail.python.org/pipermail/tkinter-discuss/2005-July/000476.htmlcontains such code. I did another one because I didn't like that other one. The problem with my wrapper is that it is highly untested, in fact I only used the function bindtargetand only for 10 seconds or so.
Tk 没有任何命令来处理这个问题,而且 Python 不包含任何额外的 Tk 扩展来执行拖放应用程序间的操作,因此您需要一个扩展来执行操作。Tkdnd(http://sourceforge.net/projects/tkdnd 上的 Tk 扩展,而不是 Tkdnd.py 模块)对我有用。要在 Python 中使用它,需要一个包装器。快速搜索一个,似乎http://mail.python.org/pipermail/tkinter-discuss/2005-July/000476.html包含这样的代码。我做了另一个,因为我不喜欢另一个。我的包装器的问题在于它未经测试,实际上我只使用了该功能bindtarget并且只使用了 10 秒左右。
With the wrapper below, you can create some widget and announce that it supports receiving dragged files. Here is one example:
使用下面的包装器,您可以创建一些小部件并宣布它支持接收拖动的文件。这是一个例子:
# The next two lines are not necessary if you installed TkDnd
# in a proper place.
import os
os.environ['TKDND_LIBRARY'] = DIRECTORYTOTHETKDNDBINARY
import Tkinter
from untested_tkdnd_wrapper import TkDND
root = Tkinter.Tk()
dnd = TkDND(root)
entry = Tkinter.Entry()
entry.pack()
def handle(event):
event.widget.insert(0, event.data)
dnd.bindtarget(entry, handle, 'text/uri-list')
root.mainloop()
And here is the code for untested_tkdnd_wrapper.py:
这是代码untested_tkdnd_wrapper.py:
import os
import Tkinter
def _load_tkdnd(master):
tkdndlib = os.environ.get('TKDND_LIBRARY')
if tkdndlib:
master.tk.eval('global auto_path; lappend auto_path {%s}' % tkdndlib)
master.tk.eval('package require tkdnd')
master._tkdnd_loaded = True
class TkDND(object):
def __init__(self, master):
if not getattr(master, '_tkdnd_loaded', False):
_load_tkdnd(master)
self.master = master
self.tk = master.tk
# Available pre-defined values for the 'dndtype' parameter:
# text/plain
# text/plain;charset=UTF-8
# text/uri-list
def bindtarget(self, window, callback, dndtype, event='<Drop>', priority=50):
cmd = self._prepare_tkdnd_func(callback)
return self.tk.call('dnd', 'bindtarget', window, dndtype, event,
cmd, priority)
def bindtarget_query(self, window, dndtype=None, event='<Drop>'):
return self.tk.call('dnd', 'bindtarget', window, dndtype, event)
def cleartarget(self, window):
self.tk.call('dnd', 'cleartarget', window)
def bindsource(self, window, callback, dndtype, priority=50):
cmd = self._prepare_tkdnd_func(callback)
self.tk.call('dnd', 'bindsource', window, dndtype, cmd, priority)
def bindsource_query(self, window, dndtype=None):
return self.tk.call('dnd', 'bindsource', window, dndtype)
def clearsource(self, window):
self.tk.call('dnd', 'clearsource', window)
def drag(self, window, actions=None, descriptions=None,
cursorwin=None, callback=None):
cmd = None
if cursorwin is not None:
if callback is not None:
cmd = self._prepare_tkdnd_func(callback)
self.tk.call('dnd', 'drag', window, actions, descriptions,
cursorwin, cmd)
_subst_format = ('%A', '%a', '%b', '%D', '%d', '%m', '%T',
'%W', '%X', '%Y', '%x', '%y')
_subst_format_str = " ".join(_subst_format)
def _prepare_tkdnd_func(self, callback):
funcid = self.master.register(callback, self._dndsubstitute)
cmd = ('%s %s' % (funcid, self._subst_format_str))
return cmd
def _dndsubstitute(self, *args):
if len(args) != len(self._subst_format):
return args
def try_int(x):
x = str(x)
try:
return int(x)
except ValueError:
return x
A, a, b, D, d, m, T, W, X, Y, x, y = args
event = Tkinter.Event()
event.action = A # Current action of the drag and drop operation.
event.action_list = a # Action list supported by the drag source.
event.mouse_button = b # Mouse button pressed during the drag and drop.
event.data = D # The data that has been dropped.
event.descr = d # The list of descriptions.
event.modifier = m # The list of modifier keyboard keys pressed.
event.dndtype = T
event.widget = self.master.nametowidget(W)
event.x_root = X # Mouse pointer x coord, relative to the root win.
event.y_root = Y
event.x = x # Mouse pointer x coord, relative to the widget.
event.y = y
event.action_list = str(event.action_list).split()
for name in ('mouse_button', 'x', 'y', 'x_root', 'y_root'):
setattr(event, name, try_int(getattr(event, name)))
return (event, )
Together with Tkdnd, you will find a tkdnd.tclprogram which is a higher level over the own C extension it provides. I didn't wrap this higher level code, but it could be more interesting to replicate it in Python than to use this lower level wrapper.
与 Tkdnd 一起,您会发现一个tkdnd.tcl程序,它比它提供的自己的 C 扩展更高级。我没有包装这个更高级别的代码,但是在 Python 中复制它可能比使用这个更低级别的包装更有趣。
回答by Gary02127
Things have progressed since this question was originally posted, and tkdnd2.8 (Tcl extensions) and TkinterDnD2 (Python wrapper for tkdnd2.8) are readily available on SourceForge.net.
自从这个问题最初发布以来,事情已经取得了进展,并且在 SourceForge.net 上可以轻松获得 tkdnd2.8(Tcl 扩展)和 TkinterDnD2(tkdnd2.8 的 Python 包装器)。
Here's minimal sample code to do exactly what you've asked.
这里有最少的示例代码,可以完全按照您的要求执行操作。
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()
You can see How to Install and Use TkDnD with Python 2.7 Tkinter on OSXfor download and installation info for both Windows and Mac on Python 2.7 and 3.6.
您可以查看如何在 OSX 上使用 Python 2.7 Tkinter 安装和使用 TkDnD,以获取适用于 Python 2.7 和 3.6 上的 Windows 和 Mac 的下载和安装信息。

