Don't set completion model to None, just delete it

To avoid a leak when calling `QTreeView.setModel(None)`, this commit switches
to relying on the `model.destroyed` signal to make sure related state is
cleaned up. Upstream bug: https://bugreports.qt.io/browse/QTBUG-49966

When you call `setModel(None)` on a QTreeView it causes a small memory leak
of `QItemSelectionModel` objects created by the QTreeView's child QHeaderView
object.

`QAbstractItemView` will create a new `QItemSelectionModel` whenever a model
is set, even if that model is `None`. When the new model is non-null the
new selection model will be set to be deleted when the new model is, but when
the new model is None the new selection model will be linked to the
static `QAbstractItemModelPrivate::staticEmptyModel()`. Since that empty model
lives forever, so do the related section models, unless callers take care to
clean them up themselves.

Both `QTreeView` and it's child `QHeaderView` implement `QAbstractItemView`
and have this behaviour. For the `QTreeView` we were making sure to delete
the old selection model ourselves (as of fe1215c74d). But for the one
created by the `QHeaderView` we can't get a reference it because
`QTreeView.setModel()` would call `QHeaderView.setModel()` and then
`QHeaderView.setSelectionModel()` right away to assign it's own
selection model to the child, leaving no references to the selection
model created by `QHeaderView.setModel()`.

I was previously using `header.findChildren(QItemSelectionModel)` to clean up
old orphaned selection models, but this approach is a lot simpler!

To observe this for yourself you can plonk something like this in
`set_model()` before the early return and switch between the old and new
implementation and see how it changes behaviour.

        header = self.header()
        header_children = header.findChildren(QItemSelectionModel)
        our_children = self.findChildren(QItemSelectionModel)
        print(f"{len(our_children)=} {len(header_children)=}")

You can also observer the selection models accumulating in Gammaray
(https://github.com/KDAB/GammaRay/) if you just open and close the selection a
lot and then filter the object view by "model".

The relevant code is in `QTreeView` and `QAbstractItemView`'s
`setModel()`, `setSlectionModel()` and `modelDestroyed()`. Bot mostly in
the `setModels()` where you can see the relevant signals being connected
and disconnected.
https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/itemviews/qtreeview.cpp#n179

Fixes: #7947
This commit is contained in:
toofar 2023-10-01 18:40:44 +13:00
parent d3dc1bf4c1
commit b77cbbb2c3
1 changed files with 1 additions and 3 deletions

View File

@ -364,15 +364,13 @@ class CompletionView(QTreeView):
old_model = self.model()
if old_model is not None and model is not old_model:
old_model.deleteLater()
self._selection_model().deleteLater()
self.setModel(model)
if model is None:
self._active = False
self.hide()
return
self.setModel(model)
model.setParent(self)
self._active = True
self.pattern = None