21.3. Methods

21.3.1. Setting up the source widget

The method drag_source_set() specifies a set of target types for a drag operation on a widget.
 
widget.drag_source_set(start_button_mask, targets, actions )

The parameters signify the following:

The targets parameter is a list of tuples each similar to:
 
(target, flags, info)

target specifies a string representing the drag type.

flags restrict the drag application. flags can be set to 0 or the following flags:
 
TARGET_SAME_APP    # Target will only be selected for drags within a single application. 

TARGET_SAME_WIDGET # Target will only be selected for drags within a single widget. 

info is an application assigned integer identifier.

If a widget is no longer required to act as a source for drag-and-drop operations, the method drag_source_unset() can be used to remove a set of drag-and-drop target types.
 
widget.drag_source_unset()

21.3.2. Signals on the source widget:

The source widget is sent the following signals during a drag-and-drop operation.

Table 21-1. Source widget signals

drag_begin def drag_begin_cb(widget, drag_context, data):
drag_motion def drag_motion_cb(widget, drag_context, x, y, time, data):
drag_data_get def drag_data_get_cb(widget, drag_context, selection_data, info, time, data):
drag_data_delete def drag_data_delete_cb(widget, drag_context, data):
drag_drop def drag_drop_cb(widget, drag_context, x, y, time, data):
drag_end def drag_end_cb(widget, drag_context, data):

21.3.3. Setting up a destination widget:

drag_dest_set() specifies that this widget can receive drops and specifies what types of drops it can receive.

drag_dest_unset() specifies that the widget can no longer receive drops.
 
widget.drag_dest_set(flags, targets, actions)

widget.drag_dest_unset()

flags specifies what actions GTK should take on behalf of widget for drops on  it. The possible values of flags are:
 
DEST_DEFAULT_MOTION          If set for a widget, GTK, during a drag over this
                             widget will check if the drag matches this widget's list
                             of possible targets and actions. GTK will then call
                             drag_status() as appropriate.
DEST_DEFAULT_HIGHLIGHT       If set for a widget, GTK will draw a highlight on this
                             widget as long as a drag is over this widget and the
                             widget drag format and action is acceptable.
DEST_DEFAULT_DROP            If set for a widget, when a drop occurs, GTK will
                             check if the drag matches this widget's list of possible
                             targets and actions. If so, GTK will call
                             drag_data_get() on behalf of the widget. Whether
                             or not the drop is succesful, GTK will call
                             drag_finish(). If the action was a move, then if the
                             drag was succesful, then TRUE will be passed for the
                             delete parameter to drag_finish().
DEST_DEFAULT_ALL             If set, specifies that all default actions should be taken.

actions is a bitmask of possible actions for a drag onto this widget. The possible values (defined in GDK.py) that can be or'd for actions are:
 
ACTION_DEFAULT
ACTION_COPY
ACTION_MOVE
ACTION_LINK
ACTION_PRIVATE
ACTION_ASK

21.3.4. Signals on the destination widget:

The destination widget is sent the following signals during a drag-and-drop operation.

Table 21-2. Destination widget signals

drag_data_received def drag_data_received_cb(widget, drag_context, x, y, selection_data, info, time, data):

The dragndrop.py example program demonstrates the use of drag and drop in one application. A button with a xpm pixmap is the source for the drag; it provides both text and xpm data. A layout widget is the destination for the xpm drop while a button is the destination for the text drop. Figure 21.1 illustrates the program display after an xpm drop has been made on the layout and a text drop has been made on the button:

Figure 21.1 Drag and Drop Example

The dragndrop.py source code is:
 
    1   #!/usr/bin/env python
    2   
    3   # example dragndrop.py
    4   
    5   import gtk
    6   import GDK
    7   import string, time
    8   
    9   import gtkxpm
   10   
   11   class DragNDropExample:
   12       HEIGHT = 600
   13       WIDTH = 600
   14       TARGET_TYPE_TEXT = 80
   15       TARGET_TYPE_PIXMAP = 81
   16       fromImage = [ ( "text/plain", 0, TARGET_TYPE_TEXT ),
   17                 ( "image/x-xpixmap", 0, TARGET_TYPE_PIXMAP ) ]
   18       toButton = [ ( "text/plain", 0, TARGET_TYPE_TEXT ) ]
   19       toCanvas = [ ( "image/x-xpixmap", 0, TARGET_TYPE_PIXMAP ) ]
   20   
   21       def layout_resize(self, widget, event):
   22           x, y, width, height = widget.get_allocation()
   23           if width > self.lwidth or height > self.lheight:
   24               self.lwidth = max(width, self.lwidth)
   25               self.lheight = max(height, self.lheight)
   26               widget.set_size(self.lwidth, self.lheight)
   27   
   28       def makeLayout(self):
   29           self.lwidth = self.WIDTH
   30           self.lheight = self.HEIGHT
   31           box = gtk.GtkVBox(gtk.FALSE,0)
   32           box.show()
   33           table = gtk.GtkTable(2, 2, gtk.FALSE)
   34           table.show()
   35           box.pack_start(table, gtk.TRUE, gtk.TRUE, 0)
   36           layout = gtk.GtkLayout()
   37           self.layout = layout
   38           layout.set_size(self.lwidth, self.lheight)
   39           layout.connect("size_allocate", self.layout_resize)
   40           layout.show()
   41           table.attach(layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
   42                        gtk.FILL|gtk.EXPAND, 0, 0)
   43           # create the scrollbars and pack into the table
   44           vScrollbar = gtk.GtkVScrollbar(None)
   45           vScrollbar.show()
   46           table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,
   47                        gtk.FILL|gtk.SHRINK, 0, 0)
   48           hScrollbar = gtk.GtkHScrollbar(None)
   49           hScrollbar.show()
   50           table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,
   51                        gtk.FILL|gtk.SHRINK,
   52                        0, 0)      
   53           # tell the scrollbars to use the layout widget's adjustments
   54           vAdjust = layout.get_vadjustment()
   55           vScrollbar.set_adjustment(vAdjust)
   56           hAdjust = layout.get_hadjustment()
   57           hScrollbar.set_adjustment(hAdjust)
   58           layout.connect("drag_data_received", self.receiveCallback)
   59           layout.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
   60                                     gtk.DEST_DEFAULT_HIGHLIGHT |
   61                                     gtk.DEST_DEFAULT_DROP,
   62                                     self.toCanvas, GDK.ACTION_COPY)
   63           self.addImage(gtkxpm.gtk_xpm, 0, 0)
   64           button = gtk.GtkButton("Text Target")
   65           button.show()
   66           button.connect("drag_data_received", self.receiveCallback)
   67           button.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
   68                                gtk.DEST_DEFAULT_HIGHLIGHT |
   69                                gtk.DEST_DEFAULT_DROP,
   70                                self.toButton, GDK.ACTION_COPY)
   71           box.pack_start(button, gtk.FALSE, gtk.FALSE, 0)
   72           return box
   73   
   74       def addImage(self, xpm, xd, yd):
   75           hadj = self.layout.get_hadjustment()
   76           vadj = self.layout.get_vadjustment()
   77           style = self.window.get_style()
   78           pixmap, mask = gtk.create_pixmap_from_xpm_d(
   79               self.window.get_window(), style.bg[gtk.STATE_NORMAL], xpm)
   80           pixmapWidget = gtk.GtkPixmap(pixmap, mask)
   81           button = gtk.GtkButton()
   82           button.add(pixmapWidget)
   83           width, height = map(string.atoi, string.split(xpm[0])[:2])
   84           button.connect("drag_data_get", self.sendCallback)
   85           button.drag_source_set(GDK.BUTTON1_MASK, self.fromImage,
   86                                  GDK.ACTION_COPY)
   87           button.show_all()
   88           # have to adjust for the scrolling of the layout - event location
   89           # is relative to the viewable not the layout size
   90           self.layout.put(button, int(xd+hadj.value), int(yd+vadj.value))
   91           return
   92   
   93       def sendCallback(self, widget, context, selection, targetType, eventTime):
   94           if targetType == self.TARGET_TYPE_TEXT:
   95               now = time.time()
   96               str = time.ctime(now)
   97               selection.set(selection.target, 8, str)
   98           elif targetType == self.TARGET_TYPE_PIXMAP:
   99               selection.set(selection.target, 8,
  100                             string.join(gtkxpm.gtk_xpm, '\n'))
  101   
  102       def receiveCallback(self, widget, context, x, y, selection, targetType,
  103                           time):
  104           if targetType == self.TARGET_TYPE_TEXT:
  105               label = widget.children()[0]
  106               label.set_text(selection.data)
  107           elif targetType == self.TARGET_TYPE_PIXMAP:
  108               self.addImage(string.split(selection.data, '\n'), x, y)
  109   
  110       def __init__(self):
  111           self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
  112           self.window.set_default_size(300, 300)
  113           self.window.connect("destroy", gtk.mainquit)
  114           self.window.show()
  115           layout = self.makeLayout()
  116           self.window.add(layout)
  117   
  118   def main():
  119       gtk.mainloop()
  120   
  121   if __name__ == "__main__":
  122       DragNDropExample()
  123       main()