Paepoi

Paepoi » PyGObject Tips » Gtk(PyGObject) Tips | ドラッグ・アンド・ドロップ

Gtk(PyGObject) Tips | ドラッグ・アンド・ドロップ

# 最終更新日 2019.09.08

2019 年現在の仕様に追記と書き換え。

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

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

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib

class Win(Gtk.ApplicationWindow):
    '''
        drag_dest_add_uri_targets は GtkWidget のメソッド
        この例はウインドウ全体をターゲットにしていますが個別にもできる
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # これだけでいいです
        self.drag_dest_add_uri_targets()
        #
        self.label = Gtk.Label(label='ファイルをドロップしてね')
        self.add(self.label)
        self.show_all()

    def do_drag_data_received(self, context, x, y, data, info, time):
        '''
            data: GtkSelectionData
        '''
        uris = data.get_uris()
        l = []
        for uri in uris:
            # URI なのでフルパス名に変換
            fn = GLib.filename_from_uri(uri)[0]
            # ディレクトリ名を取り除く
            l.append(GLib.path_get_basename(fn))
        self.label.set_text('\n'.join(l))

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/drag01.png
文字列のドロップ
gnome-terminal に Gedit で文字列選択しドロップするとその選択文字列が流し込まれる。
同様に Nautilus からファイルをドロップするとそのファイルのフルパスが IFS 区切りで流し込まれる。
それと同様に振り分けを行なうには以下のように。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib

class Win(Gtk.ApplicationWindow):
    '''
        get_text メソッドでドロップされた文字列は取得できます
        ファイルのドロップと振り分けするには以下のように
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # ファイル
        self.drag_dest_add_uri_targets()
        # 文字列
        self.drag_dest_add_text_targets()
        #
        self.label = Gtk.Label(label='ファイル又は選択文字列をドロップ')
        self.add(self.label)
        self.show_all()

    def do_drag_data_received(self, context, x, y, data, info, time):
        '''
            data: GtkSelectionData
        '''
        name = data.get_data_type().name()
        # タイトルバーで確認
        self.props.title = name
        # 振り分け
        if name == 'UTF8_STRING':
            s = data.get_text()
            self.label.set_text(s)
        elif name == 'text/uri-list':
            uris = data.get_uris()
            l = []
            for uri in uris:
                fn = f"'{GLib.filename_from_uri(uri)[0]}'"
                l.append(fn)
            self.label.set_text(' '.join(l))

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/drag02.png
文字列等のドラッグ
ココでは GtkTreeView にある文字列を別アプリにドラッグできるサンプルを以下に。
GtkTargetEntry に text/plain を指定し GtkSelectionData に文字列を突っ込むだけです。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

class Win(Gtk.ApplicationWindow):
    '''
        TreeView で選択して検索ボックス等にドロップ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # TreeView
        liststore = Gtk.ListStore.new([str])
        for s in ['Suzuki V-Strom', 'Suzuki Hayabusa', 'Suzuki Katana', 'GSX-S1000']:
            liststore.append([s])
        cell = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn(title='YouTube')
        column.pack_start(cell, True)
        column.add_attribute(cell, 'text', 0)
        treeview = Gtk.TreeView(model=liststore)
        treeview.append_column(column)
        # Drag Source
        src = Gtk.TargetEntry.new('text/plain', 0, 0)
        treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [src], 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)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/drag03.png
reorderable
GtkTreeView, GtkNotebook には reorderable プロパティがある。
コレを True にするだけで DnD による並べ替えが可能になる。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib#, Gdk

class Win(Gtk.ApplicationWindow):
    '''
        TreeView
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # TreeView
        cell = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn()
        column.pack_start(cell, True)
        column.add_attribute(cell, 'text', 0)
        store = Gtk.ListStore.new([str])
        store.append(['このリストは'])
        store.append(['DnD で'])
        store.append(['並べ替えできます'])
        tree = Gtk.TreeView(model=store, headers_visible=False, reorderable=True)
        tree.append_column(column)
        # Notebook
        note = Gtk.Notebook()
        page2 = Gtk.Label(label='次のページ')
        page3 = Gtk.Label(label='最後のページ')
        note.append_page(tree, Gtk.Label(label='このタブは'))
        note.append_page(page2, Gtk.Label(label='DnD で'))
        note.append_page(page3, Gtk.Label(label='並べ替えできます'))
        # Notebook は Child Property
        note.child_set_property(tree, 'reorderable', True)
        note.child_set_property(page2, 'reorderable', True)
        note.child_set_property(page3, 'reorderable', True)
        self.add(note)
        self.show_all()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/drag04.png
組み込み機能
GtkEntry, GtkTextView では秀丸のような DnD 編集は作成した時点で可能。
更にデフォルトでアプリケーション間の DnD が可能。
GTK2 と GTK3 間でも、更に Qt アプリ間でも可能だったりします。

たとえば Gedit で文字列選択して Firefox や Chrome のタブバーにドラッグしてください。
矢印アイコンが出た所でドロップすると検索されます、筆者はよくやります。

又ファイルをドロップすると file:/// で始まる text/uri-list が文字列で流し込まれる。
ただ改行コード(Nautilus は CRLF)も流し込まれるので注意。
ちなみに Cocoa の NSTextField でもフルパスが流し込まれます。

ということでコレに関しては何もする必要が無い。
Copyright(C) sasakima-nao All rights reserved 2002 --- 2020.