小部件从 QTreeview 中消失
Widgets Disappear From QTreeview
为什么我的组合框在清除搜索过滤器字段后从树视图中消失?
启动应用程序如下所示:
然后我使用按预期工作的 QLineEdit 进行搜索:
然后我清除了搜索字段,我所有的组合框都不见了?
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_()
如果检查代码的逻辑,您会发现组合框与QModelIndex 高度相关,也就是说,如果QModelIndex 消失,那么QComboBox 也会消失。在 QSortFilterProxyModel 的情况下,当进行过滤时消除和创建 QModelIndex 因此也消除了 QComboBox,并且它们不会被恢复,一个可能的解决方案是跟踪删除,但这非常复杂。另一个最佳解决方案是使用提供 QComboBox 的委托作为编辑器,这些 QComboBox 是按需创建的。
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_()
为什么我的组合框在清除搜索过滤器字段后从树视图中消失?
启动应用程序如下所示:
然后我使用按预期工作的 QLineEdit 进行搜索:
然后我清除了搜索字段,我所有的组合框都不见了?
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_()
如果检查代码的逻辑,您会发现组合框与QModelIndex 高度相关,也就是说,如果QModelIndex 消失,那么QComboBox 也会消失。在 QSortFilterProxyModel 的情况下,当进行过滤时消除和创建 QModelIndex 因此也消除了 QComboBox,并且它们不会被恢复,一个可能的解决方案是跟踪删除,但这非常复杂。另一个最佳解决方案是使用提供 QComboBox 的委托作为编辑器,这些 QComboBox 是按需创建的。
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_()