Drag and drop explorer files to tkinter entry widget?

前端 未结 2 1762
时光取名叫无心
时光取名叫无心 2020-11-27 15:51

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 l

相关标签:
2条回答
  • 2020-11-27 16:22

    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.

    0 讨论(0)
  • 2020-11-27 16:36

    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.

    0 讨论(0)
提交回复
热议问题