L'Isola di Niente
L'Isola di Niente » PyGObject Tips » ドラッグ・アンド・ドロップ

ドラッグ・アンド・ドロップ

マウス右ボタンでファイルアイコンや選択範囲等を掴んで移動、別の場所で離す行為。
ドラッグ・アンド・ドロップでは長いので以下 DnD とします。

ファイルのドロップ

ほとんどの人が text/uri-list のドロップ以外は利用しないと思う。
ようするにファイルマネージャからのファイルドロップ。

text/uri-list を指定して GtkTargetEntry オブジェクトを作成する。
その GtkTargetEntry を Widget に drag_dest_set させる。
ターゲット Widget には gtk_drag_dest_add_uri_targets 指定が必要です。

これでドロップターゲットとしての機能追加は終わりです。
コピーや移動の振り分け等を行いたい場合は context パラメータで行う。
#!/usr/bin/env python3

from gi.repository import Gtk, Gdk

class DWin(Gtk.Window):
    """
        何かファイルをドロップすると URI を書き出す
        複数ファイルのドロップでも改行して表示するサンプル
    """
    def __init__(self):
        """
            この例は GtkWindow 全体をドロップターゲットにしています
            個別 Widget で指定することも可能です
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # DnD
        dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0)
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                [dnd_list],
                Gdk.DragAction.COPY )
        self.drag_dest_add_uri_targets()
        # URI を表示する GtkLabel
        self.label = Gtk.Label("Please drop your files")
        self.add(self.label)
        #
        self.show_all()

    def do_drag_data_received(self, context, x, y, data, info, time):
        """
            パラメータの data は GtkSelectionData
            data.get_uris() でドロップされたファイルの URI リストを得る
            Nautilus('\r\n'), Dolphin('\n') 等の改行コード違いは吸収される
        """
        uris = data.get_uris()
        self.label.set_text("\n".join(uris))

DWin()
Gtk.main()

文字列のドラッグ

DnD で検索すると自アプリ内で完結するサンプルばかりが見つかるが使い道が無い。
ココでは GtkTreeView にある文字列を別アプリにドラッグできるサンプルを以下に。

GtkTargetEntry に text/plain を指定し GtkSelectionData に文字列を突っ込むだけです。
起動して Firefox の検索バー等にドロップしてみて下さい
#!/usr/bin/env python3

from gi.repository import Gtk, Gdk

class TextDragWin(Gtk.Window):
    """
        文字列の書き込み可能部にアイテムをドラッグすれば文字列が流し込まれる
    """
    def __init__(self):
        """
            text/plain は文字列の ContentType
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # TreeView
        liststore = Gtk.ListStore.new([str])
        for s in ("CBR1000RR", "YZF-R1", "GSX-R1000", "ZX-10R"):
            liststore.append([s])
        cell = Gtk.CellRendererText.new()
        column = Gtk.TreeViewColumn("Super Sports", cell, text=0)
        treeview = Gtk.TreeView.new_with_model(liststore)
        treeview.append_column(column)
        # Drag Source
        # いつからか解らないけど使えるようになっていた
        dnd_list = Gtk.TargetEntry.new("text/plain", 0, 0)
        #dnd_list = ("text/plain", 0, 0) # もう古い
        treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [dnd_list], Gdk.DragAction.COPY)
        treeview.connect("drag-data-get", self.on_drag_data_get)
        #
        self.add(treeview)
        self.show_all()

    def on_drag_data_get(self, widget, context, data, info, time):
        """
            GtkSelectionData にデータを入れるとドロップターゲットに情報が渡る
        """
        selection = widget.get_selection()
        model, it = selection.get_selected()
        text = model.get_value(it, 0)
        data.set_text(text, -1)

TextDragWin()
Gtk.main()

OLE Drag and Drop

GTK+ のテキスト編集部分はデフォルトでアプリケーション間のドラッグアンドドロップが可能。
たとえば Gedit に何か書いて文字列選択状態で Firefox の検索バーに DnD してください。
その文字列で検索開始されたはずです。

GTK2 と GTK3 間でも、更に Qt アプリ間でも可能だったりします。
ただし gnome-terminal 等の端末エミュレータにはドロップは可能だけどドラッグはできない。

ということでコレに関しては何もする必要が無い。

組み込み機能

GtkEntry, GtkTextView では秀丸のような DnD 編集は作成した時点で可能。
又ファイルをドロップすると file:/// で始まる text/uri-list が文字列で流し込まれる。
ただ改行コード(Nautilus は CRLF)も流し込まれるので注意。

GtkTreeView は reorderable プロパティを True にするだけで並べ替え可能。
#!/usr/bin/env python3

from gi.repository import Gtk

class ReOrdreWin(Gtk.Window):
    def __init__(self):
        """
            GtkTreeView.set_reorderable(True)
            のみで DnD 並び替え可能になる
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # TreeView
        liststore = Gtk.ListStore.new([str])
        for s in ("CBR1000RR", "YZF-R1", "GSX-R1000", "ZX-10R"):
            liststore.append([s])
        cell = Gtk.CellRendererText.new()
        column = Gtk.TreeViewColumn("Super Sports", cell, text=0)
        treeview = Gtk.TreeView.new_with_model(liststore)
        treeview.append_column(column)
        # リオーダー指定
        treeview.set_reorderable(True)
        #
        self.add(treeview)
        self.show_all()

ReOrdreWin()
Gtk.main()
ということでコレに関しても特に何もする必要が無い。
GTK+ って Windows SDK 等をやっていた人から見ると「マジかよ...」ですよね。
Copyright(C) sasakima-nao All rights reserved 2002 --- 2017.