【问题标题】:Checkboxes in treeview update slowly树视图中的复选框更新缓慢
【发布时间】:2020-10-14 02:10:21
【问题描述】:

我为树视图实现了一个自定义模型,用于在树视图中添加复选框。如果我检查父节点,所有子节点都应该自动检查。这基本上可行,但在检查父节点和更新子节点之间缺少时间。

from PyQt4 import QtCore, QtGui
import sys

class Node(object):   
    def __init__(self, name, parent=None, checked=False):

        self._name = name
        self._children = []
        self._parent = parent
        self._checked = checked

        if parent is not None:
            parent.addChild(self)

    def addChild(self, child):
        self._children.append(child)

    def insertChild(self, position, child):
        if position < 0 or position > len(self._children):
            return False

        self._children.insert(position, child)
        child._parent = self
        return True

    def name(self):
        return self._name

    def checked(self):
        return self._checked

    def setChecked(self, state):
        self._checked = state

        for c in self._children:
            c.setChecked(state)

    def child(self, row):
        return self._children[row]

    def childCount(self):
        return len(self._children)

    def parent(self):
        return self._parent

    def row(self):
        if self._parent is not None:
            return self._parent._children.index(self)

class TreeModel(QtCore.QAbstractItemModel):
    def __init__(self, root, parent=None):
        super().__init__(parent)
        self._rootNode = root

    def rowCount(self, parent):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()

        return parentNode.childCount()

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        if not index.isValid():
            return None

        node = index.internalPointer()

        if role == QtCore.Qt.DisplayRole:
            if index.column() == 0:
                return node.name()
        if role == QtCore.Qt.CheckStateRole:
            if node.checked():
                return QtCore.Qt.Checked
            else:
                return QtCore.Qt.Unchecked

    def setData(self, index, value, role=QtCore.Qt.EditRole):

        if index.isValid():
            if role == QtCore.Qt.CheckStateRole:
                node = index.internalPointer()
                node.setChecked(not node.checked())
                return True
        return False

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            return "Nodes"

    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsUserCheckable

    def parent(self, index):
        node = self.getNode(index)
        parentNode = node.parent()

        if parentNode == self._rootNode:
            return QtCore.QModelIndex()
        return self.createIndex(parentNode.row(), 0, parentNode)

    def index(self, row, column, parent):
        parentNode = self.getNode(parent)
        childItem = parentNode.child(row)

        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QtCore.QModelIndex()

    def getNode(self, index):
        if index.isValid():
            node = index.internalPointer()
            if node:
                return node

        return self._rootNode

    def removeRows(self, position, rows, parent=QtCore.QModelIndex()):

        parentNode = self.getNode(parent)
        self.beginRemoveRows(parent, position, position + rows - 1)

        for row in range(rows):
            success = parentNode.removeChild(position)

        self.endRemoveRows()

        return success

def main_simple():
    app = QtGui.QApplication(sys.argv)

    rootNode   = Node("Root")
    n1 = Node("Node1", rootNode)
    n2 = Node("Node2", rootNode)
    n3 = Node("Node3", rootNode)

    n1_1 = Node("Node1 1", n1)
    n1_2 = Node("Node1 2", n1)
    n1_3 = Node("Node1 3", n1)

    n2_1 = Node("Node2 1", n2)
    n2_2 = Node("Node2 2", n2)
    n2_3 = Node("Node2 3", n2)

    n3_1 = Node("Node3 1", n3)
    n3_2 = Node("Node3 2", n3)
    n3_3 = Node("Node3 3", n3)

    model = TreeModel(rootNode)

    treeView = QtGui.QTreeView()
    treeView.show()
    treeView.setModel(model)

    sys.exit(app.exec_())

if __name__ == '__main__':
    main_simple()

如何避免这种缺失,让用户界面更加流畅?

【问题讨论】:

    标签: python pyqt qtreeview


    【解决方案1】:

    当孩子们被切换时,模型应该为孩子们发出dataChanged() 信号,以便视图可以立即更新他们的复选框。根据the documentationsetData 也应该为显式更改的项目发出dataChanged

    这应该可行:

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            if role == QtCore.Qt.CheckStateRole:
                node = index.internalPointer()
                node.setChecked(not node.checked())
                self.dataChanged.emit(index, index)               
                self.emitDataChangedForChildren(index)
                return True
        return False
    
    def emitDataChangedForChildren(self, index):
        count = self.rowCount(index)
        if count:            
            self.dataChanged.emit(index.child(0, 0), index.child(count-1, 0))
            for child in range(count):
                self.emitDataChangedForChildren(index.child(child, 0))
    

    【讨论】:

    • 请注意,虽然 OP 只要求刷新被点击的节点 children,但将复选框与 QAbstractItemModel 结合通常也需要更新 parent 节点,只要它们代表一种 逻辑和 的子节点(尤其是如果其子节点具有混合检查状态,则您希望将每个节点设置为 Qt.PartiallyChecked)。在这种情况下,我发现只需刷新根节点self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex()) 就可以了,不需要答案的迭代方法。
    【解决方案2】:

    根据上面的 flonks 回答,对我有用的非迭代解决方案如下:

    C++:emit dataChanged(QModelIndex(), QModelIndex());

    Python:self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())

    这应该会更新树中的所有项目。

    【讨论】:

      猜你喜欢
      • 2011-05-27
      • 1970-01-01
      • 2014-05-14
      • 1970-01-01
      • 2011-10-23
      • 2015-02-28
      • 2011-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多