2

I have a Gtk.ListStore model filled with titles and author information, and I want the user to be able to move the elements around to put them in any order they choose. This means providing "Move Up" and "Move Down" buttons.

Unfortunately, the logic I found in this question isn't cooperating with a Gtk.TreeView that allows multiple elements to be selected.

Gtk.TreeSelection.get_selected() returns a tuple that contains a Gtk.TreeIter that points to the single, currently-selected row, but Gtk.TreeSelection.get_selected_rows() returns a tuple that contains a list of Gtk.TreePath elements.

The answer given to the single-selected-row question linked to above only works with Gtk.TreeIter objects, and I haven't been able to figure out how to convert a Gtk.TreePath into a Gtk.TreeIter object.

I did, however, find the Gtk.ListStore.move_above(iter, position) and Gtk.ListStore.move_below(iter, position) methods, but again, they require Gtk.TreeIter objects to work.

Am I missing something completely obvious to anyone else?

Community
  • 1
  • 1
Raceimaztion
  • 9,494
  • 4
  • 26
  • 41

3 Answers3

2

Gtk.ListStore.move_above(iter, position) and Gtk.ListStore.move_below(iter, position) are the correct methods to call, however, it turns out that the documentation I was reading is actually wrong relative to the version of Python 3 and the Gtk3 interface I'm using.
(apologies, I don't know how to figure out which version of the interface I'm using)

Gtk.TreeSelection.get_selected_rows() actually returns a tuple containing a Gtk.TreeModelRow and a representation of the model I was using.

Gtk.TreeModelRow contains references to both the respective Gtk.TreePath and Gtk.TreeIter objects for the given row, as referenced by the variables Gtk.TreeModelRow.path and Gtk.TreeModelRow.iter.

So, my final code for moving multiple selected rows up:

selection = treeView.get_selection()
selections, model = selection.get_selected_rows()

for row in selections:
    # Make sure the row we are given is actually selected
    if selection.iter_is_selected(row.iter) and row.previous != None:
        self.listData.move_before(row.iter, row.previous.iter)

And my code for moving multiple selected rows down:

selection = treeView.get_selection()
selections, model = selection.get_selected_rows()

# Note: Must loop through rows in the opposite direction so as not to move a row all the way to the bottom
for i in range(len(selections)-1, -1, -1):
    row = selections[i]
    # Make sure the row we are given is actually selected
    if selection.iter_is_selected(row.iter) and row.next != None:
        self.listData.move_after(row.iter, row.next.iter)
Raceimaztion
  • 9,494
  • 4
  • 26
  • 41
  • I'm new to Python and PyGtk... for some reason my TreeModelRow object `row` has no `previous` attribute. Is this only available in later versions? – codedog Jun 07 '15 at 22:35
  • I must have a more recent version somehow. The documentation I have actually doesn't list `previous` for `TreeModelRow` objects... – Raceimaztion Jun 08 '15 at 07:14
1

The accepted answer to this question uses row.previous, which I don't seem to have in my version. Here's my solution for moving rows up. It seems to work okay but I'm open to suggestions and criticism since I'm new to Python and PyGtk, and am currently on Python 2.7 and PyGtk2

@staticmethod
    def _move_item_up(list_store, list_widget):
        selection = list_widget.get_selection()
        selections, model = selection.get_selected_rows()
        for row in selections:
            previous_path = (row.path[0]-1,)
            if selection.iter_is_selected(row.iter) and previous_path[0] > -1:
                list_store.move_before(row.iter, list_store.get_iter(previous_path))
codedog
  • 2,488
  • 9
  • 38
  • 67
0

The accepted answer uses undocumented members that do not exist in all libraries. The other has a bug when you try to move up rows that are already on top: it will cycle the top one to the bottom of the selection, shifting all other selected rows up one.

Here is a working solution for both up and down.

def move_selected_items_up(treeView):
    selection = treeView.get_selection()
    model, selected_paths = selection.get_selected_rows()
    for path in selected_paths:
        index_above = path[0]-1
        if index_above < 0:
            return
        model.move_before(model.get_iter(path), model.get_iter((index_above,)))

def move_selected_items_down(treeView):
    selection = treeView.get_selection()
    model, selected_paths = selection.get_selected_rows()
    for path in reversed(selected_paths):
        index_below = path[0]+1
        if index_below >= len(model):
            return
        model.move_after(model.get_iter(path), model.get_iter((index_below,)))

Credits to @codedog's answer as the starting point. Originally modified just to fix the bug, but then simplified through better use of values returned from get_selected_rows()

vossad01
  • 11,552
  • 8
  • 56
  • 109