Completed
Push — master ( fb14b4...0d6791 )
by Olivier
01:00
created

RefsWidget._show_refs()   A

Complexity

Conditions 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 3
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 GetNodeButton
12
13
14
logger = logging.getLogger(__name__)
15
16
17
class RefsWidget(QObject):
18
19
    error = pyqtSignal(Exception)
20
    reference_changed = pyqtSignal(ua.NodeId)
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
        self.view.horizontalHeader().setSectionResizeMode(0)
36
        self.view.horizontalHeader().setStretchLastSection(True)
37
        state = self.settings.value("refs_widget", None)
38
        self.node = None
39
        if state is not None:
40
            self.view.horizontalHeader().restoreState(state)
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 View Code Duplication
    def do_remove_ref(self, ref, check=True):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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("refs_widget", 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
        typename = ua.ObjectIdNames[ref.ReferenceTypeId.Identifier]
136
        if ref.NodeId.NamespaceIndex == 0 and ref.NodeId.Identifier in ua.ObjectIdNames:
137
            nodeid = ua.ObjectIdNames[ref.NodeId.Identifier]
138
        else:
139
            nodeid = ref.NodeId.to_string()
140
        if ref.TypeDefinition.Identifier in ua.ObjectIdNames:
141
            typedef = ua.ObjectIdNames[ref.TypeDefinition.Identifier]
142
        else:
143
            typedef = ref.TypeDefinition.to_string()
144
        titem = QStandardItem(typename)
145
        titem.setData(ref, Qt.UserRole)
146
        self.model.appendRow([
147
            titem,
148
            QStandardItem(nodeid),
149
            QStandardItem(ref.BrowseName.to_string()),
150
            QStandardItem(typedef)
151
        ])
152
153
154
class MyDelegate(QStyledItemDelegate):
155
156
    error = pyqtSignal(Exception)
157
    reference_changed = pyqtSignal(ua.NodeId)
158
159
    def __init__(self, parent, widget):
160
        QStyledItemDelegate.__init__(self, parent)
161
        self._widget = widget
162
163
    @trycatchslot
164
    def createEditor(self, parent, option, idx):
165
        print("CREATE EDITOR", parent, option, idx)
166
        if idx.column() > 1:
167
            return None
168
        data_idx = idx.sibling(idx.row(), 0)
169
        item = self._widget.model.itemFromIndex(data_idx)
170
        ref = item.data(Qt.UserRole)
171
        if idx.column() == 1:
172
            node = Node(self._widget.node.server, ref.NodeId)
173
            startnode = Node(self._widget.node.server, ua.ObjectIds.RootFolder)
174
            button = GetNodeButton(parent, node, startnode)
175
            return button
176
        elif idx.column() == 0:
177
            node = Node(self._widget.node.server, ref.ReferenceTypeId)
178
            startnode = Node(self._widget.node.server, ua.ObjectIds.ReferenceTypesFolder)
179
            button = GetNodeButton(parent, node, startnode)
180
            return button
181
182
    @trycatchslot
183
    def setModelData(self, editor, model, idx):
184
        data_idx = idx.sibling(idx.row(), 0)
185
        ref = model.data(data_idx, Qt.UserRole)
186
        self._widget.do_remove_ref(ref, check=False)
187
        if idx.column() == 0:
188
            ref.ReferenceTypeId = editor.current_node.nodeid
189
            model.setData(idx, ref.ReferenceTypeId.to_string(), Qt.DisplayRole)
190
        elif idx.column() == 1:
191
            ref.NodeId = editor.current_node.nodeid
192
            ref.NodeClass = editor.current_node.get_node_class()
193
            model.setData(idx, ref.NodeId.to_string(), Qt.DisplayRole)
194
        model.setData(data_idx, ref, Qt.UserRole)
195
        if ref.NodeId.is_null() or ref.ReferenceTypeId.is_null():
196
            logger.info("Do not save yet. Need NodeId and ReferenceTypeId to be set")
197
            return
198
        self._write_ref(ref)
199
200 View Code Duplication
    def _write_ref(self, ref):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
201
        logger.info("Writing ref %s", ref)
202
        it = ua.AddReferencesItem()
203
        it.SourceNodeId = self._widget.node.nodeid
204
        it.ReferenceTypeId = ref.ReferenceTypeId
205
        it.IsForward = ref.IsForward
206
        it.TargetNodeId = ref.NodeId
207
        it.TargetNodeClass = ref.NodeClass
208
209
        results = self._widget.node.server.add_references([it])
210
        results[0].check()
211
212
        self._widget.reload()
213
214
215
216
217