Динамически заполняйте QTreeView, чтобы приложение не зависало - PullRequest
0 голосов
/ 13 сентября 2018

Я хочу показать древовидную структуру организации узлов на сервере OPC. Я использую PyQt5 и opcua для достижения этой цели. Чтобы попробовать код, я использовал Prosys OPC UA Simulation Server , и он отлично работает. Это выглядит так:

enter image description here

Однако, когда я попытался использовать код с реальными общедоступными серверами , приложение вылетало. Я полагаю, что это происходит потому, что на реальных серверах количество существующих узлов огромно, и приложению приходится много обрабатывать.

Так что мне было интересно, есть ли способ заполнить дерево «по требованию». А именно, ищите дочерние узлы только тогда, когда пользователь нажимает на узел, чтобы только узлы, которые должны быть на экране, присутствовали в качестве элементов в объекте 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_())

1 Ответ

0 голосов
/ 14 сентября 2018

Мне удалось найти решение, которое работает, но я думаю, что оно далеко не лучшее.Это код:

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_())

Комментарии относительно идентификаторов узлов не имеют отношения к дереву в PyQt, они просто относятся к проблемам форматирования, связанным с библиотекой opcua.

Что я думаюМожно (и нужно) улучшить тот факт, что каждый раз, когда элемент раскрывается, все его дочерние элементы удаляются с помощью item.takeChildren(), чтобы добавить их позже при вызове метода add_node_to_tree().Я знаю, что это неправильно, но я не мог найти другой способ сделать это.

...