2011/10/17

Processingアプリケーションでファイルのドラッグ&ドロップをサポート

秋の情報処理技術者試験も終わり、まったりと Twitter を巡回していたところ、『Processing で作成したアプリケーションウィンドウにファイルのドラッグ & ドロップ機能をサポートしたい』と考えている方を(今さらながら)発見しました。


こんにちは!Processingって、ドラッグ&ドロップを実現するコードないですよね?今後でる可能性ってあるんでしょうか… 直感的な操作を実装したい場合、D&Dは必須だと思うんです。そういう情報ってご存知ですか?2011年9月16日 10:49 via web

要するに、以下の図のように、マウスのワンジェスチャでファイルを読み込めたら便利じゃね?っていう話だと思います。


基本的には、2月7日の日記に書いた方針で実現可能なのですが、Processing の(言語的な)制約のため、当該日記のソースコードにちょっと手を加える必要があります。

2月7日の日記では、簡単のためメインクラスそのものが DropTargetListener を実装していましたが、Processing の場合はウィンドウを作成するクラス(PApplet)が隠蔽されてしまっており、(DropTargetListener に限らず)インタフェースを実装する事が事実上不可能になっているためです。

そこで、Processing 用のサンプルを超適当に作りました。早い話が手抜き更新ですが、ソースコードをコピペするだけでそれなりに動くように調整してあるのでちょっと便利です。



【サンプルの概要】

ウィンドウにファイル(複数可)をドラッグ & ドロップすると、それらのフルパスをコンソールに列挙するプログラムです。

ファイルのフルパスさえ判れば、あとは loadImage() とかが使えるので煮るなり焼くなりできますね。
てきとうなファイルを、アプリケーションのウィンドウにドラッグ & ドロップすると…
ドラッグ & ドロップされたファイルを取得できる!


【ソースコード】

今回は素直に無名クラスを作り、処理を委譲する事にしました。

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.File;
import java.io.IOException;

DropTarget dropTarget;

void setup()  {
  
  // てきとうにサイズ設定
  size(400, 300);
  
  // ==================================================
  // ファイルのドラッグ&ドロップをサポートするコード
  // ==================================================
  dropTarget = new DropTarget(this, new DropTargetListener() {
    public void dragEnter(DropTargetDragEvent dtde) {}
    public void dragOver(DropTargetDragEvent dtde) {}
    public void dropActionChanged(DropTargetDragEvent dtde) {}
    public void dragExit(DropTargetEvent dte) {}
    public void drop(DropTargetDropEvent dtde) {
      dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
      Transferable trans = dtde.getTransferable();
      List<File> fileNameList = null;
      if(trans.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
        try {
          fileNameList = (List<File>)
            trans.getTransferData(DataFlavor.javaFileListFlavor);
        } catch (UnsupportedFlavorException ex) {
          /* 例外処理 */
        } catch (IOException ex) {
          /* 例外処理 */
        }
      }
      if(fileNameList == null) return;
      // ドラッグ&ドロップされたファイルの一覧は一時リストに格納される
      // 以下のように書くと、ファイルのフルパスを表示
      for(File f : fileNameList) println(f.getAbsolutePath());
    }
  });
  // ==================================================
  
}

void draw() {
  /* 省略 */
}



【おまけ】

このサンプルでは、一時リストにファイルを格納する仕様なので、永続的にファイル情報を保持したい場合は、別途そのような仕組みを用意する必要があります。

ただし、その場合はマルチスレッド環境で正常に動作するよう、それなりに注意する必要があるでしょう。

なぜなら、draw() メソッドと DropTargetListener#drop()共有資源に対する同時アクセスが発生する可能性が出てくるためです。

多少の留意事項はありますが、ドラッグ & ドロップのインタラクションは意外と簡単に実装できる事がお解り頂けたかと思います。

ではでは。

0 件のコメント:

コメントを投稿

ひとことどうぞφ(・ω・,,)