RefsWidget.add_ref()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
1
import logging
2
from copy import copy
3
4
from PyQt5.QtCore import pyqtSignal, QObject, QSettings, Qt
5
from PyQt5.QtGui import QStandardItemModel, QStandardItem
6
from PyQt5.QtWidgets import QMenu, QAction, QStyledItemDelegate, QAbstractItemView
7
8
from opcua import ua, Node
9
10
from uawidgets.utils import trycatchslot
11
from uawidgets.get_node_dialog import GetNodeTextButton
12
13
14
logger = logging.getLogger(__name__)
15
16
17
class RefsWidget(QObject):
18
19
    error = pyqtSignal(Exception)
20
    reference_changed = pyqtSignal(Node)
21
22
    def __init__(self, view):
23
        self.view = view
24
        QObject.__init__(self, view)
25
        self.model = QStandardItemModel()
26
27
        delegate = MyDelegate(self.view, self)
28
        delegate.error.connect(self.error.emit)
29
        delegate.reference_changed.connect(self.reference_changed.emit)
30
        self.view.setEditTriggers(QAbstractItemView.DoubleClicked)
31
        self.view.setModel(self.model)
32
        self.view.setItemDelegate(delegate)
33
        self.settings = QSettings()
34
        self.model.setHorizontalHeaderLabels(['ReferenceType', 'NodeId', "BrowseName", "TypeDefinition"])
35
        state = self.settings.value("WindowState/refs_widget_state", None)
36
        if state is not None:
37
            self.view.horizontalHeader().restoreState(state)
38
        self.view.horizontalHeader().setSectionResizeMode(0)
39
        self.view.horizontalHeader().setStretchLastSection(True)
40
        self.node = None
41
42
        self.reloadAction = QAction("Reload", self.model)
43
        self.reloadAction.triggered.connect(self.reload)
44
        self.addRefAction = QAction("Add Reference", self.model)
45
        self.addRefAction.triggered.connect(self.add_ref)
46
        self.removeRefAction = QAction("Remove Reference", self.model)
47
        self.removeRefAction.triggered.connect(self.remove_ref)
48
49
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
50
        self.view.customContextMenuRequested.connect(self.showContextMenu)
51
        self._contextMenu = QMenu()
52
        self._contextMenu.addAction(self.reloadAction)
53
        self._contextMenu.addSeparator()
54
        self._contextMenu.addAction(self.addRefAction)
55
        self._contextMenu.addAction(self.removeRefAction)
56
57
    def showContextMenu(self, position):
58
        if not self.node:
59
            return
60
        self.removeRefAction.setEnabled(False)
61
        idx = self.view.currentIndex()
62
        if idx.isValid():
63
            self.removeRefAction.setEnabled(True)
64
        self._contextMenu.exec_(self.view.viewport().mapToGlobal(position))
65
66
    def clear(self):
67
        # remove all rows but not header!!
68
        self.model.removeRows(0, self.model.rowCount())
69
        self.node = None
70
71
    def _make_default_ref(self):
72
        #FIXME: remeber last choosen values or use values that make sense
73
        ref = ua.ReferenceDescription()
74
        return ref
75
76
    @trycatchslot
77
    def add_ref(self):
78
        ref = self._make_default_ref()
79
        logger.info("Adding ref: %s", ref)
80
        self._add_ref_row(ref)
81
        idx = self.model.index(self.model.rowCount() - 1, 0)
82
        self.view.setCurrentIndex(idx)
83
        #self.view.edit(idx)
84
85
    @trycatchslot
86
    def reload(self):
87
        node = self.node
88
        self.clear()
89
        self.show_refs(node)
90
91
    @trycatchslot
92
    def remove_ref(self):
93
        idx = self.view.currentIndex()
94
        if not idx.isValid():
95
            logger.warning("No valid reference selected to remove")
96
        idx = idx.sibling(idx.row(), 0)
97
        item = self.model.itemFromIndex(idx)
98
        ref = item.data(Qt.UserRole)
99
        self.do_remove_ref(ref)
100
        self.reload()
101
    
102
    def do_remove_ref(self, ref, check=True):
103
        logger.info("Removing: %s", ref)
104
        it = ua.DeleteReferencesItem()
105
        it.SourceNodeId = self.node.nodeid
106
        it.ReferenceTypeId = ref.ReferenceTypeId
107
        it.IsForward = ref.IsForward
108
        it.TargetNodeId = ref.NodeId
109
        it.DeleteBidirectional = False
110
        #param = ua.DeleteReferencesParameters()
111
        #param.ReferencesToDelete.append(it)
112
        results = self.node.server.delete_references([it])
113
        logger.info("Remove result: %s", results[0]) 
114
        if check:
115
            results[0].check()
116
117
    def save_state(self):
118
        self.settings.setValue("WindowState/refs_widget_state", self.view.horizontalHeader().saveState())
119
120
    def show_refs(self, node):
121
        self.clear()
122
        self.node = node
123
        self._show_refs(node)
124
125
    def _show_refs(self, node):
126
        try:
127
            refs = node.get_children_descriptions(refs=ua.ObjectIds.References)
128
        except Exception as ex:
129
            self.error.emit(ex)
130
            raise
131
        for ref in refs:
132
            self._add_ref_row(ref)
133
134
    def _add_ref_row(self, ref):
135
        if ref.ReferenceTypeId.Identifier in ua.ObjectIdNames:
136
            typename = ua.ObjectIdNames[ref.ReferenceTypeId.Identifier]
137
        else:
138
            typename = str(ref.ReferenceTypeId)
139
        if ref.NodeId.NamespaceIndex == 0 and ref.NodeId.Identifier in ua.ObjectIdNames:
140
            nodeid = ua.ObjectIdNames[ref.NodeId.Identifier]
141
        else:
142
            nodeid = ref.NodeId.to_string()
143
        if ref.TypeDefinition.Identifier in ua.ObjectIdNames:
144
            typedef = ua.ObjectIdNames[ref.TypeDefinition.Identifier]
145
        else:
146
            typedef = ref.TypeDefinition.to_string()
147
        titem = QStandardItem(typename)
148
        titem.setData(ref, Qt.UserRole)
149
        self.model.appendRow([
150
            titem,
151
            QStandardItem(nodeid),
152
            QStandardItem(ref.BrowseName.to_string()),
153
            QStandardItem(typedef)
154
        ])
155
156
157
class MyDelegate(QStyledItemDelegate):
158
159
    error = pyqtSignal(Exception)
160
    reference_changed = pyqtSignal(Node)
161
162
    def __init__(self, parent, widget):
163
        QStyledItemDelegate.__init__(self, parent)
164
        self._widget = widget
165
166
    @trycatchslot
167
    def createEditor(self, parent, option, idx):
168
        if idx.column() > 1:
169
            return None
170
        data_idx = idx.sibling(idx.row(), 0)
171
        item = self._widget.model.itemFromIndex(data_idx)
172
        ref = item.data(Qt.UserRole)
173
        if idx.column() == 1:
174
            node = Node(self._widget.node.server, ref.NodeId)
175
            startnode = Node(self._widget.node.server, ua.ObjectIds.RootFolder)
176
            button = GetNodeTextButton(parent, node, startnode)
177
            return button
178
        elif idx.column() == 0:
179
            node = Node(self._widget.node.server, ref.ReferenceTypeId)
180
            startnode = Node(self._widget.node.server, ua.ObjectIds.ReferenceTypesFolder)
181
            button = GetNodeTextButton(parent, node, startnode)
182
            return button
183
184
    @trycatchslot
185
    def setModelData(self, editor, model, idx):
186
        data_idx = idx.sibling(idx.row(), 0)
187
        ref = model.data(data_idx, Qt.UserRole)
188
        self._widget.do_remove_ref(ref, check=False)
189
        if idx.column() == 0:
190
            ref.ReferenceTypeId = editor.get_node().nodeid
191
            model.setData(idx, ref.ReferenceTypeId.to_string(), Qt.DisplayRole)
192
        elif idx.column() == 1:
193
            ref.NodeId = editor.get_node().nodeid
194
            ref.NodeClass = editor.get_node().get_node_class()
195
            model.setData(idx, ref.NodeId.to_string(), Qt.DisplayRole)
196
        model.setData(data_idx, ref, Qt.UserRole)
197
        if ref.NodeId.is_null() or ref.ReferenceTypeId.is_null():
198
            logger.info("Do not save yet. Need NodeId and ReferenceTypeId to be set")
199
            return
200
        self._write_ref(ref)
201
202
    def _write_ref(self, ref):
203
        logger.info("Writing ref %s", ref)
204
        it = ua.AddReferencesItem()
205
        it.SourceNodeId = self._widget.node.nodeid
206
        it.ReferenceTypeId = ref.ReferenceTypeId
207
        it.IsForward = ref.IsForward
208
        it.TargetNodeId = ref.NodeId
209
        it.TargetNodeClass = ref.NodeClass
210
211
        results = self._widget.node.server.add_references([it])
212
        results[0].check()
213
214
        self.reference_changed.emit(self._widget.node)
215
        self._widget.reload()
216
217
218
219
220