Multiple Selection + Drag and Drop in PyGTK
There's no easy way to make PyGTK's TreeView drag and drop operations work together with multiple selection. When clicking to drag, it deselects all items except for the one under the cursor.
The solution is to trap the mouse click event. If the user does not drag, the click is synthesized when the mouse is released so that clicking one item to deselect others behaves normally.
I implemented this in GTG, but here it is in isolated form, under the MIT license, so you are free to re-use it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MultiDragDropTreeView(DragDropTreeView): | |
'''TreeView that captures mouse events to make drag and drop work properly''' | |
def __init__(self): | |
super(MultiDragDropTreeView, self).__init__() | |
self.connect('button_press_event', self.on_button_press) | |
self.connect('button_release_event', self.on_button_release) | |
self.defer_select = False | |
def on_button_press(self, widget, event): | |
# Here we intercept mouse clicks on selected items so that we can | |
# drag multiple items without the click selecting only one | |
target = self.get_path_at_pos(int(event.x), int(event.y)) | |
if (target | |
and event.type == gtk.gdk.BUTTON_PRESS | |
and not (event.state & (gtk.gdk.CONTROL_MASK|gtk.gdk.SHIFT_MASK)) | |
and self.get_selection().path_is_selected(target[0])): | |
# disable selection | |
self.get_selection().set_select_function(lambda *ignore: False) | |
self.defer_select = target[0] | |
def on_button_release(self, widget, event): | |
# re-enable selection | |
self.get_selection().set_select_function(lambda *ignore: True) | |
target = self.get_path_at_pos(int(event.x), int(event.y)) | |
if (self.defer_select and target | |
and self.defer_select == target[0] | |
and not (event.x==0 and event.y==0)): # certain drag and drop | |
self.set_cursor(target[0], target[1], False) | |
self.defer_select=False |
The MultiDragDropTreeView
class, DragDropTreeView
class (that enables DnD operations) and a demo that compares them side-by-side can be downloaded below.