[api] Make QgsLayerTreeView proxy handling more flexible

Allow use of custom QgsLayerTreeProxyModel subclasses
This commit is contained in:
Nyall Dawson 2025-08-18 10:58:21 +10:00
parent 6c63043138
commit 1fbddb214d
7 changed files with 106 additions and 18 deletions

View File

@ -7,12 +7,13 @@ try:
except (NameError, AttributeError):
pass
try:
QgsLayerTreeViewMenuProvider.__abstract_methods__ = ['createContextMenu']
QgsLayerTreeViewMenuProvider.__group__ = ['layertree']
except (NameError, AttributeError):
pass
try:
QgsLayerTreeProxyModel.__virtual_methods__ = ['nodeShown']
QgsLayerTreeProxyModel.__overridden_methods__ = ['filterAcceptsRow']
QgsLayerTreeProxyModel.__group__ = ['layertree']
except (NameError, AttributeError):
pass
try:
QgsLayerTreeViewMenuProvider.__abstract_methods__ = ['createContextMenu']
QgsLayerTreeViewMenuProvider.__group__ = ['layertree']
except (NameError, AttributeError):
pass

View File

@ -75,6 +75,13 @@ shown).
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;
virtual bool nodeShown( QgsLayerTreeNode *node ) const;
%Docstring
Returns ``True`` if the specified ``node`` should be shown.
.. versionadded:: 4.0
%End
};
@ -118,6 +125,19 @@ Constructor for QgsLayerTreeView
%Docstring
Overridden :py:func:`~QgsLayerTreeView.setModel` from base class. Only
:py:class:`QgsLayerTreeModel` is an acceptable model.
.. note::
This method automatically creates a :py:class:`QgsLayerTreeProxyModel` to use as a proxy.
%End
void setModel( QgsLayerTreeModel *model, QgsLayerTreeProxyModel *proxyModel );
%Docstring
Sets the ``model`` and ``proxyModel`` for the view.
Use this method when a custom proxy model is required.
.. versionadded:: 4.0
%End
QgsLayerTreeModel *layerTreeModel() const;

View File

@ -7,12 +7,13 @@ try:
except (NameError, AttributeError):
pass
try:
QgsLayerTreeViewMenuProvider.__abstract_methods__ = ['createContextMenu']
QgsLayerTreeViewMenuProvider.__group__ = ['layertree']
except (NameError, AttributeError):
pass
try:
QgsLayerTreeProxyModel.__virtual_methods__ = ['nodeShown']
QgsLayerTreeProxyModel.__overridden_methods__ = ['filterAcceptsRow']
QgsLayerTreeProxyModel.__group__ = ['layertree']
except (NameError, AttributeError):
pass
try:
QgsLayerTreeViewMenuProvider.__abstract_methods__ = ['createContextMenu']
QgsLayerTreeViewMenuProvider.__group__ = ['layertree']
except (NameError, AttributeError):
pass

View File

@ -75,6 +75,13 @@ shown).
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;
virtual bool nodeShown( QgsLayerTreeNode *node ) const;
%Docstring
Returns ``True`` if the specified ``node`` should be shown.
.. versionadded:: 4.0
%End
};
@ -118,6 +125,19 @@ Constructor for QgsLayerTreeView
%Docstring
Overridden :py:func:`~QgsLayerTreeView.setModel` from base class. Only
:py:class:`QgsLayerTreeModel` is an acceptable model.
.. note::
This method automatically creates a :py:class:`QgsLayerTreeProxyModel` to use as a proxy.
%End
void setModel( QgsLayerTreeModel *model, QgsLayerTreeProxyModel *proxyModel );
%Docstring
Sets the ``model`` and ``proxyModel`` for the view.
Use this method when a custom proxy model is required.
.. versionadded:: 4.0
%End
QgsLayerTreeModel *layerTreeModel() const;

View File

@ -91,6 +91,15 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model )
if ( !treeModel )
return;
auto proxyModel = new QgsLayerTreeProxyModel( treeModel, this );
proxyModel->setShowPrivateLayers( mShowPrivateLayers );
proxyModel->setHideValidLayers( mHideValidLayers );
setModel( treeModel, proxyModel );
}
void QgsLayerTreeView::setModel( QgsLayerTreeModel *treeModel, QgsLayerTreeProxyModel *proxyModel )
{
if ( mMessageBar )
connect( treeModel, &QgsLayerTreeModel::messageEmitted, this, [this]( const QString &message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 ) {
Q_UNUSED( duration )
@ -99,8 +108,7 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model )
treeModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
mProxyModel = new QgsLayerTreeProxyModel( treeModel, this );
mProxyModel = proxyModel;
connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted );
connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved );
@ -108,8 +116,6 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model )
new ModelTest( mProxyModel, this );
#endif
mProxyModel->setShowPrivateLayers( mShowPrivateLayers );
mProxyModel->setHideValidLayers( mHideValidLayers );
QTreeView::setModel( mProxyModel );
connect( treeModel->rootGroup(), &QgsLayerTreeNode::expandedChanged, this, &QgsLayerTreeView::onExpandedChanged );

View File

@ -96,9 +96,14 @@ class GUI_EXPORT QgsLayerTreeProxyModel : public QSortFilterProxyModel
protected:
bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override;
private:
bool nodeShown( QgsLayerTreeNode *node ) const;
/**
* Returns TRUE if the specified \a node should be shown.
*
* \since QGIS 4.0
*/
virtual bool nodeShown( QgsLayerTreeNode *node ) const;
private:
QgsLayerTreeModel *mLayerTreeModel = nullptr;
QString mFilterText;
bool mShowPrivateLayers = false;
@ -138,9 +143,22 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
explicit QgsLayerTreeView( QWidget *parent SIP_TRANSFERTHIS = nullptr );
~QgsLayerTreeView() override;
//! Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
/**
* Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
*
* \note This method automatically creates a QgsLayerTreeProxyModel to use as a proxy.
*/
void setModel( QAbstractItemModel *model ) override;
/**
* Sets the \a model and \a proxyModel for the view.
*
* Use this method when a custom proxy model is required.
*
* \since QGIS 4.0
*/
void setModel( QgsLayerTreeModel *model, QgsLayerTreeProxyModel *proxyModel );
//! Gets access to the model casted to QgsLayerTreeModel
QgsLayerTreeModel *layerTreeModel() const;

View File

@ -22,7 +22,11 @@ from qgis.core import (
QgsMarkerSymbol,
QgsMapLayerLegend,
)
from qgis.gui import QgsLayerTreeView, QgsLayerTreeViewDefaultActions
from qgis.gui import (
QgsLayerTreeView,
QgsLayerTreeViewDefaultActions,
QgsLayerTreeProxyModel,
)
import unittest
from qgis.testing import start_app, QgisTestCase
@ -814,6 +818,24 @@ class TestQgsLayerTreeView(QgisTestCase):
view.selectedLegendNodes(), [legend_nodes[0], legend_nodes[2]]
)
def test_set_model_and_proxy(self):
root = QgsLayerTree()
model = QgsLayerTreeModel(root)
view = QgsLayerTreeView()
view.setModel(model)
self.assertEqual(view.layerTreeModel(), model)
# a proxy should have been auto-created
self.assertIsInstance(view.model(), QgsLayerTreeProxyModel)
# set an explicit proxy
root2 = QgsLayerTree()
model2 = QgsLayerTreeModel(root2)
my_proxy = QgsLayerTreeProxyModel(model2, None)
view.setModel(model2, my_proxy)
self.assertEqual(view.layerTreeModel(), model2)
self.assertEqual(view.model(), my_proxy)
if __name__ == "__main__":
unittest.main()