动态填充QTreeView,以便应用程序不会崩溃

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态填充QTreeView,以便应用程序不会崩溃相关的知识,希望对你有一定的参考价值。

我想显示OPC服务器中节点的组织方式的树视图。我正在使用PyQt5opcua来实现这一目标。为了尝试代码,我使用Prosys OPC UA Simulation Server,它工作正常。它看起来像这样:

enter image description here

但是,当我尝试使用real publicly available servers的代码时,应用程序崩溃了。我相信这种情况正在发生,因为在真实服务器中,现有节点的数量巨大,而且应用程序还有很多需要处理的。

所以我想知道是否有办法按需填充树。即,仅在用户单击节点时查找子节点,以便只有屏幕上的节点作为QTreeWidget对象中的项目存在。我不知道这是否可行,因为目前我正在一次性完全填充树,而不仅仅是当用户要求查看节点的子节点时。

这是我目前的代码:

import sys
from opcua import Client
from PyQt5 import QtWidgets, QtGui

class Window(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)        
        self.node_tree = QtWidgets.QTreeWidget()
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.node_tree)
        self.setLayout(layout)

        self.client = Client('opc.tcp://127.0.0.1:53530/UA/Sim')
        self.client.connect()
        self.root = self.client.get_root_node()
        self.fill_tree(self.node_tree, self.root)

    def fill_tree(self, group, node):
        item = QtWidgets.QTreeWidgetItem(group, [str(node)])
        children = node.get_children()
        variables = node.get_variables()
        for child in children:
            if child in variables:
                QtWidgets.QTreeWidgetItem(item, [str(child) + ' (variable)'])
            else:
                self.fill_tree(item, child)

    def closeEvent(self,event):
        self.client.disconnect()
        sys.exit(0)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = Window()
    ex.show()
    sys.exit(app.exec_())
答案

我设法找到一个有效的解决方案,但我认为它远非最好的解决方案。这是代码:

import sys
from opcua import Client
from PyQt5 import QtWidgets, QtGui

class Window(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)        
        self.node_tree = QtWidgets.QTreeWidget()
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.node_tree)
        self.setLayout(layout)

        self.client = Client('opc.tcp://opcuaserver.com:48010')
        self.client.connect()
        self.root = self.client.get_root_node()
        self.add_node_to_tree(self.node_tree, self.root)
        self.node_tree.itemExpanded.connect(self.get_node_from_tree_item)

    def get_node_from_tree_item(self, item):
        ## Get the NodeId as a string
        aux = item.text(0)
        start = aux.find('NodeId(') + len('NodeId(')
        end = aux.find(')')
        node_str = str(aux[start:end])
        ## If the ID is defined as an 'i=', then what comes after the ';'
        ## should be ignored to access the node
        if (node_str[:2] == 'i=') & (node_str.find(';') != -1):
            new_end = node_str.find(';')
            node_str = node_str[:new_end]
        node = self.client.get_node(node_str)
        children = node.get_children()
        variables = node.get_variables()
        item.takeChildren()
        for child in children:
            if child in variables:
                self.add_node_to_tree(item, child, True)
            else:
                self.add_node_to_tree(item, child, False)

    def add_node_to_tree(self, group, node, var=False):
        item = QtWidgets.QTreeWidgetItem(group, [str(node) + var*' (variable)'])
        children = node.get_children()
        for child in children:
                QtWidgets.QTreeWidgetItem(item, [str(child)])

    def closeEvent(self,event):
        self.client.disconnect()
        sys.exit(0)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = Window()
    ex.show()
    sys.exit(app.exec_())

有关节点ID的注释与PyQt中的树无关,它们只是与opcua库相关的格式化问题。

我认为可以(并且应该)改进的事实是,每次扩展一个项目时,所有的子项都会被item.takeChildren()删除,以便在调用add_node_to_tree()方法时将其添加回来。我知道这是不对的,但我找不到另一种方法来做到这一点。

以上是关于动态填充QTreeView,以便应用程序不会崩溃的主要内容,如果未能解决你的问题,请参考以下文章

QTableWidget 作为 QTreeView 中的子级

QTreeView根据内容动态高度,显示三角形

隐藏动态框架的符号化崩溃日志

从 ModelIndex 扩展 QTreeView 项目 [重复]

我需要插入自定义代码,以便 Clickfunnels 不会自动填充页面,如果他们之前已经在页面上

以编程方式检查QAbstractItemModel / QTreeView中的项目