After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 656891 - Add support for __setitem__ to TreeModel and support for slices to TreeModelRow
Add support for __setitem__ to TreeModel and support for slices to TreeModelRow
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: introspection
Git master
Other Linux
: Normal enhancement
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2011-08-19 10:33 UTC by Sebastian Pölsterl
Modified: 2011-08-24 16:15 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Adds overrides and test cases (5.27 KB, patch)
2011-08-19 10:34 UTC, Sebastian Pölsterl
needs-work Details | Review

Description Sebastian Pölsterl 2011-08-19 10:33:56 UTC
Assigning rows like the following worked in pygtk as well:

model = Gtk.ListStore(int, str)
...
model[0] = (132, "something")

Setting and retrieving slices from a row was not possible with pygtk, though.
With this feature you can do things like

print model[0][3:]
model[0][3:] = (1, 2, 3)

or even use extended slices:

print model[0][2:10:2]
model[0][2:10:2] = (2, 4, 6, 8)
Comment 1 Sebastian Pölsterl 2011-08-19 10:34:38 UTC
Created attachment 194209 [details] [review]
Adds overrides and test cases
Comment 2 johnp 2011-08-24 15:39:03 UTC
Comment on attachment 194209 [details] [review]
Adds overrides and test cases

>From 2319b16fe9fd773c70dedf50f764a3b8e6042240 Mon Sep 17 00:00:00 2001
>From: =?UTF-8?q?Sebastian=20P=C3=B6lsterl?= <sebp@k-d-w.org>
>Date: Fri, 19 Aug 2011 12:27:04 +0200
>Subject: [PATCH] Added support for __setitem__ to TreeModel and support for
> slices to TreeModelRow
>
>---
> gi/overrides/Gtk.py     |   26 +++++++++++++++++++-
> tests/test_overrides.py |   57 +++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 81 insertions(+), 2 deletions(-)
>
>diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py
>index 2aea6ad..3d82276 100644
>--- a/gi/overrides/Gtk.py
>+++ b/gi/overrides/Gtk.py
>@@ -734,6 +734,10 @@ class TreeModel(Gtk.TreeModel):
>                 raise IndexError("could not find tree path '%s'" % key)
>             return TreeModelRow(self, aiter)
> 
>+    def __setitem__(self, key, value):
>+        row = self[key]
>+        self.set_row(row.iter, value)
>+
>     def __iter__(self):
>         return TreeModelRowIter(self, self.get_iter_first())
> 
>@@ -781,6 +785,8 @@ class TreeModel(Gtk.TreeModel):
>     def set_row(self, treeiter, row):
>         # TODO: Accept a dictionary for row
>         # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})

What is this for?  Is this because str are iterable also?

>+        if isinstance(row, str):
>+            raise TypeError('Expected a list or tuple, but got str')


>         n_columns = self.get_n_columns()
>         if len(row) != n_columns:
>@@ -1033,6 +1039,12 @@ class TreeModelRow(object):
>             elif key < 0:
>                 key = self._convert_negative_index(key)
>             return self.model.get_value(self.iter, key)
>+        elif isinstance(key, slice):
>+            start, stop, step = key.indices(self.model.get_n_columns())
>+            alist = []

xrange is not supported in python 3.  Essentially range is a generator in python 3.  Just use range, don't try to use fancy logic.  If it is really a performance issue file another bug and we will look at it then.

>+            for i in xrange(start, stop, step):
>+                alist.append(self.model.get_value(self.iter, i))
>+            return alist
>         else:
>             raise TypeError("indices must be integers, not %s" % type(key).__name__)
> 
>@@ -1042,9 +1054,19 @@ class TreeModelRow(object):
>                 raise IndexError("column index is out of bounds: %d" % key)
>             elif key < 0:
>                 key = self._convert_negative_index(key)
>-            return self.model.set_value(self.iter, key, value)
>+            self.model.set_value(self.iter, key, value)
>+        elif isinstance(key, slice):
>+            start, stop, step = key.indices(self.model.get_n_columns())
>+            indexList = range(start, stop, step)
>+            if len(indexList) != len(value):
>+                raise ValueError(
>+                    "attempt to assign sequence of size %d to slice of size %d"
>+                        % (len(value), len(indexList)))
>+
>+            for i,v in enumerate(indexList):
>+                self.model.set_value(self.iter, v, value[i])
>         else:
>-            raise TypeError("indices must be integers, not %s" % type(key).__name__)
>+            raise TypeError("index must be an integer or slice, not %s" % type(key).__name__)
> 
>     def _convert_negative_index(self, index):
>         new_index = self.model.get_n_columns() + index
>diff --git a/tests/test_overrides.py b/tests/test_overrides.py
>index 427f4d1..2f25121 100644
>--- a/tests/test_overrides.py
>+++ b/tests/test_overrides.py
>@@ -1246,6 +1246,63 @@ class TestGtk(unittest.TestCase):
>         self.assertRaises(ValueError, tree_store.get, aiter, 1, 100)
>         self.assertEqual(tree_store.get(aiter, 0, 1), (10, 'this is row #10'))
> 
>+    def test_tree_model_edit(self):
>+        model = Gtk.ListStore(int, str, float)
>+        model.append([1, "one", -0.1])
>+        model.append([2, "two", -0.2])
>+
>+        def set_row(value):
>+            model[1] = value
>+
>+        self.assertRaises(TypeError, set_row, 3)
>+        self.assertRaises(TypeError, set_row, "three")
>+        self.assertRaises(ValueError, set_row, [])
>+        self.assertRaises(ValueError, set_row, [3, "three"])
>+
>+        model[0] = (3, "three", -0.3)
>+
>+    def test_tree_row_slice(self):
>+        model = Gtk.ListStore(int, str, float)
>+        model.append([1, "one", -0.1])
>+
>+        self.assertEqual([1, "one", -0.1], model[0][:])
>+        self.assertEqual([1, "one"], model[0][:2])
>+        self.assertEqual(["one", -0.1], model[0][1:])
>+        self.assertEqual(["one"], model[0][1:-1])
>+        self.assertEqual([1], model[0][:-2])
>+        self.assertEqual([], model[0][5:])
>+        self.assertEqual([1, -0.1], model[0][0:3:2])
>+
>+        model[0][:] = (2, "two", -0.2)
>+        self.assertEqual([2, "two", -0.2], model[0][:])
>+
>+        model[0][:2] = (3, "three")
>+        self.assertEqual([3, "three", -0.2], model[0][:])
>+
>+        model[0][1:] = ("four", -0.4)
>+        self.assertEqual([3, "four", -0.4], model[0][:])
>+
>+        model[0][1:-1] = ("five",)
>+        self.assertEqual([3, "five", -0.4], model[0][:])
>+
>+        model[0][0:3:2] = (6, -0.6)
>+        self.assertEqual([6, "five", -0.6], model[0][:])
>+
>+        def set_row1():
>+            model[0][5:] = ("doesn't", "matter",)
>+
>+        self.assertRaises(ValueError, set_row1)
>+
>+        def set_row2():
>+            model[0][:1] = (0, "zero", 0)
>+
>+        self.assertRaises(ValueError, set_row2)
>+
>+        def set_row3():
>+            model[0][:2] = ("0", 0)
>+
>+        self.assertRaises(ValueError, set_row3)
>+
>     def test_tree_view_column(self):
>         cell = Gtk.CellRendererText()
>         column = Gtk.TreeViewColumn(title='This is just a test',
>-- 
>1.7.6
>
Comment 3 johnp 2011-08-24 15:39:25 UTC
Feel free to commit after fixing the xrange issue.
Comment 4 Sebastian Pölsterl 2011-08-24 16:09:15 UTC
(In reply to comment #2)
> 
> What is this for?  Is this because str are iterable also?
> 
> >+        if isinstance(row, str):
> >+            raise TypeError('Expected a list or tuple, but got str')

I added this to error out on calls such as

model[aiter] = "something"

which should be

model[aiter] = ["something",]
Comment 5 Sebastian Pölsterl 2011-08-24 16:15:11 UTC
Replaced xrange with range and committed as commit 01142060ae7d71a8a1f7d3e9bbc6f52e65f01c8d