Skip to content

Commit

Permalink
Really remove rows in compound models
Browse files Browse the repository at this point in the history
In CompoundModelBase.handle_items_removed() we took a shortcut
and emitted layoutAboutToBeChanged and layoutChanged signals.
This doesn't update e.g. a view's current index which causes
some warning messages (at least on Linux).
We now properly remove the rows using beginRemoveRows() and
endRemoveRows().
  • Loading branch information
soininen committed Sep 4, 2024
1 parent 5375777 commit 550ab11
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 9 deletions.
3 changes: 1 addition & 2 deletions spinetoolbox/mvcmodels/compound_table_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ class CompoundTableModel(MinimalTableModel):
"""A model that concatenates several sub table models vertically."""

def __init__(self, parent=None, header=None):
"""Initializes model.
"""
Args:
parent (QObject, optional): the parent object
header (list of str, optional): header labels
Expand Down
22 changes: 18 additions & 4 deletions spinetoolbox/spine_db_editor/mvcmodels/compound_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def _auto_filter_accepts_model(self, model):
for db_map, entity_class_id in values:
if model.db_map == db_map and (entity_class_id is None or model.entity_class_id == entity_class_id):
break
else: # nobreak
else:
return False
return True

Expand Down Expand Up @@ -391,7 +391,6 @@ def handle_items_removed(self, db_map_data):
Args:
db_map_data (dict): list of removed dict-items keyed by DatabaseMapping
"""
self.layoutAboutToBeChanged.emit()
for db_map, items in db_map_data.items():
if db_map not in self.db_maps:
continue
Expand All @@ -411,15 +410,30 @@ def handle_items_removed(self, db_map_data):
removed_ids.remove(id_)
if not removed_ids:
break
class_filter_accepted = self._class_filter_accepts_model(model)
for row, count in sorted(rows_to_row_count_tuples(removed_rows), reverse=True):
del model._main_data[row : row + count]
if not class_filter_accepted:
continue
compound_row = self._inv_row_map[(model, row)]
self.beginRemoveRows(QModelIndex(), compound_row, compound_row + count - 1)
tail_row_map = self._row_map[compound_row + count :]
self._row_map = self._row_map[:compound_row]
for mapped_row in tail_row_map:
if model is mapped_row[0]:
self._row_map.append((model, mapped_row[1] - count))
else:
self._row_map.append(mapped_row)
for r in range(row, row + count):
del self._inv_row_map[(model, r)]
for i, r in enumerate(self._row_map[compound_row:]):
self._inv_row_map[r] = i + compound_row
self.endRemoveRows()
if model.rowCount() == 0:
emptied_single_model_indexes.append(model_index)
for model_index in reversed(emptied_single_model_indexes):
model = self.sub_models.pop(model_index)
model.deleteLater()
self._do_refresh()
self.layoutChanged.emit()

def db_item(self, index):
sub_index = self.map_to_sub(index)
Expand Down
4 changes: 3 additions & 1 deletion tests/spine_db_editor/mvcmodels/test_compound_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ def test_model_updates_when_entity_class_is_removed(self):
model = CompoundParameterDefinitionModel(self._db_editor, self._db_mngr, self._db_map)
model.init_model()
fetch_model(model)
model.set_filter_class_ids({self._db_map: {entity_class_2["id"]}})
self.assertEqual(model.rowCount(), 4)
model.set_filter_class_ids({self._db_map: {entity_class_2["id"]}})
model.refresh()
self.assertEqual(model.rowCount(), 3)
self._db_mngr.remove_items({self._db_map: {"entity_class": [entity_class_2["id"]]}})
self.assertEqual(model.rowCount(), 1)

Expand Down
3 changes: 3 additions & 0 deletions tests/spine_db_editor/widgets/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def create_and_store_editor(instance, parent, option, target_index):
view.edit(index)
if self._cell_editor is None:
# Native editor widget is being used, fall back to setting value directly in model.
view.closeEditor()
view.model().setData(index, value)
return
if isinstance(self._cell_editor, SearchBarEditor):
Expand Down Expand Up @@ -71,6 +72,8 @@ def create_and_store_editor(instance, parent, option, target_index):
view.edit(index)

def reset(self):
if self._cell_editor is not None:
self._cell_editor.deleteLater()
self._cell_editor = None


Expand Down
2 changes: 1 addition & 1 deletion tests/test_SpineToolboxProject.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import networkx as nx
from PySide6.QtCore import QVariantAnimation
from PySide6.QtGui import QColor
from PySide6.QtWidgets import QApplication, QMessageBox, QGraphicsRectItem
from PySide6.QtWidgets import QApplication, QGraphicsRectItem, QMessageBox
from spine_engine.project_item.executable_item_base import ExecutableItemBase
from spine_engine.project_item.project_item_specification import ProjectItemSpecification
from spine_engine.spine_engine import ItemExecutionFinishState
Expand Down
2 changes: 1 addition & 1 deletion tests/widgets/test_custom_combobox.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Unit tests for the classes in ``custom_combobox`` module.
OpenProjectDialogComboBox is tested in test_open_project_dialog module."""
import unittest
from PySide6.QtGui import QPaintEvent, QImage, QColor
from PySide6.QtGui import QColor, QImage, QPaintEvent
from PySide6.QtWidgets import QWidget
from spinetoolbox.widgets.custom_combobox import CustomQComboBox, ElidedCombobox
from tests.mock_helpers import TestCaseWithQApplication
Expand Down

0 comments on commit 550ab11

Please sign in to comment.