2013-01-10 134 views
12

私はかなりPythonを新しくしています。私はTKinterエントリウィジェットにファイル名(完全なパスを含む)を入力しようとしています。 ファイル名へのパスが非常に長くなる可能性があるので、Windowsエクスプローラから直接ファイルをドラッグ&ドロップすることができます。 Perlで私は以下を見てきました:pythonドラッグアンドドロップエクスプローラファイルを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',); 

私はPythonでTKinterを使ってできることはありますか?

+0

実際には、Tkdndモジュールがありますが、アプリケーション内でのみ動作します(つまり、アプリケーションによって作成されたウィンドウでドラッグ&ドロップ)。あなたが望むものをサポートするTkDND(http://sourceforge.net/projects/tkdnd/)と呼ばれるTkエクステンションがあり、それがPythonバインディングになっていなければ簡単にできます。 – mmgp

答えて

12

Tkにはそれを処理するコマンドがありません。Pythonには、アプリケーション間でのドロップを実行する余分なTk拡張が含まれていないので、操作を実行するには拡張が必要です。 Tkdnd(Tkの拡張子はhttp://sourceforge.net/projects/tkdndで、Tkdnd.pyモジュールではありません)は私のために働きます。 Pythonから使用するには、ラッパーが必要です。すばやく1つを検索すると、http://mail.python.org/pipermail/tkinter-discuss/2005-July/000476.htmlにそのようなコードが含まれているようです。私は他のものを好きではなかったので、私はもう1つ行った。私のラッパーの問題は、それが高度にテストされていないということです。実際には、関数bindtargetを使って10秒間しか使用しませんでした。

下記のラッパーを使用すると、ウィジェットを作成し、ドラッグされたファイルの受信をサポートすることを通知できます。ここでは一例です:

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

そして、ここで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,) 

一緒Tkdndと、あなたはそれが提供する独自のC拡張を超える高いレベルでtkdnd.tclプログラムを見つけます。私はこの上位レベルのコードをラップしませんでしたが、この下位レベルのラッパーを使うよりPythonで複製する方が面白いかもしれません。

+0

@ mmgpに感謝します!ちょうど 'tkDND'が' root'以外のマスター/親を持つウィジェットをバインドできないように見えるようにしたいと思っていました(_私のスクリプトの中のいくつかの問題を指摘しています[youtube-dl-gui -tkdnd.py](http://sdaaubckp.svn.sourceforge.net/viewvc/sdaaubckp/xtra/youtube-dl-gui-tkdnd/youtube-dl-gui-tkdnd.py?view=markup)_)。もう一度感謝します。 – sdaau

+1

@sdaau私はちょうど 'main = ttk.Frame();のようにウィジェットを作成しました。エントリ= ttk.Entry(メイン) '。そして、私は 'entry'で' bindtarget'をうまく実行し、ドロップイベントを受け入れます。問題が他の場所にないと確信していますか?失敗した最小の例を表示できますか? – mmgp

3

この質問は当初の投稿であり、tkdnd2.8(Tcl拡張機能)とTkinterDnD2(tkdnd2.8用のPythonラッパー)は、SourceForge.netですぐに利用可能です。

ここでは、質問したとおりに行うための最小限のサンプルコードを示します。

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

あなたは、Python 2.7と3.6でWindowsとMacの両方のダウンロードとインストールの情報についてはHow to Install and Use TkDnD with Python 2.7 Tkinter on OSXを見ることができます。

関連する問題