今日も見に来てくださって、ありがとうございます。石川さんです。
先日、tkinterモジュールのソースコードを眺めていたら、dnd.pyというファイルがありましてちょっと、おお、と思ったので記録しておきます。dndというからにはドラッグ・アンド・ドロップでしょ。
動作確認
ぼくは、Anacondaを使っているので、C:\ProgramData\Anaconda3\Lib\tkinterにモジュールがあって、そこにこのdnd.pyファイルがあります。ソースを見ると、最後の方にありました、テスト用に直接実行できるコードです。
if __name__ == '__main__':
test()
さっそく実行してみました。
ソースコード
ソースコードを追加したかったのですが、みなさんのフォルダにもあるはずなのと、(ぼくの環境は、C:\ProgramData\Anaconda3\Lib\tkinter\dnd.pyです。)、著作権的にも問題があると思いますので、今回はそちらをご覧ください。
解説
ソースコードは下から見ていくとよさそうですね。まずは、ルートのTkをインスタンス化してから、その後、Testerクラスを使ってウィンドウを三つ作成しています。この中にはそれぞれCanvasが作られてありますね。そして、次にIconクラスのインスタンスを三つ作って、それぞれそのウィンドウのCanvasにattachを実行する、という流れですね。attachでは、引数で受け取ったCanvasにLabelをつくって、それからマウスの第一ボタンが押されたときの処理としてpressが登録されています。
pressでは、dndモジュールの最初に定義されているdnd_startが呼び出されていて、ここでDndHandlerによってラップされたインスタンスが作成されます。DndHandlerの初期化の処理で、マウスのボタンがリリースされたときのイベントの処理にon_releaseが登録され、<Motion>イベントの処理にon_motionが登録されています。要するにマウスが動くたびにon_motionが呼び出されて、マウスのボタンを離したときにon_releaseが呼び出されるようにしました、ということですね。あ、あと、カーソルが"hand2"に変更されているように見えます。
このマウスが動くたびに呼び出されるon_motionは、処理中にターゲットのCanvasが変更されていない間はdnd_motionを呼び出すことでキャンバス内のラベルを移動させています。ターゲットのCanvasが変更されたときは、dnd_leaveとdnd_enterがそれぞれ変更前のCanvasと変更後のCanvasで呼び出されて、ドラッグ・アンド・ドロップ実現しています。
最後にマウスのボタンが離されたときに、イベントが削除されたりカーソルを元に戻したりしたあとに、dnd_commitを呼び出して終了処理をしています。
まとめ
ドラッグアンドドロップは、実装するのに、こんなに複雑なことをしないといけないのですねぇ。今回はざっと眺めただけなのでよいですけど、自分で同じ機能を実現するのは、かなり苦労しそうです。


“Python tkinter GUI プログラミング ドラッグ・アンド・ドロップ” への1件の返信