动态填充 QTreeView 以便应用程序不会崩溃
Dynamically fill QTreeView so that app doesn't crash
我想展示 OPC 服务器中的节点是如何组织的树视图。我正在使用 PyQt5
和 opcua
来实现这一点。为了尝试代码,我使用了 Prosys OPC UA Simulation Server,它运行良好。它看起来像这样:
但是,当我尝试将代码与 real publicly available servers 一起使用时,应用程序崩溃了。我相信这种情况正在发生,因为在真实的服务器中,现有节点的数量是巨大的,然后应用程序有很多要处理的。
所以我想知道是否有办法填充树'on-demand'。即,仅当用户单击节点时才查找子节点,以便只有应显示在屏幕上的节点才会作为项目出现在 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()
方法时将它们添加回来.我知道这是不对的,但我找不到其他方法。
我想展示 OPC 服务器中的节点是如何组织的树视图。我正在使用 PyQt5
和 opcua
来实现这一点。为了尝试代码,我使用了 Prosys OPC UA Simulation Server,它运行良好。它看起来像这样:
但是,当我尝试将代码与 real publicly available servers 一起使用时,应用程序崩溃了。我相信这种情况正在发生,因为在真实的服务器中,现有节点的数量是巨大的,然后应用程序有很多要处理的。
所以我想知道是否有办法填充树'on-demand'。即,仅当用户单击节点时才查找子节点,以便只有应显示在屏幕上的节点才会作为项目出现在 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()
方法时将它们添加回来.我知道这是不对的,但我找不到其他方法。