Golang Tk9 Drag and Drop
2025/11/26, 2025/11/26 - ~
golang で tk9 を使って GUI アプリを作れるが、ドラッグアンドドロップする方法(windows)
python であれば、tkinterdnd2 を使えば、エクスプローラーからドラッグアンドドロップできるが、golang の modernc.org/tk9.0 ではできないのだろうか?
以下のようにすれば、可能なようである。
- MSYS2 UCRT64 シェルを起動する。
- tcl 9 のソースをコンパイルする
- tk 9 のソースをコンパイルする
- tkdnd のソースをコンパイルする。library の下の tkdnd_generic.tcl を修正する。
- golang のプログラムのほうで tkdnd を読み込ませる。ドラッグアンドドロップに応答する関数を記述する。
ボタン上にドラッグアンドドロップできるサンプルを置いておく。(PC保護のダイアログが表示されるため、気になる方は、go build -ldflags="-H windowsgui" してから実行してください。)
tcl / tk のコンパイル
ソースコードをダウンロードする。
展開して、それぞれのディレクトリ下で以下のようにする。(詳細な手順は省略…)
./configure --prefix=/opt/tcl9.0.2
make
make install
./configure --prefix=/opt/tk9.0.2
make
make install
tkdnd のコンパイル
https://github.com/petasis/tkdnd を git clone しておく
./configure --with-tcl=/opt/tcl9.0.2/lib/ --with-tk=/opt/tk9.0.2/lib --prefix=/opt/tkdnd
make
make install
/opt/tcl9.0.2/lib/tkdnd2.9.5 を go アプリ下にコピーする。
コマンドプロンプトやエクスプローラーから実行する時は libwinpthread-1.dll が必要なようなので /ucrt64/lib/libwinpthread-1.dll も同じところにコピーする。
tkdnd_generic.tcl の修正
コピーした tkdnd2.9.5 フォルダ下にある、tkdnd_generic.tcl を修正する。
HandleDrop 関数の中を修正する。
$ diff -u /opt/tcl9.0.2/lib/tkdnd2.9.5/tkdnd_generic.tcl tkdnd_generic.tcl
--- /opt/tcl9.0.2/lib/tkdnd2.9.5/tkdnd_generic.tcl 2025-11-22 12:10:03.588767300 +0900
+++ tkdnd_generic.tcl 2025-11-24 10:05:07.850670000 +0900
@@ -416,6 +416,7 @@
%L \{$_typelist\} %% % \
%t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \
%c \{$_codelist\} %C \{[lindex $_codelist 0]\} \
+ %# 0 %K [join [lmap name $data { binary encode base64 [encoding convertto utf-8 $name]}] ","] %w 0 %h 0 %s 0\
] $cmd]
set _action [uplevel \#0 $cmd]
}
ここでは、Drop イベントの部分だけ修正している。
元のバージョンで modernc.org/tk9.0, tkdnd の組み合わせでうまく動かない理由を調べたところ、以下のようになっていた。
元のバージョンのままだと、以下のようなエラーになる。
eventDispatcher internal error: argv[1]="4 %# .button3 %K %w %h 21 11 1314 749 C:/Users/moriya/OneDrive/Desktop/aaa.log %s", err=newEvent: parsing event serial "%#": strconv.ParseInt: parsing "%#": invalid syntax
402 行目あたりに、
set cmd [bind $_drop_target <<Drop>>]
の行がある。ここで、tk コマンドのテンプレートが生成される。その下で %〜 に値をセットしているが、セットした文字列を modernc.org/tk9.0 に渡すと、go tk9 の tk.go newEvent 関数でうまく処理できない。
- %# がそのまま渡っているが、Int としてパースされてエラー
- %D にセットしているファイル一覧がアプリ側に渡らない
%# には 0 をセットしておく。
tkdnd は %D にファイル一覧をセットしている。しかし、bind で返ってくるパラメータは ドキュメント によると、
%D
This reports the delta value of a MouseWheel event. The delta value represents the rotation units the mouse wheel has been moved. The sign of the value represents the direction the mouse wheel was scrolled.
となっている。modernc.org/tk9.0 では、Int として解釈している。そのため、引数が渡らない。
modernc.org/tk9.0 のソースを見たところ、%K %w %h などは、そのまま文字列が入るようだ。このいずれかを利用することにした。 また、tcl/tk のファイルリストは {{ファイル名}{ファイル名}…} のような形式になっている。スペースが入ったりすると go アプリ側にうまく渡せなかった。しかたないので、ファイル名を base64 エンコードして ‘,’ で結合して渡している。
上の HandleDrop 関数の修正では、%K を使用した。go アプリ側では、Keysym フィールドを使って値を読み出す。読み出した結果は、’,’ で分割して、base64 decode して元のファイル名に戻す。
まとめ
以上のようにすれば、windows 上で drag and drop を利用することは可能なようだ。
いつか公式に使えるようになればよいのですが。ちょっとしたアプリを作るのに、tk は手軽なので。