如何通过以下概念对QTreeview中的项目进行排序?
self.model_01 = self.model()
for i in range(self.model_01.rowCount()):
if self.itemData(i) is None:
self.setItemData(i, i)
...
def ascending_order(self):
self.model_01.setSortRole(Qt.DisplayRole)
self.model_01.sort(self.modelColumn(), Qt.AscendingOrder)
def descending_order(self):
self.model_01.setSortRole(Qt.DisplayRole)
self.model_01.sort(self.modelColumn(), Qt.DescendingOrder)
def given_order(self):
print("given order")
self.model_01.setSortRole(Qt.UserRole)
self.model_01.sort(self.modelColumn(), Qt.AscendingOrder)
def reverse_order(self):
print("reverse order")
self.model_01.setSortRole(Qt.UserRole)
self.model_01.sort(self.modelColumn(), Qt.DescendingOrder)
使用此代码,我可以在 Qt.DisplayRole 中按升序和降序对项目进行排序。
但是在 Qt.UserRole 中,我无法对项目进行排序。
如何按升序(原顺序)或原顺序倒序排列项目?
更新 - 最小的可重现示例
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
data = {
"select all": {
'Group 1': ['item11', 'item12'],
'Group 3': ['item32', 'item31'],
'Group 2': ['item21', 'item22'],
'Group 4': ['item41', 'item42'],
}
}
class MyModel(QStandardItemModel):
def __init__(self):
super().__init__()
self.root_text, self.parent_text, self.child_text = [], [], []
for root_key, root_value in data.items():
if root_key not in self.root_text:
self.root_text.append(root_key)
root_item = QStandardItem()
root_item.setData(root_key, role=Qt.DisplayRole)
root_item.setCheckable(True)
self.appendRow(root_item)
for parent_key, parent_value in root_value.items():
if parent_key not in self.parent_text:
self.parent_text.append(parent_key)
parent_item = QStandardItem()
parent_item.setData(parent_key, role=Qt.DisplayRole)
parent_item.setCheckable(True)
root_item.appendRow(parent_item)
for child_value in parent_value:
if child_value not in self.child_text:
self.child_text.append(child_value)
child_item = []
child_item = QStandardItem()
child_item.setData(child_value, role=Qt.DisplayRole)
child_item.setCheckable(True)
parent_item.appendRow(child_item)
self.itemChanged.connect(self.update_children)
def update_children(self, item, fromUser=True):
print(item,"item")
if fromUser:
# temporarily disconnect to avoid recursion
self.itemChanged.disconnect(self.update_children)
for i in range(item.rowCount()):
child = item.child(i)
child.setCheckState(item.checkState())
# explicitly call update_children
self.update_children(child, False)
if fromUser:
root = self.invisibleRootItem()
parent = item.parent() or root
while True:
count = parent.rowCount()
checked = 0
for i in range(count):
state = parent.child(i).checkState()
if state == Qt.Checked:
checked += 1
elif state == Qt.PartiallyChecked:
parent.setCheckState(Qt.PartiallyChecked)
break
else:
if not checked:
parent.setCheckState(Qt.Unchecked)
elif checked == count:
parent.setCheckState(Qt.Checked)
else:
parent.setCheckState(Qt.PartiallyChecked)
if parent == root:
break
parent = parent.parent() or root
self.itemChanged.connect(self.update_children)
class MyCombo(QComboBox):
clickedData = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.treeView = QTreeView()
self.treeView.setHeaderHidden(True)
self.setView(self.treeView)
self.treeView.viewport().installEventFilter(self)
# Qmenu intilize
self.menu = QMenu()
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.cntxt_menu)
self.RightClickMenu()
self.delegate = QStyledItemDelegate(self.treeView)
def eventFilter(self, obj, event):
if (
event.type() == event.MouseButtonPress
and event.button() == Qt.LeftButton
):
index = self.treeView.indexAt(event.pos())
if index.isValid():
opt = self.treeView.viewOptions()
opt.rect = self.treeView.visualRect(index)
self.delegate.initStyleOption(opt, index)
checkRect = self.style().subElementRect(
QStyle.SE_ItemViewItemCheckIndicator, opt, self.treeView)
if checkRect.contains(event.pos()):
self.clickedData = index, checkRect
elif event.type() == event.MouseButtonRelease:
if event.button() == Qt.LeftButton and self.clickedData:
index = self.treeView.indexAt(event.pos())
pressIndex, checkRect = self.clickedData
if index == pressIndex and event.pos() in checkRect:
state = index.data(Qt.CheckStateRole)
if state == Qt.Checked:
state = Qt.Unchecked
else:
state = Qt.Checked
self.model().setData(index, state, Qt.CheckStateRole)
self.clickedData = None
return True
elif (
event.type() == event.MouseButtonPress
and event.button() == Qt.LeftButton
):
index = self.treeView.indexAt(event.pos())
state = index.data(Qt.CheckStateRole)
if state == Qt.Checked:
state = Qt.Unchecked
else:
state = Qt.Checked
self.model().setData(index, state, Qt.CheckStateRole)
self.treeView.viewport().update()
self.clickedData = None
return True
return super().eventFilter(obj, event)
def showPopup(self):
self.treeView.expandAll()
width = self.treeView.sizeHintForColumn(0)
maxCount = self.maxVisibleItems()
index = self.model().index(0, 0, self.rootModelIndex())
visible = 0
while index.isValid():
visible += 1
index = self.treeView.indexBelow(index)
if visible > maxCount:
# the visible count is higher than the maximum, so the vertical
# scroll bar will be shown and we have to consider its width.
# Note that this does NOT consider styles that use "transient"
# scroll bars, which are shown *within* the content of the view,
# as it happens on macOs; see QStyle.styleHint() and
# QStyle::SH_ScrollBar_Transient
width += self.treeView.verticalScrollBar().sizeHint().width()
break
self.treeView.setMinimumWidth(width)
super().showPopup()
def RightClickMenu(self):
self.menu.clear()
self.ascending_action = QAction('Ascending',self)
self.menu.addAction(self.ascending_action)
self.ascending_action.triggered.connect(self.ascending_order)
self.descending_action = QAction('Descending')
self.descending_action.triggered.connect(self.descending_order)
self.menu.addAction(self.descending_action)
self.original_action = QAction('Original Order')
self.original_action.triggered.connect(self.original_order)
self.menu.addAction(self.original_action)
self.reverse_action = QAction('Reverse order')
self.reverse_action.triggered.connect(self.reverse_order)
self.menu.addAction(self.reverse_action)
def cntxt_menu(self,pos):
self.model_01 = self.model()
self.menu.exec_(self.mapToGlobal(pos))
def ascending_order(self):
self.model_01.setSortRole(Qt.DisplayRole)
self.model_01.sort(self.modelColumn(),Qt.AscendingOrder)
def descending_order(self):
self.model_01.setSortRole(Qt.DisplayRole)
self.model_01.sort(self.modelColumn(), Qt.DescendingOrder)
def original_order(self):
print("given order")
self.model_01.setSortRole(Qt.UserRole)
# self.model_01.sort(0, Qt.AscendingOrder)
self.model_01.sort(0,Qt.AscendingOrder)
def reverse_order(self):
print("reverse order")
self.model_01.setSortRole(Qt.UserRole)
self.model_01.sort(self.modelColumn(), Qt.DescendingOrder)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QCombobox")
self.comboBox = MyCombo()
self.comboBox.setEditable(False)
self.model = MyModel()
self.comboBox.setModel(self.model)
self.vbox = QVBoxLayout()
self.setLayout(self.vbox)
self.vbox.addWidget(self.comboBox)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())