Skip to content

Commit 87849a8

Browse files
authored
Merge pull request #4474 from ajdapretnar/fix-setitem
PyTableModel.__setitem__: properly emit signals
2 parents 24c7ab2 + 221f981 commit 87849a8

File tree

2 files changed

+73
-9
lines changed

2 files changed

+73
-9
lines changed

Orange/widgets/utils/itemmodels.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,18 @@ def _RoleData():
102102
# pylint: disable=missing-docstring
103103
def __init__(self, sequence=None, parent=None, editable=False):
104104
super().__init__(parent)
105+
self._rows = self._cols = 0
105106
self._headers = {}
106107
self._editable = editable
107108
self._table = None
108-
self._roleData = None
109+
self._roleData = {}
109110
self.wrap(sequence or [])
110111

111112
def rowCount(self, parent=QModelIndex()):
112-
return 0 if parent.isValid() else len(self)
113+
return 0 if parent.isValid() else self._rows
113114

114115
def columnCount(self, parent=QModelIndex()):
115-
return 0 if parent.isValid() else max(map(len, self._table), default=0)
116+
return 0 if parent.isValid() else self._cols
116117

117118
def flags(self, index):
118119
flags = super().flags(index)
@@ -214,6 +215,7 @@ def removeRows(self, row, count, parent=QModelIndex()):
214215
del self[row:row + count]
215216
for rowidx in range(row, row + count):
216217
self._roleData.pop(rowidx, None)
218+
self._rows = self._table_dim()[0]
217219
return True
218220
return False
219221

@@ -225,19 +227,25 @@ def removeColumns(self, column, count, parent=QModelIndex()):
225227
for col in range(column, column + count):
226228
cols.pop(col, None)
227229
del self._headers.get(Qt.Horizontal, [])[column:column + count]
230+
self._cols = self._table_dim()[1]
228231
self.endRemoveColumns()
229232
return True
230233

234+
def _table_dim(self):
235+
return len(self._table), max(map(len, self), default=0)
236+
231237
def insertRows(self, row, count, parent=QModelIndex()):
232238
self.beginInsertRows(parent, row, row + count - 1)
233239
self._table[row:row] = [[''] * self.columnCount() for _ in range(count)]
240+
self._rows = self._table_dim()[0]
234241
self.endInsertRows()
235242
return True
236243

237244
def insertColumns(self, column, count, parent=QModelIndex()):
238245
self.beginInsertColumns(parent, column, column + count - 1)
239246
for row in self._table:
240247
row[column:column] = [''] * count
248+
self._rows = self._table_dim()[0]
241249
self.endInsertColumns()
242250
return True
243251

@@ -262,18 +270,37 @@ def __delitem__(self, i):
262270
self._check_sort_order()
263271
self.beginRemoveRows(QModelIndex(), start, stop)
264272
del self._table[i]
273+
rows = self._table_dim()[0]
274+
self._rows = rows
265275
self.endRemoveRows()
276+
self._update_column_count()
266277

267278
def __setitem__(self, i, value):
279+
self._check_sort_order()
268280
if isinstance(i, slice):
269281
start, stop, _ = _as_contiguous_range(i, len(self))
270-
stop -= 1
282+
self.removeRows(start, stop - start)
283+
self.beginInsertRows(QModelIndex(), start, start + len(value) - 1)
284+
self._table[start:start] = value
285+
self._rows = self._table_dim()[0]
286+
self.endInsertRows()
287+
self._update_column_count()
271288
else:
272-
start = stop = i = i if i >= 0 else len(self) + i
273-
self._check_sort_order()
274-
self._table[i] = value
275-
self.dataChanged.emit(self.index(start, 0),
276-
self.index(stop, self.columnCount() - 1))
289+
self._table[i] = value
290+
self.dataChanged.emit(self.index(i, 0),
291+
self.index(i, self.columnCount() - 1))
292+
293+
def _update_column_count(self):
294+
cols_before = self._cols
295+
cols_after = self._table_dim()[1]
296+
if cols_before < cols_after:
297+
self.beginInsertColumns(QModelIndex(), cols_before, cols_after - 1)
298+
self._cols = cols_after
299+
self.endInsertColumns()
300+
elif cols_before > cols_after:
301+
self.beginRemoveColumns(QModelIndex(), cols_after, cols_before - 1)
302+
self._cols = cols_after
303+
self.endRemoveColumns()
277304

278305
def _check_sort_order(self):
279306
if self.mapToSourceRows(Ellipsis) is not Ellipsis:
@@ -285,6 +312,7 @@ def wrap(self, table):
285312
self.beginResetModel()
286313
self._table = table
287314
self._roleData = self._RoleData()
315+
self._rows, self._cols = self._table_dim()
288316
self.resetSorting()
289317
self.endResetModel()
290318

@@ -296,6 +324,7 @@ def clear(self):
296324
self._table.clear()
297325
self.resetSorting()
298326
self._roleData.clear()
327+
self._rows, self._cols = self._table_dim()
299328
self.endResetModel()
300329

301330
def append(self, row):

Orange/widgets/utils/tests/test_itemmodels.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import numpy as np
88

99
from AnyQt.QtCore import Qt, QModelIndex
10+
from AnyQt.QtTest import QSignalSpy
1011

1112
from Orange.data import \
1213
Domain, \
@@ -123,6 +124,40 @@ def test_other_roles(self):
123124
self.model.data(self.model.index(1, 0),
124125
Qt.TextAlignmentRole))
125126

127+
def test_set_iten_slice(self):
128+
self.model[:1] = [[10, 11], [12, 13], [14, 15]]
129+
self.assertEqual(list(self.model), [[10, 11], [12, 13], [14, 15], [2, 3]])
130+
131+
self.model[1:3] = []
132+
self.assertEqual(list(self.model), [[10, 11], [2, 3]])
133+
134+
self.model[:] = [[20, 21]]
135+
self.assertEqual(list(self.model), [[20, 21]])
136+
137+
self.model[1:] = [[10, 11], [2, 3]]
138+
self.assertEqual(list(self.model), [[20, 21], [10, 11], [2, 3]])
139+
140+
def test_emits_column_changes_on_row_insert(self):
141+
inserted = []
142+
removed = []
143+
model = PyTableModel()
144+
model.columnsInserted.connect(inserted.append)
145+
model.columnsRemoved.connect(removed.append)
146+
inserted = QSignalSpy(model.columnsInserted)
147+
removed = QSignalSpy(model.columnsRemoved)
148+
model.append([2])
149+
self.assertEqual(list(inserted)[-1][1:], [0, 0])
150+
model.append([2, 3])
151+
self.assertEqual(list(inserted)[-1][1:], [1, 1])
152+
del model[:]
153+
self.assertEqual(list(removed)[0][1:], [0, 1])
154+
model.extend([[0, 1], [0, 2]])
155+
self.assertEqual(list(inserted)[-1][1:], [0, 1])
156+
model.clear()
157+
self.assertEqual(list(removed)[0][1:], [0, 1])
158+
model[:] = [[1], [2]]
159+
self.assertEqual(list(inserted)[-1][1:], [0, 0])
160+
126161

127162
class TestVariableListModel(unittest.TestCase):
128163
@classmethod

0 commit comments

Comments
 (0)