如何在 pyqt5 中对 Qtreeview 中的项目进行排序?

问题描述 投票:0回答:0

如何通过以下概念对QTreeview中的项目进行排序?

  • 升序(从 A 到 Z,0 到 9)
  • 降序(从 Z 到 A,9 到 0)
  • 用户给出的订单或原始订单(从A到Z)
  • 原顺序的倒序(从Z到A)
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_())
python pyqt pyqt5 qtreeview
© www.soinside.com 2019 - 2024. All rights reserved.