Home > Gtk3, PyGObject, python > PyGObject: Drag and Drop

PyGObject: Drag and Drop

13 Aprile 2020

torna all’indice appunti

Drag and drop

Impostare un Drag and Drop tra widgets consiste nel selezionare un Drag Source,
ovvero il widget dal quale cominceremo il Drag, selezionare un Drag Destination,
ovvero il widget sul quale eseguiremo il Drop, gestire i segnali su entrambi i widget.
In maniera molto generica, un Drag Source viene selezionato con il metodo
Gtk.Widget.drag_source_set(),
mentre il Drag Destination, viene selezionato con il metodo
Gtk.Widget.drag_dest_set().

Alcuni widget specializzati, come Gtk.TreeView and Gtk.IconView, usano metodi specifici per questa
operazione.

Un drag and drop basico richiede che il source sia connesso al segnale “drag-data-get”
e il destination sia connesso al segnale “drag-data-received”.
Se invece abbiamo bisogno di maggior complessità, come aree di drop specifiche, o icone di drag
personalizzate, sarà necessaria la connessione a segnali aggiuntivi e dovremo interagire con
l’oggetto Gdk.DragContext.

Per trasferire dati tra source e destination, dobbiamo
interagire con la variabile Gtk.SelectionData fornita nei segnali “drag-data-get”
e “drag-data-received”, usando i metodi getter e setter di Gtk.SelectionData.

Target Entries

Per permettere al drag source e al destination di conoscere quali dati stanno rispettivamente
spedendo e ricevendo, è necessaria una lista comune di Gtk.TargetEntry.
Possiamo costruirla creando direttamente una lista di istanze Gtk.TargetEntry, o creando
un oggetto Gtk.TargetList:

>>> targets = [
...     Gtk.TargetEntry.new("STRING", Gtk.TargetFlags.SAME_APP, 0),
...     Gtk.TargetEntry.new("image/png", Gtk.TargetFlags.SAME_APP, 1),
...            ]
>>> targets = Gtk.TargetList.new([
...     Gtk.TargetEntry.new("STRING", Gtk.TargetFlags.SAME_APP, 0),
...     Gtk.TargetEntry.new("image/png", Gtk.TargetFlags.SAME_APP, 1),
...            ])
>>> targets.find(Gdk.Atom.intern_static_string("STRING"))
(True, info=0)

Oppure utilizzare i metodi che mette a disposizione la classe Gtk.TargetList:
Gtk.TargetList.add_image_targets(info, writable), Gtk.TargetList.add_text_targets(info), ecc.
Una volta creata la lista targets e abilitati i widget all’operazione di drag and drop con
i metodi enable_model_drag_source(button_mask, targets, actions) ed
enable_model_drag_dest(targets, actions), non resta che associarla ai widget
source e destination, preposti al drag and drop, con i metodi drag_dest_set_target_list(targets) e
drag_source_set_target_list(targets).

>>> drag_source = Gtk.IconView()
>>> drag_source.enable_model_drag_source(start_button_mask=Gdk.ModifierType.BUTTON1_MASK, targets=[],
...             actions=Gdk.DragAction.COPY)
>>> drag_dest = Gtk.IconView()
>>> drag_dest.enable_model_drag_dest(targets=[], actions=Gdk.DragAction.COPY)
>>> drag_source.drag_source_set_target_list(targets)
>>> drag_dest.drag_dest_set_target_list(targets)

Segnali

Drag Source Signals:

Name Emesso Scopo
drag-begin Quando lo User unizia il drag Setta la icona di drag
drag-data-get Quando i dati sono richiesti dal destination Trasferisce i dati del drag da source a destination
drag-data-delete Quando un drag con la action Gdk.DragAction.MOVE è completata Elimina i dati dal source per completare il “move”
drag-end Quando il drag è completo Anunlla quello fatto in drag-begin

Drag Destination Signals:

Name Emesso Scopo
drag-motion Quando la icon Drag va sopra un’area di drop Permette di eseguire il drop solo su certe aree
drag-drop Quando la icona viene lasciata su un’area di drop Permette di eseguire il drop solo su certe aree
drag-data-received Quando i dati di drag sono ricevuti dal destination Trasferisce i dati del drag da source a destination

Ecco un codice di esempio:

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


class GWindow(Gtk.Window):
    def __init__(self):
        super().__init__(title="Drag and Drop Example")
        self.set_default_size(300, 400)

        label_source = Gtk.Label(label="Drag source")
        label_destination = Gtk.Label(label="Drop Area")
        self.drag_source = DragSourceIconView()  # Source widget
        self.drag_dest = DragDestination()  # Destination widget
        button_img = Gtk.RadioButton.new_with_label_from_widget(
            None, "Drag image only")
        button_text = Gtk.RadioButton.new_with_label_from_widget(
            button_img, "Drag text only")

        # layout
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        label_box = Gtk.Box(spacing=12)
        label_box.pack_start(label_source, True, True, 0)
        label_box.pack_start(label_destination, True, True, 0)
        dnd_box = Gtk.Box(spacing=5)
        dnd_box.pack_start(self.drag_source, True, True, 0)
        dnd_box.pack_start(self.drag_dest, True, True, 0)
        button_box = Gtk.Box(spacing=6)
        button_box.pack_start(button_img, True, False, 0)
        button_box.pack_start(button_text, True, False, 0)
        vbox.pack_start(label_box, True, False, 0)
        vbox.pack_start(dnd_box, True, False, 0)
        vbox.pack_start(button_box, True, False, 0)
        self.add(vbox)
        # bindings
        button_img.connect("toggled", self.add_image_targets)
        button_text.connect("toggled", self.add_text_targets)

        self.add_image_targets()

    def add_image_targets(self, button=None):
        targets = Gtk.TargetList.new([])
        targets.add_image_targets(info=1, writable=True)
        self.drag_dest.drag_dest_set_target_list(targets)
        self.drag_source.drag_source_set_target_list(targets)

    def add_text_targets(self, button=None):
        targets = Gtk.TargetList.new([])
        targets.add_text_targets(info=0)
        self.drag_dest.drag_dest_set_target_list(targets)
        self.drag_source.drag_source_set_target_list(targets)


class DragSourceIconView(Gtk.IconView):
    def __init__(self):
        super().__init__()
        self.set_text_column(0)
        self.set_pixbuf_column(1)

        model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
        self.set_model(model)
        for text, icon in [("Item 1", "image-missing"),
                           ("Item 2", "help-about"), ("Item 3", "edit-copy")]:
            pixbuf = Gtk.IconTheme.get_default().load_icon(icon, 32, 0)
            self.get_model().append([text, pixbuf])

        self.enable_model_drag_source(
            start_button_mask=Gdk.ModifierType.BUTTON1_MASK, targets=[],
            actions=Gdk.DragAction.COPY)
        # bindings
        self.connect("drag-data-get", self.on_drag_data_get)

    def on_drag_data_get(self, widget, drag_context, data, info, time):
        selected_path = self.get_selected_items()[0]
        selected_iter = self.get_model().get_iter(selected_path)
        if info == 0:
            text = self.get_model().get_value(selected_iter, 0)
            data.set_text(text, -1)
        elif info == 1:
            pixbuf = self.get_model().get_value(selected_iter, 1)
            data.set_pixbuf(pixbuf)


class DragDestination(Gtk.IconView):
    def __init__(self):
        super().__init__()
        self.set_text_column(0)
        self.set_pixbuf_column(1)

        model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
        self.set_model(model)
        self.enable_model_drag_dest(targets=[], actions=Gdk.DragAction.COPY)
        # bindings
        self.connect("drag-data-received", self.on_drag_data_received)

    def on_drag_data_received(self, w, context, x, y, data, info, time, *args):
        model = w.get_model()
        pixbuf = text = None
        if info == 0:
            text = data.get_text()  # None if 'only image' is toggled
        elif info == 1:
            pixbuf = data.get_pixbuf()  # None if 'only text' is toggled
        model.append([text, pixbuf])


if __name__ == "__main__":
    win = GWindow()
    win.connect("destroy", Gtk.main_quit)
    win.show_all()
    Gtk.main()

link di riferimento:

torna all’indice degli appunti
Drag and Drop

Categorie:Gtk3, PyGObject, python Tag: , ,
I commenti sono chiusi.