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.TargetEntry
s é 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.TargetEntry
s 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.TargetEntry
s 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
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()