QTreeView实现层级选中联动效果

from PySide2.QtCore import Qt, QModelIndex, QAbstractItemModel
from PySide2.QtGui import QColor, QBrush
from PySide2.QtWidgets import QApplication, QDialog, QHBoxLayout, QTreeView, QVBoxLayout, QWidget, QLabel

import canmatrix


class MyModel(QAbstractItemModel):
    def __init__(self, db):
        super().__init__()
        self.db = db
        self.check_state = {}

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

        if role == Qt.DisplayRole:
            if index.column() == 0:
                node = index.internalPointer()
                return node.name

        if role == Qt.CheckStateRole:
            if index.column() == 0:
                node = index.internalPointer()
                if node in self.check_state:
                    return self.check_state[node]
                else:
                    return Qt.Unchecked

        return None

    def setData(self, index, value, role):
        if not index.isValid():
            return False

        if role == Qt.CheckStateRole:
            if index.column() == 0:
                node = index.internalPointer()
                self.check_state[node] = value
                self.update_children(node, value)
                self.update_parent(node)
                self.dataChanged.emit(index, index)
                return True

        return False

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags

        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            if section == 0:
                return "Name"

        return None

    def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
            return QModelIndex()

        if not parent.isValid():
            node = self.db.nodes[row]
            return self.createIndex(row, column, node)

        parent_node = parent.internalPointer()
        if isinstance(parent_node, canmatrix.CanNode):
            if column == 0:
                message = parent_node.transmitters[row]
                return self.createIndex(row, column, message)
        elif isinstance(parent_node, canmatrix.CanMessage):
            if column == 0:
                signal = parent_node
            	return self.createIndex(row, column, signal)

        return QModelIndex()

    def parent(self, index):
        if not index.isValid():
            return QModelIndex()

        node = index.internalPointer()
        if isinstance(node, canmatrix.CanMessage):
            return self.createIndex(self.db.nodes.index(node.transmitters[0]), 0, self.db.nodes[0])
        elif isinstance(node, canmatrix.CanSignal):
            return self.createIndex(node.parent_frame.frame_id - 1, 0, self.db.nodes[0])

        return QModelIndex()

    def rowCount(self, parent):
        if not parent.isValid():
            return len(self.db.nodes)

        node = parent.internalPointer()
        if isinstance(node, canmatrix.CanNode):
            return len(node.transmitters)
        elif isinstance(node, canmatrix.CanMessage):
            return len(node.signals)

        return 0

    def columnCount(self, parent):
        return 1

    def update_children(self, node, value):
        if isinstance(node, canmatrix.CanNode):
            for message in node.transmitters:
                self.check_state[message] = value
                for signal in message.signals:
                    self.check_state[signal] = value
                    self.update_children(signal, value)

    def update_parent(self, node):
        if isinstance(node, canmatrix.CanSignal):
            parent = node.parent_frame
            if parent in self.check_state:
                if all(signal in self.check_state and self.check_state[signal] == Qt.Checked for signal in parent.signals):
                    self.check_state[parent] = Qt.Checked
                elif any(signal in self.check_state and self.check_state[signal] == Qt.Checked for signal in parent.signals):
                    self.check_state[parent] = Qt.PartiallyChecked
                else:
                    self.check_state[parent] = Qt.Unchecked
                self.update_parent(parent)

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

        return index.internalPointer()


class MyWindow(QDialog):
    def __init__(self, db):
        super().__init__()

        self.db = db
        self.model = MyModel(self.db)

        hbox = QHBoxLayout()

        self.tree_view = QTreeView()
        self.tree_view.setModel(self.model)
        self.tree_view.setSortingEnabled(True)
        self.tree_view.setHeaderHidden(False)
        self.tree_view.setSelectionMode(QTreeView.ExtendedSelection)
        self.tree_view.selectionModel().selectionChanged.connect(self.selection_changed)

        hbox.addWidget(self.tree_view)

        self.info_label = QLabel()
        hbox.addWidget(self.info_label)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox)
        self.setLayout(vbox)

    def selection_changed(self, selected, deselected):
        for index in selected.indexes():
            node = self.model.getNode(index)
            if node:
                self.info_label.setText(node.__str__())


if __name__ == "__main__":
    app = QApplication([])
    db = canmatrix.formats.loadp('example.dbc')[0]
    window = MyWindow(db)
    window.show()
    app.exec_()

在这个示例中,我们实现了一个自定义的数据模型(MyModel),它从一个canmatrix.CanMatrix对象中读取数据并将其显示在QTreeView上。在MyModel中,我们为每个节点添加了一个布尔属性check_state,用于记录该节点的选中状态。在处理选中项更改的时候,我们使用update_children()和update_parent()方法更新选中状态,并使用setData()方法更新TreeView上的项。

在MyWindow中,我们创建了一个QTreeView对象,并将其设置为我们自定义的数据模型(MyModel)。我们还创建了一个info_label QLabel对象,用于显示选中节点的详细信息。

最后,我们将selectionChanged信号连接到selection_changed()槽函数,该函数在选中项更改时更新info_label的文本。

在TreeView中,我们为节点、消息和信号添加了自定义的setData()方法,用于更新其选中状态。在MyModel中,我们还重载了data()方法,以便我们可以为TreeView中的每个项提供不同的文本和显示样式。

最后,我们还实现了父节点和子节点之间的联动效果。在update_children()方法中,我们使用递归方式更新所有子节点的选中状态。在update_parent()方法中,我们使用递归方式更新父节点的选中状态。

通过这个示例,我们展示了如何使用PySide的MVC模式来创建一个高度定制的TreeView,并展示了如何实现选中项联动效果。您可以根据自己的需求进一步扩展这个示例,并添加更多功能。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值