aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sb_builder/packages.py16
-rw-r--r--tests/test_gui.py22
2 files changed, 34 insertions, 4 deletions
diff --git a/sb_builder/packages.py b/sb_builder/packages.py
index ab6f3ce..68de7c2 100644
--- a/sb_builder/packages.py
+++ b/sb_builder/packages.py
@@ -112,6 +112,16 @@ class Model(QAbstractTableModel):
self.checked &= names
self.endResetModel()
+ def boxes_changed(self):
+ """Only check states changed: dataChanged on the box column (count follows).
+
+ Not layoutChanged: emitted without layoutAboutToBeChanged it leaves the
+ filtering proxy with stale persistent indexes, and Qt segfaults.
+ """
+ if self.rows:
+ self.dataChanged.emit(self.index(0, 0), self.index(len(self.rows) - 1, 0),
+ [Qt.ItemDataRole.CheckStateRole])
+
def rowCount(self, parent=QModelIndex()):
return 0 if parent.isValid() else len(self.rows)
@@ -337,13 +347,11 @@ class PackagesTab(QWidget):
def select_visible(self):
for i in range(self.proxy.rowCount()):
self.model.checked.add(self.proxy.index(i, 0).data(Qt.ItemDataRole.UserRole)["name"])
- self.model.layoutChanged.emit()
- self.update_count()
+ self.model.boxes_changed()
def clear(self):
self.model.checked.clear()
- self.model.layoutChanged.emit()
- self.update_count()
+ self.model.boxes_changed()
def targets(self):
"""Ticked packages, sorted; the highlighted row alone is not a target."""
diff --git a/tests/test_gui.py b/tests/test_gui.py
index bd08e7f..ca51c3c 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -651,6 +651,28 @@ class SmokeTest(unittest.TestCase):
w.hint_dialogs[0].build()
self.assertIn(("job-start", "slackrepo", "update", "--preview", "nuclei"), remote.calls)
+ def test_clear_with_filter_and_current_row(self):
+ # regression: Clear emitted layoutChanged with no layoutAboutToBeChanged;
+ # behind a filter, with a current row, the proxy's stale indexes segfaulted Qt
+ rows = [inv_row(f"games/g{i}", maintainer="orphaned - no maintainer" if i % 2 else "A")
+ for i in range(40)] + [inv_row(f"misc/m{i}") for i in range(40)]
+ w, _ = self.window(inventory=({"columns": COLS, "rows": rows}, None))
+ w.show()
+ tab = w.open_packages()
+ tab.chips["orphaned"].setChecked(True)
+ tab.query.setText("cat:games")
+ for i in range(3):
+ tab.table.clicked.emit(tab.proxy.index(i, 1))
+ tab.table.setCurrentIndex(tab.proxy.index(2, 1))
+ app.processEvents()
+ select, clear = tab.keys
+ clear.activated.emit()
+ app.processEvents()
+ self.assertEqual(tab.count.text(), "20 shown · 0 selected")
+ select.activated.emit()
+ app.processEvents()
+ self.assertEqual(tab.count.text(), "20 shown · 20 selected")
+
def test_packages_new_hint(self):
w, remote = self.window(**{"job-start": ({"id": "20260101-000001-hint-new"}, None)})
w.poll_jobs()