Completed
Pull Request — master (#8)
by Olivier
01:06
created

RefsWidget   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 18
c 2
b 0
f 0
dl 0
loc 109
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A show_refs() 0 4 1
A _add_ref_row() 0 17 4
B __init__() 0 30 2
A remove_ref() 0 9 2
A add_ref() 0 7 1
A save_state() 0 2 1
A _show_refs() 0 8 3
A _make_default_ref() 0 4 1
A showContextMenu() 0 8 2
A clear() 0 4 1
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.addRefAction = QAction("Add Reference", self.model)
42
        self.addRefAction.triggered.connect(self.add_ref)
43
        self.removeRefAction = QAction("Remove Reference", self.model)
44
        self.removeRefAction.triggered.connect(self.remove_ref)
45
46
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
47
        self.view.customContextMenuRequested.connect(self.showContextMenu)
48
        self._contextMenu = QMenu()
49
        self._contextMenu.addAction(self.addRefAction)
50
        self._contextMenu.addAction(self.removeRefAction)
51
52
    def showContextMenu(self, position):
53
        self.removeRefAction.setEnabled(False)
54
        idx = self.view.currentIndex()
55
        if not idx.isValid():
56
            print("NOT VALID")
57
            return
58
        self.removeRefAction.setEnabled(True)
59
        self._contextMenu.exec_(self.view.viewport().mapToGlobal(position))
60
61
    def clear(self):
62
        # remove all rows but not header!!
63
        self.model.removeRows(0, self.model.rowCount())
64
        self.node = None
65
66
    def _make_default_ref(self):
67
        #FIXME: remeber last choosen values or use values that make sense
68
        ref = ua.ReferenceDescription()
69
        return ref
70
71
    @trycatchslot
72
    def add_ref(self):
73
        ref = self._make_default_ref()
74
        logger.info("Adding ref: %s", ref)
75
        self._add_ref_row(ref)
76
        idx = self.model.index(self.model.rowCount() - 1, 0)
77
        self.view.setCurrentIndex(idx)
78
        #self.view.edit(idx)
79
80
    @trycatchslot
81
    def remove_ref(self):
82
        idx = self.view.currentIndex()
83
        if not idx.isValid():
84
            logger.warning("No valid reference selected to remove")
85
        idx = idx.sibling(idx.row(), 0)
86
        item = self.model.itemFromIndex(idx)
87
        ref = item.data(Qt.UserRole)
88
        logger.info("Removing: %s", ref)
89
        #self.reload()
90
91
    def save_state(self):
92
        self.settings.setValue("refs_widget", self.view.horizontalHeader().saveState())
93
94
    def show_refs(self, node):
95
        self.clear()
96
        self.node = node
97
        self._show_refs(node)
98
99
    def _show_refs(self, node):
100
        try:
101
            refs = node.get_children_descriptions(refs=ua.ObjectIds.References)
102
        except Exception as ex:
103
            self.error.emit(ex)
104
            raise
105
        for ref in refs:
106
            self._add_ref_row(ref)
107
108
    def _add_ref_row(self, ref):
109
        typename = ua.ObjectIdNames[ref.ReferenceTypeId.Identifier]
110
        if ref.NodeId.NamespaceIndex == 0 and ref.NodeId.Identifier in ua.ObjectIdNames:
111
            nodeid = ua.ObjectIdNames[ref.NodeId.Identifier]
112
        else:
113
            nodeid = ref.NodeId.to_string()
114
        if ref.TypeDefinition.Identifier in ua.ObjectIdNames:
115
            typedef = ua.ObjectIdNames[ref.TypeDefinition.Identifier]
116
        else:
117
            typedef = ref.TypeDefinition.to_string()
118
        titem = QStandardItem(typename)
119
        titem.setData(ref, Qt.UserRole)
120
        self.model.appendRow([
121
            titem,
122
            QStandardItem(nodeid),
123
            QStandardItem(ref.BrowseName.to_string()),
124
            QStandardItem(typedef)
125
        ])
126
127
128
class MyDelegate(QStyledItemDelegate):
129
130
    error = pyqtSignal(Exception)
131
    reference_changed = pyqtSignal(ua.NodeId)
132
133
    def __init__(self, parent, widget):
134
        QStyledItemDelegate.__init__(self, parent)
135
        self._widget = widget
136
137
    @trycatchslot
138
    def createEditor(self, parent, option, idx):
139
        print("CREATE EDITOR", parent, option, idx)
140
        if idx.column() == 2:
141
            return None
142
        item = self._widget.model.itemFromIndex(idx)
143
        ref = item.data(Qt.UserRole)
144
        if idx.column() == 1:
145
            #get target nodeid
146
            pass
147
        elif idx.column() == 0:
148
            # get ref type
149
            node = Node(self._widget.node.server, ref.ReferenceTypeId)
150
            startnode = Node(self._widget.node.server, ua.ObjectIds.ReferenceTypesFolder)
151
            button = GetNodeButton(parent, node, startnode)
152
            return button
153
154
    @trycatchslot
155
    def setModelData(self, editor, model, idx):
156
        # if user is setting a value on a null variant, try using the nodes datatype instead
157
        idx = idx.sibling(idx.row(), 0)
158
        ref = model.data(idx, Qt.UserRole)
159
        #FIXME: read values from ui and update
160
        logger.info("Writing ref %s", ref)
161
162
163