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:
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?
The tkinter. dnd module provides drag-and-drop support for objects within a single application, within the same window or between windows. To enable an object to be dragged, you must create an event binding for it that starts the drag-and-drop process.
However, the simplest way to enable dragging from a widget is to reimplement the widget's mousePressEvent() and start a drag and drop operation: def mousePressEvent(self, event): if (event. button() == Qt. LeftButton and iconLabel.
The Entry widget is used to accept single-line text strings from a user. If you want to display multiple lines of text that can be edited, then you should use the Text widget. If you want to display one or more lines of text that cannot be modified by the user, then you should use the Label widget.
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.html contains 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 bindtarget
and only for 10 seconds or so.
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
:
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.tcl
program 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.
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.
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 OSX for download and installation info for both Windows and Mac on Python 2.7 and 3.6.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With