Reputation: 1412
Disclaimer: I found different similar answers for this topic, but I couldn't get the one that solves mine.
I have the following source model structure that is shown by a single-column, headerless QTreeView
:
- Cat 1
|-- Cat1_El1
|-- ...
|-- Cat1_ElL
...
- Cat N
|-- CatN_El1
|-- ...
|-- CatN_ElM
The view and the source model work fine. Each low-level item is dynamically created by the user, while the categories are hard-coded.
Now, the core issue follows: Based upon a certain condition, that I can easily manage through an extra property of an instanziated Proxy model, I'd like to display in a QComboBox
all the items of a specific category.
I thought of re-implementing the QAbstractProxyModel
but I was facing some troubles to understand how to re-implement the two methods mapFromSource
and mapToSource
stated by the Qt Doc. What I came up with is shown below. All my doubts and inferring are reported as comments, as I thought it clearer.
Note that I was coding in Python, using PyQt5, but actually I'm much more interested into the concept of how to re-implement the proxy rather than into the code itself (thus, also a C++ code works very fine)
class GWModulesComboProxy(QIdentityProxyModel):
# This re-implements the basic proxy structure
def __init__(self, moduleType : CategoriesType, parent: Optional[QObject]) -> None:
# `CategoriesType` --> See later `ComboProxyCatN`
self.moduleType = moduleType
super().__init__(parent)
def index(self, row: int, column: int, parent: QModelIndex) -> QModelIndex:
# Based upon the crash of the model when clicking on the combobox, I'm inferring that I should re-implementing
# this method and call, FROM it, the `mapToSource`. But I can't figure out how
pass
def mapFromSource(self, sourceIndex: QModelIndex) -> QModelIndex:
if not sourceIndex.isValid():
return QModelIndex()
# If the node is a root node (i.e. a Cathegory itself), it will never show up into the ComboBox
if not sourceIndex.parent().isValid():
return QModelIndex()
# Into the source model, the root nodes are ordered against the IntEnum CategoriesType --> See later `ComboProxyCatN`
if sourceIndex.parent().row() == self.moduleType:
return sourceIndex
return QModelIndex()
def mapToSource(self, proxyIndex: QModelIndex) -> QModelIndex:
if not proxyIndex.isValid():
return QModelIndex()
parent = self.sourceModel().index(self.moduleType, 1, QModelIndex())
return self.sourceModel().index(proxyIndex.row(), proxyIndex.column(), parent)
def data(self, proxyIndex: QModelIndex, role: int) -> Any:
# I was guessing that re-implementing `data` was meaningless since the data should be already taken from the source
# by means of the mapper methods. Am I correct?
pass
def columnCount(self, parent: QModelIndex) -> int:
return 1
class ComboProxyCatN(ComboProxy):
# Just an example how to manage the specific Category N
def __init__(self, parent: Optional[QObject]) -> None:
# CategoriesType is an IntEnum that stores the different cathegories
super().__init__(CategoriesType.CatN, parent)
Upvotes: 0
Views: 216
Reputation: 243965
To show a branch of the tree it is not necessary to use a proxymodel but rather the root must be indicated through the setRootModelIndex()
method of the QComboBox.
The following example shows the branch of the category "Cat 5":
import random
from PyQt5 import QtCore, QtGui, QtWidgets
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
model = QtGui.QStandardItemModel()
for i in range(1, 10):
root_item = QtGui.QStandardItem(f"Cat {i}")
model.appendRow(root_item)
for j in range(1, random.randrange(5, 10)):
child_item = QtGui.QStandardItem(f"Cat{i}_El{j}")
root_item.appendRow(child_item)
view = QtWidgets.QTreeView()
view.setModel(model)
view.expandAll()
category = "Cat 5"
root_index = QtCore.QModelIndex()
indexes = model.match(
model.index(0, 0), QtCore.Qt.DisplayRole, category, flags=QtCore.Qt.MatchExactly
)
if indexes:
root_index = indexes[0]
# or fifth row
# root_index = model.index(4, 0)
combobox = QtWidgets.QComboBox()
combobox.setModel(model)
combobox.setRootModelIndex(root_index)
combobox.setCurrentIndex(0)
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(view)
lay.addWidget(combobox)
widget.resize(640, 480)
widget.show()
sys.exit(app.exec_())
Upvotes: 3