2013-01-10 18 views
12

Sono abbastanza nuovo in Python. Sto cercando di inserire un nome file (completo di percorso completo) in un widget di immissione TKinter. Poiché il percorso del nome del file può essere molto lungo, mi piacerebbe poter trascinare il file direttamente su da Esplora risorse. In Perl ho visto quanto segue:python trascina e rilascia i file di esplorazione nel widget di ingresso tkinter

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',); 

C'è qualcosa di simile che posso fare usando TKinter in Python?

+0

Non proprio, esiste il modulo 'Tkdnd' ma funziona solo all'interno dell'applicazione (ovvero, trascina e rilascia nella finestra (o nelle finestre) create dall'applicazione). Esiste un'estensione Tk che si chiama TkDND (http://sourceforge.net/projects/tkdnd/) che supporta ciò che si desidera, rendendo un binding Python per esso (se non ce n'è uno) dovrebbe essere facile. – mmgp

risposta

12

Tk non ha alcun comando per gestire questo, e Python non include alcuna estensione Tk in più per eseguire applicazioni inter-trascinamento & goccia, quindi, è necessario una proroga per eseguire l'operazione. Tkdnd (l'estensione Tk a http://sourceforge.net/projects/tkdnd, non il modulo Tkdnd.py) funziona per me. Per usarlo da Python, è richiesto un wrapper. Cercando rapidamente uno, sembra che http://mail.python.org/pipermail/tkinter-discuss/2005-July/000476.html contenga tale codice. Ne ho fatto un altro perché non mi piaceva quell'altro. Il problema con il mio wrapper è che è altamente testato, infatti ho usato solo la funzione bindtarget e solo per 10 secondi circa.

Con il wrapper sottostante, è possibile creare alcuni widget e annunciare che supporta la ricezione di file trascinati. Ecco un esempio:

# 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() 

Ed ecco il codice per 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,) 

Insieme Tkdnd, troverete un programma tkdnd.tcl che è un livello più alto sopra la propria estensione C che fornisce. Non ho avvolto questo codice di livello superiore, ma potrebbe essere più interessante replicarlo in Python piuttosto che usare questo wrapper di basso livello.

+0

Mille grazie per questo @mmgp! Volevo solo notare che 'tkDND' non sembra in grado di legare un widget che ha un master/genitore diverso da' root' (ho notato alcuni dei miei problemi nello script [youtube-dl-gui -tkdnd.py] (http://sdaaubckp.svn.sourceforge.net/viewvc/sdaaubckp/xtra/youtube-dl-gui-tkdnd/youtube-dl-gui-tkdnd.py?view=markup) _). Molte grazie ancora - grazie! – sdaau

+1

@sdaau Ho appena creato un widget come in: 'main = ttk.Frame(); entry = ttk.Entry (main) '. E eseguo 'bindtarget' bene su' entry', e accetta gli eventi drop. Sei sicuro che il problema non sia da qualche altra parte? Puoi mostrare un esempio minimo che fallisce? – mmgp

3

Le cose sono progredite da quando questa domanda è stata originariamente pubblicata, e tkdnd2.8 (estensioni Tcl) e TkinterDnD2 (wrapper Python per tkdnd2.8) sono prontamente disponibili su SourceForge.net.

Ecco un codice di esempio minimo per fare esattamente quello che hai chiesto.

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() 

Si può vedere How to Install and Use TkDnD with Python 2.7 Tkinter on OSX per il download e informazioni di installazione per Windows e Mac su Python 2.7 e 3.6.

Problemi correlati