21. Arrastar e soltar

Nota

As versões do PyGObject < 3.0.3 contêm um bug que não permite arrastar e soltar para funcionar corretamente. Portanto, uma versão do PyGObject >= 3.0.3 é necessária para os exemplos a seguir funcionarem.

Configurar arrastar e soltar entre widgets consiste em selecionar uma fonte de arrasto (o widget do qual o usuário começa a arrastar) com o método Gtk.Widget.drag_source_set(), selecionando um destino de arrasto (o widget que o usuário coloca em) com o método Gtk.Widget.drag_dest_set() e depois manipular os sinais relevantes em ambos os widgets.

Em vez de usar Gtk.Widget.drag_source_set() e Gtk.Widget.drag_dest_set() alguns widgets especializados requerem o uso de funções específicas (como Gtk.TreeView e Gtk.IconView).

Um arrastar e soltar básico requer apenas que a fonte se conecte ao sinal “drag-data-get” e que o destino se conecte ao sinal “drag-data-received”. Coisas mais complexas, como áreas de queda específicas e ícones de arrastar personalizados, exigirão que você se conecte a sinais adicionais e interaja com o objeto Gdk.DragContext que fornece.

Para transferir dados entre a origem e o destino, você deve interagir com a variável Gtk.SelectionData fornecida nos sinais “drag-data-get” e “drag-data-received” usando os métodos get e set de Gtk.SelectionData.

21.1. Entradas de alvo

Para permitir que a origem e o destino do arrastar saibam quais dados estão recebendo e enviando, uma lista comum de Gtk.TargetEntrys é necessária. A Gtk.TargetEntry descreve um dado que será enviado pela fonte de arrasto e recebido pelo destino do arrasto.

Existem duas maneiras de adicionar Gtk.TargetEntrys a uma origem e destino. Se o arrastar e soltar for simples e cada entrada de destino for de um tipo diferente, você pode usar o grupo de métodos mencionado aqui <Gtk.Widget.drag_source_add_text_targets>().

Se você precisar de mais de um tipo de dados ou quiser fazer coisas mais complexas com os dados, você precisará criar o Gtk.TargetEntrys usando o método Gtk.TargetEntry.new().

21.2. Sinais de origem do arrasto

Nome

Quando é emitido

Propósito comum

drag-begin

Usuário inicia um arrasto

Configurar ícone de arrasto

drag-data-get

Quando dados do arrasto são solicitados pelo destino

Transferir dados do arrasto da origem para o destino

drag-data-delete

Quando um arrasto com a ação Gdk.DragAction.MOVE é concluído

Excluir dados da origem para concluir o “movimento”

drag-end

Quando o arrasto estiver concluído

Desfazer qualquer coisa feita no drag-begin

21.3. Sinais de destino do arrasto

Nome

Quando é emitido

Propósito comum

drag-motion

O ícone de arrasto se move sobre uma área de soltar

Permitir que apenas algumas áreas sejam soltas

drag-drop

O ícone é solto em uma área de arrasto

Permitir que apenas algumas áreas sejam soltas

drag-data-received

Quando dados do arrasto são recebidos pelo destino

Transferir dados do arrasto da origem para o destino

21.4. Exemplo

_images/drag_and_drop_example.png
  1import gi
  2
  3gi.require_version("Gtk", "3.0")
  4from gi.repository import Gtk, Gdk, GdkPixbuf
  5
  6(TARGET_ENTRY_TEXT, TARGET_ENTRY_PIXBUF) = range(2)
  7(COLUMN_TEXT, COLUMN_PIXBUF) = range(2)
  8
  9DRAG_ACTION = Gdk.DragAction.COPY
 10
 11
 12class DragDropWindow(Gtk.Window):
 13    def __init__(self):
 14        super().__init__(title="Drag and Drop Demo")
 15
 16        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
 17        self.add(vbox)
 18
 19        hbox = Gtk.Box(spacing=12)
 20        vbox.pack_start(hbox, True, True, 0)
 21
 22        self.iconview = DragSourceIconView()
 23        self.drop_area = DropArea()
 24
 25        hbox.pack_start(self.iconview, True, True, 0)
 26        hbox.pack_start(self.drop_area, True, True, 0)
 27
 28        button_box = Gtk.Box(spacing=6)
 29        vbox.pack_start(button_box, True, False, 0)
 30
 31        image_button = Gtk.RadioButton.new_with_label_from_widget(None, "Images")
 32        image_button.connect("toggled", self.add_image_targets)
 33        button_box.pack_start(image_button, True, False, 0)
 34
 35        text_button = Gtk.RadioButton.new_with_label_from_widget(image_button, "Text")
 36        text_button.connect("toggled", self.add_text_targets)
 37        button_box.pack_start(text_button, True, False, 0)
 38
 39        self.add_image_targets()
 40
 41    def add_image_targets(self, button=None):
 42        targets = Gtk.TargetList.new([])
 43        targets.add_image_targets(TARGET_ENTRY_PIXBUF, True)
 44
 45        self.drop_area.drag_dest_set_target_list(targets)
 46        self.iconview.drag_source_set_target_list(targets)
 47
 48    def add_text_targets(self, button=None):
 49        self.drop_area.drag_dest_set_target_list(None)
 50        self.iconview.drag_source_set_target_list(None)
 51
 52        self.drop_area.drag_dest_add_text_targets()
 53        self.iconview.drag_source_add_text_targets()
 54
 55
 56class DragSourceIconView(Gtk.IconView):
 57    def __init__(self):
 58        Gtk.IconView.__init__(self)
 59        self.set_text_column(COLUMN_TEXT)
 60        self.set_pixbuf_column(COLUMN_PIXBUF)
 61
 62        model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
 63        self.set_model(model)
 64        self.add_item("Item 1", "image-missing")
 65        self.add_item("Item 2", "help-about")
 66        self.add_item("Item 3", "edit-copy")
 67
 68        self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], DRAG_ACTION)
 69        self.connect("drag-data-get", self.on_drag_data_get)
 70
 71    def on_drag_data_get(self, widget, drag_context, data, info, time):
 72        selected_path = self.get_selected_items()[0]
 73        selected_iter = self.get_model().get_iter(selected_path)
 74
 75        if info == TARGET_ENTRY_TEXT:
 76            text = self.get_model().get_value(selected_iter, COLUMN_TEXT)
 77            data.set_text(text, -1)
 78        elif info == TARGET_ENTRY_PIXBUF:
 79            pixbuf = self.get_model().get_value(selected_iter, COLUMN_PIXBUF)
 80            data.set_pixbuf(pixbuf)
 81
 82    def add_item(self, text, icon_name):
 83        pixbuf = Gtk.IconTheme.get_default().load_icon(icon_name, 16, 0)
 84        self.get_model().append([text, pixbuf])
 85
 86
 87class DropArea(Gtk.Label):
 88    def __init__(self):
 89        Gtk.Label.__init__(self)
 90        self.set_label("Drop something on me!")
 91        self.drag_dest_set(Gtk.DestDefaults.ALL, [], DRAG_ACTION)
 92
 93        self.connect("drag-data-received", self.on_drag_data_received)
 94
 95    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
 96        if info == TARGET_ENTRY_TEXT:
 97            text = data.get_text()
 98            print("Received text: %s" % text)
 99
100        elif info == TARGET_ENTRY_PIXBUF:
101            pixbuf = data.get_pixbuf()
102            width = pixbuf.get_width()
103            height = pixbuf.get_height()
104
105            print("Received pixbuf with width %spx and height %spx" % (width, height))
106
107
108win = DragDropWindow()
109win.connect("destroy", Gtk.main_quit)
110win.show_all()
111Gtk.main()