Why do my comboboxes disappear from the treeview after clear the search filter field?
Starting application looks like this:
I then search using the QLineEdit which works as expected:
I then clear the search field and all my comoboboxes are gone?
import os, sys, pprint
sys.path.append(os.environ.get('PS_SITEPACKAGES'))
from Qt import QtGui, QtWidgets, QtCore
class VersionProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(VersionProxyModel, self).__init__(*args, **kwargs)
self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
def checkParents(self, index):
while (index.isValid()):
if super(VersionProxyModel, self).filterAcceptsRow(index.row(), index.parent()):
return True
index = index.parent()
return False
def checkChildren(self, index):
for i in range(0, self.sourceModel().rowCount(index)):
if super(VersionProxyModel, self).filterAcceptsRow(i, index):
return True
# recursive
for i in range(0, self.sourceModel().rowCount(index)):
self.checkChildren(self.sourceModel().index(i, 0, index))
return False
def filterAcceptsRow(self, source_row, parent):
if super(VersionProxyModel, self).filterAcceptsRow(source_row, parent):
return True
if self.checkChildren(self.sourceModel().index(source_row, 0, parent)):
return True
return self.checkParents(parent)
class Window(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(800, 400)
self.uiSearch = QtWidgets.QLineEdit()
self.versionModel = QtGui.QStandardItemModel()
self.versionProxyModel = VersionProxyModel()
self.versionProxyModel.setSourceModel(self.versionModel)
self.versionProxyModel.setDynamicSortFilter(True)
self.uiVersionTreeView = QtWidgets.QTreeView()
self.uiVersionTreeView.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.uiVersionTreeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.uiVersionTreeView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.uiVersionTreeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.uiVersionTreeView.setModel(self.versionProxyModel)
self.uiVersionTreeView.setRootIsDecorated(False)
# layout
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.uiSearch)
self.layout.addWidget(self.uiVersionTreeView)
self.setLayout(self.layout)
# signals/slots
self.uiSearch.textChanged.connect(self.searchFilterChanged)
self.populate()
def populate(self):
sortColumn = self.uiVersionTreeView.header().sortIndicatorSection()
sortDirection = self.uiVersionTreeView.header().sortIndicatorOrder()
self.versionModel.clear()
self.uiVersionTreeView.setSortingEnabled(False)
self.versionModel.setHorizontalHeaderLabels(['Entity', 'Type', 'Name', 'Versions'])
versions = {
'Leslie': [
{'fullname': 'medic_skin_v001', 'name': 'Medic', 'type': 'Bulky'}
],
'Mike': [
{'fullname': 'tech_skin_v001', 'name': 'Tech', 'type': 'Average'},
{'fullname': 'tech_skin_v002', 'name': 'Master', 'type': 'Average'}
],
'Michelle': [
{'fullname': 'warrior_skin_v001', 'name': 'Warrior', 'type': 'Athletic'},
{'fullname': 'warrior_skin_v002', 'name': 'Warrior', 'type': 'Athletic'},
{'fullname': 'warrior_skin_v003', 'name': 'Warrior', 'type': 'Athletic'}]
}
for key, values in versions.items():
col1 = QtGui.QStandardItem(values[0]['name'])
col2 = QtGui.QStandardItem(values[0]['type'])
col3 = QtGui.QStandardItem(key)
col4 = QtGui.QStandardItem()
self.versionModel.appendRow([col1, col2, col3, col4])
# set data
col2.setData(QtGui.QColor(80,150,200), role=QtCore.Qt.ForegroundRole)
combo = QtWidgets.QComboBox()
for x in values:
combo.addItem(x['fullname'], x)
mIndex = self.versionProxyModel.mapFromSource(col4.index())
self.uiVersionTreeView.setIndexWidget(mIndex, combo)
# Restore
self.uiVersionTreeView.setSortingEnabled(True)
self.uiVersionTreeView.setSortingEnabled(True)
self.uiVersionTreeView.sortByColumn(sortColumn, sortDirection)
self.uiVersionTreeView.expandAll()
for i in range(self.versionModel.columnCount()):
self.uiVersionTreeView.resizeColumnToContents(i)
def searchFilterChanged(self, text):
self.versionProxyModel.setFilterWildcard(text)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Window()
ex.show()
app.exec_()
If you check the logic of your code you will see that the combobox is highly related to the QModelIndex, that is, if the QModelIndex disappears, so will the QComboBox. In the case of a QSortFilterProxyModel when doing the filtering is eliminating and creating QModelIndex so consequently also eliminates the QComboBox, and they will not be restored, a possible solution is to track the deletion but that is very complicated. Another best solution is to use a delegate that provides a QComboBox as editor, and these QComboBox are created on demand.
import os, sys, pprint
sys.path.append(os.environ.get('PS_SITEPACKAGES'))
from Qt import QtGui, QtWidgets, QtCore
class VersionProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(VersionProxyModel, self).__init__(*args, **kwargs)
self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
def checkParents(self, index):
while (index.isValid()):
if super(VersionProxyModel, self).filterAcceptsRow(index.row(), index.parent()):
return True
index = index.parent()
return False
def checkChildren(self, index):
for i in range(0, self.sourceModel().rowCount(index)):
if super(VersionProxyModel, self).filterAcceptsRow(i, index):
return True
# recursive
for i in range(0, self.sourceModel().rowCount(index)):
self.checkChildren(self.sourceModel().index(i, 0, index))
return False
def filterAcceptsRow(self, source_row, parent):
if super(VersionProxyModel, self).filterAcceptsRow(source_row, parent):
return True
if self.checkChildren(self.sourceModel().index(source_row, 0, parent)):
return True
return self.checkParents(parent)
class ComboBoxDelegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
if isinstance(self.parent(), QtWidgets.QAbstractItemView):
self.parent().openPersistentEditor(index)
super(ComboBoxDelegate, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
editor = QtWidgets.QComboBox(parent)
editor.currentIndexChanged.connect(self.commitEditor)
return editor
@QtCore.Slot()
def commitEditor(self):
editor = self.sender()
self.commitData.emit(editor)
if isinstance(self.parent(), QtWidgets.QAbstractItemView):
self.parent().updateEditorGeometries()
def setEditorData(self, editor, index):
values = index.data(QtCore.Qt.UserRole + 100)
val = index.data(QtCore.Qt.UserRole + 101)
editor.clear()
for i, x in enumerate(values):
editor.addItem(x['fullname'], x)
if val['fullname'] == x['fullname']:
editor.setCurrentIndex(i)
def setModelData(self, editor, model, index):
values = index.data(QtCore.Qt.UserRole + 100)
ix = editor.currentIndex()
model.setData(index, values[ix] , QtCore.Qt.UserRole + 101)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class Window(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(800, 400)
self.uiSearch = QtWidgets.QLineEdit()
self.versionModel = QtGui.QStandardItemModel()
self.versionProxyModel = VersionProxyModel()
self.versionProxyModel.setSourceModel(self.versionModel)
self.versionProxyModel.setDynamicSortFilter(True)
self.uiVersionTreeView = QtWidgets.QTreeView()
self.uiVersionTreeView.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.uiVersionTreeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.uiVersionTreeView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.uiVersionTreeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.uiVersionTreeView.setModel(self.versionProxyModel)
self.uiVersionTreeView.setRootIsDecorated(False)
delegate = ComboBoxDelegate(self.uiVersionTreeView)
self.uiVersionTreeView.setItemDelegateForColumn(3, delegate)
# layout
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.uiSearch)
self.layout.addWidget(self.uiVersionTreeView)
self.setLayout(self.layout)
# signals/slots
self.uiSearch.textChanged.connect(self.versionProxyModel.setFilterWildcard)
self.populate()
def populate(self):
sortColumn = self.uiVersionTreeView.header().sortIndicatorSection()
sortDirection = self.uiVersionTreeView.header().sortIndicatorOrder()
self.versionModel.clear()
self.uiVersionTreeView.setSortingEnabled(False)
self.versionModel.setHorizontalHeaderLabels(['Entity', 'Type', 'Name', 'Versions'])
versions = {
'Leslie': [
{'fullname': 'medic_skin_v001', 'name': 'Medic', 'type': 'Bulky'}
],
'Mike': [
{'fullname': 'tech_skin_v001', 'name': 'Tech', 'type': 'Average'},
{'fullname': 'tech_skin_v002', 'name': 'Master', 'type': 'Average'}
],
'Michelle': [
{'fullname': 'warrior_skin_v001', 'name': 'Warrior', 'type': 'Athletic'},
{'fullname': 'warrior_skin_v002', 'name': 'Warrior', 'type': 'Athletic'},
{'fullname': 'warrior_skin_v003', 'name': 'Warrior', 'type': 'Athletic'}]
}
for key, values in versions.items():
col1 = QtGui.QStandardItem(values[0]['name'])
col2 = QtGui.QStandardItem(values[0]['type'])
col3 = QtGui.QStandardItem(key)
col4 = QtGui.QStandardItem()
self.versionModel.appendRow([col1, col2, col3, col4])
col2.setData(QtGui.QColor(80,150,200), role=QtCore.Qt.ForegroundRole)
col4.setData(values, QtCore.Qt.UserRole + 100)
col4.setData(values[0], QtCore.Qt.UserRole + 101)
# Restore
self.uiVersionTreeView.setSortingEnabled(True)
self.uiVersionTreeView.sortByColumn(sortColumn, sortDirection)
self.uiVersionTreeView.expandAll()
for i in range(self.versionModel.columnCount()):
self.uiVersionTreeView.resizeColumnToContents(i)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Window()
ex.show()
app.exec_()