Completed
Push — master ( 032a8d...911c72 )
by Olivier
01:04
created

TreeWidget.update_browse_name_current_item()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
c 1
b 0
f 1
dl 0
loc 5
rs 9.4285
1
from PyQt5.QtCore import pyqtSignal, QMimeData, QObject
2
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon
3
from PyQt5.QtWidgets import QApplication, QAbstractItemView
4
5
from opcua import ua
6
from opcua import Node
7
8
9
class TreeWidget(QObject):
10
11
    error = pyqtSignal(str)
12
13
    def __init__(self, view):
14
        QObject.__init__(self, view)
15
        self.view = view
16
        self.model = TreeViewModel()
17
        self.model.clear()  # FIXME: do we need this?
18
        #self.model.error.connect(lambda x: self.error.emit(x))
19
        self.model.error.connect(self.error)
20
        self.view.setModel(self.model)
21
        #self.view.setUniformRowHeights(True)
22
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
23
        self.view.header().setSectionResizeMode(3)
24
        self.view.header().setStretchLastSection(True)
25
        #self.view.resizeColumnsToContents()
26
27
    def clear(self):
28
        self.model.clear()
29
30
    def set_root_node(self, node):
31
        self.model.clear()
32
        self.model.set_root_node(node)
33
34
    def copy_path(self):
35
        path = self.get_current_path()
36
        path_str = ",".join(path)
37
        QApplication.clipboard().setText(path_str)
38
39
    def copy_nodeid(self):
40
        node = self.get_current_node()
41
        text = node.nodeid.to_string()
42
        QApplication.clipboard().setText(text)
43
44
    def get_current_path(self):
45
        idx = self.view.currentIndex()
46
        idx = idx.sibling(idx.row(), 0)
47
        it = self.model.itemFromIndex(idx)
48
        path = []
49
        while it and it.data():
50
            node = it.data()
51
            name = node.get_browse_name().to_string()
52
            path.insert(0, name)
53
            it = it.parent()
54
        return path
55
56
    def update_browse_name_current_item(self, bname):
57
        idx = self.view.currentIndex()
58
        idx = idx.sibling(idx.row(), 1)
59
        it = self.model.itemFromIndex(idx)
60
        it.setText(bname.to_string())
61
62
    def update_display_name_current_item(self, dname):
63
        idx = self.view.currentIndex()
64
        idx = idx.sibling(idx.row(), 0)
65
        it = self.model.itemFromIndex(idx)
66
        it.setText(dname.Text.decode())
67
68
    def reload_current(self):
69
        idx = self.view.currentIndex()
70
        idx = idx.sibling(idx.row(), 0)
71
        it = self.model.itemFromIndex(idx)
72
        if not it:
73
            return None
74
        self.reload(it)
75
76
    def reload(self, item=None):
77
        if item is None:
78
            item = self.model.item(0, 0)
79
        for _ in range(item.rowCount()):
80
            child_it = item.child(0, 0)
81
            node = child_it.data()
82
            if node:
83
                self.model.reload(node)
84
            item.takeRow(0)
85
        node = item.data()
86
        if node:
87
            self.model.reload(node)
88
89
    def remove_current_item(self):
90
        idx = self.view.currentIndex()
91
        self.model.removeRow(idx.row(), idx.parent())
92
93
    def get_current_node(self, idx=None):
94
        if idx is None:
95
            idx = self.view.currentIndex()
96
        idx = idx.sibling(idx.row(), 0)
97
        it = self.model.itemFromIndex(idx)
98
        if not it:
99
            self.error.emit("No node selected")
100
            raise RuntimeError("No Node Selected")
101
        node = it.data()
102
        if not node:
103
            self.error.emit("Item does not contain node data, report!")
104
            raise RuntimeError("Item does not contain node data, report!")
105
        return node
106
107
108
class TreeViewModel(QStandardItemModel):
109
110
    error = pyqtSignal(str)
111
112
    def __init__(self):
113
        super(TreeViewModel, self).__init__()
114
        self._fetched = []
115
116
    def clear(self):
117
        QStandardItemModel.clear(self)
118
        self._fetched = []
119
        self.setHorizontalHeaderLabels(['DisplayName', "BrowseName", 'NodeId'])
120
121
    def set_root_node(self, node):
122
        desc = self._get_node_desc(node)
123
        self.add_item(desc, node=node)
124
125
    def _get_node_desc(self, node):
126
        attrs = node.get_attributes([ua.AttributeIds.DisplayName, ua.AttributeIds.BrowseName, ua.AttributeIds.NodeId, ua.AttributeIds.NodeClass])
127
        desc = ua.ReferenceDescription()
128
        desc.DisplayName = attrs[0].Value.Value
129
        desc.BrowseName = attrs[1].Value.Value
130
        desc.NodeId = attrs[2].Value.Value
131
        desc.NodeClass = attrs[3].Value.Value
132
        desc.TypeDefinition = ua.TwoByteNodeId(ua.ObjectIds.FolderType)
133
        return desc
134
135
    def add_item(self, desc, parent=None, node=None):
136
        item = [QStandardItem(desc.DisplayName.to_string()), QStandardItem(desc.BrowseName.to_string()), QStandardItem(desc.NodeId.to_string())]
137
        if desc.NodeClass == ua.NodeClass.Object:
138
            if desc.TypeDefinition == ua.TwoByteNodeId(ua.ObjectIds.FolderType):
139
                item[0].setIcon(QIcon(":/folder.svg"))
140
            else:
141
                item[0].setIcon(QIcon(":/object.svg"))
142
        elif desc.NodeClass == ua.NodeClass.Variable:
143
            if desc.TypeDefinition == ua.TwoByteNodeId(ua.ObjectIds.PropertyType):
144
                item[0].setIcon(QIcon(":/property.svg"))
145
            else:
146
                item[0].setIcon(QIcon(":/variable.svg"))
147
        elif desc.NodeClass == ua.NodeClass.Method:
148
            item[0].setIcon(QIcon(":/method.svg"))
149
        elif desc.NodeClass == ua.NodeClass.ObjectType:
150
            item[0].setIcon(QIcon(":/object_type.svg"))
151
        elif desc.NodeClass == ua.NodeClass.VariableType:
152
            item[0].setIcon(QIcon(":/variable_type.svg"))
153
        elif desc.NodeClass == ua.NodeClass.DataType:
154
            item[0].setIcon(QIcon(":/data_type.svg"))
155
        elif desc.NodeClass == ua.NodeClass.ReferenceType:
156
            item[0].setIcon(QIcon(":/reference_type.svg"))
157
        if node:
158
            item[0].setData(node)
159
        else:
160
            parent_node = parent.data()
161
            item[0].setData(Node(parent_node.server, desc.NodeId))
162
        if parent:
163
            return parent.appendRow(item)
164
        else:
165
            return self.appendRow(item)
166
167
    def reload(self, node):
168
        if node in self._fetched:
169
            self._fetched.remove(node)
170
171
    def canFetchMore(self, idx):
172
        item = self.itemFromIndex(idx)
173
        if not item:
174
            return True
175
        node = item.data()
176
        if node not in self._fetched:
177
            self._fetched.append(node)
178
            return True
179
        return False
180
181
    def hasChildren(self, idx):
182
        item = self.itemFromIndex(idx)
183
        if not item:
184
            return True
185
        node = item.data()
186
        if node in self._fetched:
187
            return QStandardItemModel.hasChildren(self, idx)
188
        return True
189
190
    def fetchMore(self, idx):
191
        parent = self.itemFromIndex(idx)
192
        if parent:
193
            self._fetchMore(parent)
194
195
    def _fetchMore(self, parent):
196
        try:
197
            descs = parent.data().get_children_descriptions()
198
            descs.sort(key=lambda x: x.BrowseName)
199
            for desc in descs:
200
                self.add_item(desc, parent)
201
        except Exception as ex:
202
            self.error.emit(ex)
203
            raise
204
205
    def mimeData(self, idxs):
206
        mdata = QMimeData()
207
        nodes = []
208
        for idx in idxs:
209
            item = self.itemFromIndex(idx)
210
            if item:
211
                node = item.data()
212
                if node:
213
                    nodes.append(node.nodeid.to_string())
214
        mdata.setText(", ".join(nodes))
215
        return mdata
216
217
218