21. Dra-och-släpp

Observera

Versioner av PyGObject < 3.0.3 innehåller ett fel som inte låter dra-och-släpp fungera korrekt. Därför krävs en version av PyGObject >= 3.0.3 för att följande exempel ska fungera.

Att konfigurera dra-och-släpp mellan komponenter består i att välja en dragkälla (komponenten som användaren startar dragningen från) med metoden Gtk.Widget.drag_source_set(), välja ett dragmål (komponenten som användaren släpper på) med metoden Gtk.Widget.drag_dest_set() och sedan hantera de relevanta signalerna för båda komponenterna.

Istället för att använda Gtk.Widget.drag_source_set() och Gtk.Widget.drag_dest_set() så kräver vissa specialiserade komponenter användningen av specifika funktioner (så som Gtk.TreeView och Gtk.IconView).

Ett enkelt dra-och-släpp kräver bara att källan ansluter till signalen ”drag-data-get” och att målet ansluter till signalen ”drag-data-received”. Mer komplexa saker som specifika släppområden och anpassade dragikoner kommer kräva att du ansluter till ytterligare signaler och interagerar med Gdk.DragContext-objektet det tillhandahåller.

För att överföra data mellan källan och målet behöver du interagera med variabeln Gtk.SelectionData som tillhandahålls i signalerna ”drag-data-get” och ”drag-data-received” med get- och set-metoderna för Gtk.SelectionData.

21.1. Målfält

För att dragningens källa och mål ska få veta vilka data de tar emot och skickar krävs en gemensam lista över Gtk.TargetEntry. Ett Gtk.TargetEntry beskriver ett datastycke som kommer sändas av dragkällan och tas emot av dragmålet.

Det finns två sätt att lägga till Gtk.TargetEntry till en källa och ett mål. Om dra-och-släppet är enkelt och varje målfält är av olika typ så kan du använda gruppen av metoder omnämnda här.

Om du behöver mer än en typ av data eller vill göra mer komplexa saker med dessa data kommer du behöva skapa ditt Gtk.TargetEntry med metoden Gtk.TargetEntry.new().

21.2. Dragkällsignaler

Namn

När den sänds ut

Vanligt syfte

drag-begin

Användaren startar en dragning

Konfigurera dragikon

drag-data-get

Då dragningsdata begärs av målet

Överför dragningsdata från källan till målet

drag-data-delete

Då en dragning med åtgärden Gdk.DragAction.MOVE slutförts

Ta bort data från källan för att slutföra ”flytten”

drag-end

Då dragningen slutförts

Gör allt som gjorts i drag-begin ogjort

21.3. Dragmålsignaler

Namn

När den sänds ut

Vanligt syfte

drag-motion

Dragikon flyttar över ett släppområde

Tillåt endast vissa områden att släppas till

drag-drop

Ikon släpps på ett dragområde

Tillåt endast vissa områden att släppas till

drag-data-received

När dragningsdata tas emot av målet

Överför dragningsdata från källan till målet

21.4. Exempel

_images/drag_and_drop_example.png
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
import gi

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

(TARGET_ENTRY_TEXT, TARGET_ENTRY_PIXBUF) = range(2)
(COLUMN_TEXT, COLUMN_PIXBUF) = range(2)

DRAG_ACTION = Gdk.DragAction.COPY


class DragDropWindow(Gtk.Window):
    def __init__(self):
        super().__init__(title="Drag and Drop Demo")

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)

        hbox = Gtk.Box(spacing=12)
        vbox.pack_start(hbox, True, True, 0)

        self.iconview = DragSourceIconView()
        self.drop_area = DropArea()

        hbox.pack_start(self.iconview, True, True, 0)
        hbox.pack_start(self.drop_area, True, True, 0)

        button_box = Gtk.Box(spacing=6)
        vbox.pack_start(button_box, True, False, 0)

        image_button = Gtk.RadioButton.new_with_label_from_widget(None, "Images")
        image_button.connect("toggled", self.add_image_targets)
        button_box.pack_start(image_button, True, False, 0)

        text_button = Gtk.RadioButton.new_with_label_from_widget(image_button, "Text")
        text_button.connect("toggled", self.add_text_targets)
        button_box.pack_start(text_button, True, False, 0)

        self.add_image_targets()

    def add_image_targets(self, button=None):
        targets = Gtk.TargetList.new([])
        targets.add_image_targets(TARGET_ENTRY_PIXBUF, True)

        self.drop_area.drag_dest_set_target_list(targets)
        self.iconview.drag_source_set_target_list(targets)

    def add_text_targets(self, button=None):
        self.drop_area.drag_dest_set_target_list(None)
        self.iconview.drag_source_set_target_list(None)

        self.drop_area.drag_dest_add_text_targets()
        self.iconview.drag_source_add_text_targets()


class DragSourceIconView(Gtk.IconView):
    def __init__(self):
        Gtk.IconView.__init__(self)
        self.set_text_column(COLUMN_TEXT)
        self.set_pixbuf_column(COLUMN_PIXBUF)

        model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
        self.set_model(model)
        self.add_item("Item 1", "image-missing")
        self.add_item("Item 2", "help-about")
        self.add_item("Item 3", "edit-copy")

        self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], DRAG_ACTION)
        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 == TARGET_ENTRY_TEXT:
            text = self.get_model().get_value(selected_iter, COLUMN_TEXT)
            data.set_text(text, -1)
        elif info == TARGET_ENTRY_PIXBUF:
            pixbuf = self.get_model().get_value(selected_iter, COLUMN_PIXBUF)
            data.set_pixbuf(pixbuf)

    def add_item(self, text, icon_name):
        pixbuf = Gtk.IconTheme.get_default().load_icon(icon_name, 16, 0)
        self.get_model().append([text, pixbuf])


class DropArea(Gtk.Label):
    def __init__(self):
        Gtk.Label.__init__(self)
        self.set_label("Drop something on me!")
        self.drag_dest_set(Gtk.DestDefaults.ALL, [], DRAG_ACTION)

        self.connect("drag-data-received", self.on_drag_data_received)

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        if info == TARGET_ENTRY_TEXT:
            text = data.get_text()
            print("Received text: %s" % text)

        elif info == TARGET_ENTRY_PIXBUF:
            pixbuf = data.get_pixbuf()
            width = pixbuf.get_width()
            height = pixbuf.get_height()

            print("Received pixbuf with width %spx and height %spx" % (width, height))


win = DragDropWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()