Completed
Pull Request — master (#8)
by Olivier
01:04
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
3
from PyQt5.QtCore import pyqtSignal, QObject, QSettings, Qt
4
from PyQt5.QtGui import QStandardItemModel, QStandardItem
5
from PyQt5.QtWidgets import QMenu, QAction, QStyledItemDelegate, QAbstractItemView
6
7
from opcua import ua, Node
8
9
from uawidgets.utils import trycatchslot
10
from uawidgets.get_node_dialog import GetNodeButton
11
12
13
logger = logging.getLogger(__name__)
14
15
16
class RefsWidget(QObject):
17
18
    error = pyqtSignal(Exception)
19
    reference_changed = pyqtSignal(ua.NodeId)
20
21
    def __init__(self, view):
22
        self.view = view
23
        QObject.__init__(self, view)
24
        self.model = QStandardItemModel()
25
26
        delegate = MyDelegate(self.view, self)
27
        delegate.error.connect(self.error.emit)
28
        delegate.reference_changed.connect(self.reference_changed.emit)
29
        self.view.setEditTriggers(QAbstractItemView.DoubleClicked)
30
        self.view.setModel(self.model)
31
        self.view.setItemDelegate(delegate)
32
        self.settings = QSettings()
33
        self.model.setHorizontalHeaderLabels(['ReferenceType', 'NodeId', "BrowseName", "TypeDefinition"])
34
        self.view.horizontalHeader().setSectionResizeMode(0)
35
        self.view.horizontalHeader().setStretchLastSection(True)
36
        state = self.settings.value("refs_widget", None)
37
        self.node = None
38
        if state is not None:
39
            self.view.horizontalHeader().restoreState(state)
40
41
        self.reloadAction = QAction("Reload", self.model)
42
        self.reloadAction.triggered.connect(self.reload)
43
        self.addRefAction = QAction("Add Reference", self.model)
44
        self.addRefAction.triggered.connect(self.add_ref)
45
        self.removeRefAction = QAction("Remove Reference", self.model)
46
        self.removeRefAction.triggered.connect(self.remove_ref)
47
48
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
49
        self.view.customContextMenuRequested.connect(self.showContextMenu)
50
        self._contextMenu = QMenu()
51
        self._contextMenu.addAction(self.reloadAction)
52
        self._contextMenu.addSeparator()
53
        self._contextMenu.addAction(self.addRefAction)
54
        self._contextMenu.addAction(self.removeRefAction)
55
56
    def showContextMenu(self, position):
57
        if not self.node:
58
            return
59
        self.removeRefAction.setEnabled(False)
60
        idx = self.view.currentIndex()
61
        if idx.isValid():
62
            self.removeRefAction.setEnabled(True)
63
        self._contextMenu.exec_(self.view.viewport().mapToGlobal(position))
64
65
    def clear(self):
66
        # remove all rows but not header!!
67
        self.model.removeRows(0, self.model.rowCount())
68
        self.node = None
69
70
    def _make_default_ref(self):
71
        #FIXME: remeber last choosen values or use values that make sense
72
        ref = ua.ReferenceDescription()
73
        return ref
74
75
    @trycatchslot
76
    def add_ref(self):
77
        ref = self._make_default_ref()
78
        logger.info("Adding ref: %s", ref)
79
        self._add_ref_row(ref)
80
        idx = self.model.index(self.model.rowCount() - 1, 0)
81
        self.view.setCurrentIndex(idx)
82
        #self.view.edit(idx)
83
84
    @trycatchslot
85
    def reload(self):
86
        node = self.node
87
        self.clear()
88
        self.show_refs(node)
89
90
    @trycatchslot
91
    def remove_ref(self):
92
        idx = self.view.currentIndex()
93
        if not idx.isValid():
94
            logger.warning("No valid reference selected to remove")
95
        idx = idx.sibling(idx.row(), 0)
96
        item = self.model.itemFromIndex(idx)
97
        ref = item.data(Qt.UserRole)
98
        self._remove_ref(ref)
99
        self.reload()
100
    
101
    def _remove_ref(self, ref):
102
        logger.info("Removing: %s", ref)
103
        it = ua.DeleteReferencesItem()
104
        it.SourceNodeId = self.node.nodeid
105
        it.ReferenceTypeId = ref.ReferenceTypeId
106
        it.IsForward = ref.IsForward
107
        it.TargetNodeId = ref.NodeId
108
        it.DeleteBidirectional = False
109
        #param = ua.DeleteReferencesParameters()
110
        #param.ReferencesToDelete.append(it)
111
        results = self.node.server.delete_references([it])
112
        results[0].check()
113
114
    def save_state(self):
115
        self.settings.setValue("refs_widget", self.view.horizontalHeader().saveState())
116
117
    def show_refs(self, node):
118
        self.clear()
119
        self.node = node
120
        self._show_refs(node)
121
122
    def _show_refs(self, node):
123
        try:
124
            refs = node.get_children_descriptions(refs=ua.ObjectIds.References)
125
        except Exception as ex:
126
            self.error.emit(ex)
127
            raise
128
        for ref in refs:
129
            self._add_ref_row(ref)
130
131
    def _add_ref_row(self, ref):
132
        typename = ua.ObjectIdNames[ref.ReferenceTypeId.Identifier]
133
        if ref.NodeId.NamespaceIndex == 0 and ref.NodeId.Identifier in ua.ObjectIdNames:
134
            nodeid = ua.ObjectIdNames[ref.NodeId.Identifier]
135
        else:
136
            nodeid = ref.NodeId.to_string()
137
        if ref.TypeDefinition.Identifier in ua.ObjectIdNames:
138
            typedef = ua.ObjectIdNames[ref.TypeDefinition.Identifier]
139
        else:
140
            typedef = ref.TypeDefinition.to_string()
141
        titem = QStandardItem(typename)
142
        titem.setData(ref, Qt.UserRole)
143
        self.model.appendRow([
144
            titem,
145
            QStandardItem(nodeid),
146
            QStandardItem(ref.BrowseName.to_string()),
147
            QStandardItem(typedef)
148
        ])
149
150
151
class MyDelegate(QStyledItemDelegate):
152
153
    error = pyqtSignal(Exception)
154
    reference_changed = pyqtSignal(ua.NodeId)
155
156
    def __init__(self, parent, widget):
157
        QStyledItemDelegate.__init__(self, parent)
158
        self._widget = widget
159
160
    @trycatchslot
161
    def createEditor(self, parent, option, idx):
162
        print("CREATE EDITOR", parent, option, idx)
163
        if idx.column() == 2:
164
            return None
165
        item = self._widget.model.itemFromIndex(idx)
166
        ref = item.data(Qt.UserRole)
167
        if idx.column() == 1:
168
            #get target nodeid
169
            pass
170
        elif idx.column() == 0:
171
            # get ref type
172
            node = Node(self._widget.node.server, ref.ReferenceTypeId)
173
            startnode = Node(self._widget.node.server, ua.ObjectIds.ReferenceTypesFolder)
174
            button = GetNodeButton(parent, node, startnode)
175
            return button
176
177
    @trycatchslot
178
    def setModelData(self, editor, model, idx):
179
        # if user is setting a value on a null variant, try using the nodes datatype instead
180
        idx = idx.sibling(idx.row(), 0)
181
        ref = model.data(idx, Qt.UserRole)
182
        #FIXME: read values from ui and update
183
        logger.info("Writing ref %s", ref)
184
185
186