testexecutor-ui 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testexecutor/VERSION +1 -0
- testexecutor/__init__.py +6 -0
- testexecutor/control/TestListenerApi.py +56 -0
- testexecutor/control/TestSuiteListener.py +249 -0
- testexecutor/control/__init__.py +0 -0
- testexecutor/model/GenericListModel.py +63 -0
- testexecutor/model/InstructionsModel.py +351 -0
- testexecutor/model/KeyValueModel.py +177 -0
- testexecutor/model/MultiTestWindowModel.py +184 -0
- testexecutor/model/ResultModel.py +314 -0
- testexecutor/model/TestSuiteControlModel.py +139 -0
- testexecutor/model/TestSuiteGroup.py +105 -0
- testexecutor/model/TestSuiteModel.py +301 -0
- testexecutor/model/__init__.py +0 -0
- testexecutor/model/selector/FilterGroupModel.py +90 -0
- testexecutor/model/selector/FilterModel.py +251 -0
- testexecutor/model/selector/MultiSelectDetails.py +106 -0
- testexecutor/model/selector/MultiSelectListModel.py +48 -0
- testexecutor/model/selector/__init__.py +0 -0
- testexecutor/ui/IdentificationWidget.qml +40 -0
- testexecutor/ui/InputWidget.qml +74 -0
- testexecutor/ui/InstructionWidget.qml +185 -0
- testexecutor/ui/KeyValueItem.qml +194 -0
- testexecutor/ui/KeyValueItemConfig.qml +41 -0
- testexecutor/ui/KeyValueList.qml +49 -0
- testexecutor/ui/MultiTestWidget.qml +48 -0
- testexecutor/ui/MultiTestWindow.qml +88 -0
- testexecutor/ui/ResultItem.qml +199 -0
- testexecutor/ui/ResultItemConfig.qml +46 -0
- testexecutor/ui/ResultList.qml +58 -0
- testexecutor/ui/TestSuiteConfig.qml +95 -0
- testexecutor/ui/TestSuiteController.qml +54 -0
- testexecutor/ui/TestSuiteWidget.qml +350 -0
- testexecutor/ui/selector/FilterItem.qml +75 -0
- testexecutor/ui/selector/FilterItemConfig.qml +24 -0
- testexecutor/ui/selector/FilterMultiSelectorItem.qml +169 -0
- testexecutor/ui/selector/FilterMultiSelectorItemConfig.qml +22 -0
- testexecutor/ui/selector/FilterMultiSelectorList.qml +50 -0
- testexecutor/ui/selector/HeaderConfig.qml +24 -0
- testexecutor/ui/selector/SectionConfig.qml +24 -0
- testexecutor/ui/selector/SectionDelegate.qml +85 -0
- testexecutor/ui/selector/SelectorHeader.qml +43 -0
- testexecutor/ui/selector/SuiteList.qml +150 -0
- testexecutor/ui/selector/SuiteSelector.qml +71 -0
- testexecutor/ui/selector/SuiteSelectorConfig.qml +38 -0
- testexecutor/ui/selector/TestConfig.qml +22 -0
- testexecutor/ui/selector/TestDelegate.qml +75 -0
- testexecutor/ui/te-64x64.ico +0 -0
- testexecutor_ui-0.6.1.dist-info/LICENSE +204 -0
- testexecutor_ui-0.6.1.dist-info/METADATA +27 -0
- testexecutor_ui-0.6.1.dist-info/NOTICE +3 -0
- testexecutor_ui-0.6.1.dist-info/RECORD +54 -0
- testexecutor_ui-0.6.1.dist-info/WHEEL +5 -0
- testexecutor_ui-0.6.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# Copyright 2020 Direkt, Australia
|
|
2
|
+
# Copyright 2021 Direkt Embedded Pty Ltd
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
# This Python file uses the following encoding: utf-8
|
|
17
|
+
import threading
|
|
18
|
+
from PySide6.QtCore import Slot
|
|
19
|
+
from PySide6.QtCore import Signal
|
|
20
|
+
from PySide6.QtCore import Property
|
|
21
|
+
from PySide6.QtCore import QObject
|
|
22
|
+
from PySide6.QtGui import Qt
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ControlButtonConfig(QObject):
|
|
26
|
+
|
|
27
|
+
def __init__(self, text=None):
|
|
28
|
+
QObject.__init__(self)
|
|
29
|
+
self.lock = threading.RLock()
|
|
30
|
+
self._text = text
|
|
31
|
+
|
|
32
|
+
def _settext(self, text):
|
|
33
|
+
""" Setter for text Property """
|
|
34
|
+
en = self.enabled
|
|
35
|
+
changed = False
|
|
36
|
+
with self.lock:
|
|
37
|
+
if self._text != text:
|
|
38
|
+
self._text = text
|
|
39
|
+
changed = True
|
|
40
|
+
if changed:
|
|
41
|
+
self.text_changed.emit()
|
|
42
|
+
if en is not self.enabled:
|
|
43
|
+
self.enable_changed.emit()
|
|
44
|
+
|
|
45
|
+
def _gettext(self):
|
|
46
|
+
""" Getter for text Property """
|
|
47
|
+
with self.lock:
|
|
48
|
+
t = self._text
|
|
49
|
+
return t
|
|
50
|
+
|
|
51
|
+
text_changed = Signal()
|
|
52
|
+
text = Property(str, _gettext, _settext, notify=text_changed)
|
|
53
|
+
|
|
54
|
+
def _getenabled(self):
|
|
55
|
+
""" Getter for enabled Property """
|
|
56
|
+
en = (self._text is not None) and (self._text != "")
|
|
57
|
+
return en
|
|
58
|
+
|
|
59
|
+
enable_changed = Signal()
|
|
60
|
+
enabled = Property(bool, _getenabled, None, notify=enable_changed)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class InstructionControl(QObject):
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
QObject.__init__(self)
|
|
67
|
+
self.lock = threading.RLock()
|
|
68
|
+
self._buttons = [ControlButtonConfig(), ControlButtonConfig()]
|
|
69
|
+
self._controlReceived = None
|
|
70
|
+
self._inputReceived = None
|
|
71
|
+
self._expectInput = False
|
|
72
|
+
self.userInputEvent = threading.Event()
|
|
73
|
+
|
|
74
|
+
onUserInput = Signal(str, str)
|
|
75
|
+
|
|
76
|
+
def requestDecision(self, buttontextList):
|
|
77
|
+
with self.lock:
|
|
78
|
+
self._controlReceived = None
|
|
79
|
+
self._inputReceived = None
|
|
80
|
+
return self.setButtons(buttontextList)
|
|
81
|
+
|
|
82
|
+
def lastUserDecision(self):
|
|
83
|
+
"""
|
|
84
|
+
Obtain the last user decision that was chosen by the user, or None if no decision pending.
|
|
85
|
+
The decision is not cleared until the next call to requestDecision.
|
|
86
|
+
Current implementation returns the lower case of the text on the control buttons. This is not a clean solution
|
|
87
|
+
and should be changed by adding a decision string list to requestDecision instead.
|
|
88
|
+
:return: decision string
|
|
89
|
+
"""
|
|
90
|
+
with self.lock:
|
|
91
|
+
decision = self._controlReceived
|
|
92
|
+
if decision:
|
|
93
|
+
decision = decision.lower()
|
|
94
|
+
return decision
|
|
95
|
+
|
|
96
|
+
def lastUserInput(self):
|
|
97
|
+
"""
|
|
98
|
+
Obtain the last user input decision that was chosen by the user, or None if no decision pending.
|
|
99
|
+
The decision is not cleared until the next call to requestDecision.
|
|
100
|
+
:return: decision string
|
|
101
|
+
"""
|
|
102
|
+
with self.lock:
|
|
103
|
+
input = self._inputReceived
|
|
104
|
+
return input
|
|
105
|
+
|
|
106
|
+
def clear(self):
|
|
107
|
+
self.cancelWaiting()
|
|
108
|
+
self.setButtons([])
|
|
109
|
+
|
|
110
|
+
@Slot(str)
|
|
111
|
+
def onControl(self, decision):
|
|
112
|
+
"""
|
|
113
|
+
A callback called once a button has been pressed in the instruction window.
|
|
114
|
+
onControl does not clear the instruction window, as the caller may wish to have multiple callbacks.
|
|
115
|
+
This means the caller needs to clear the instruction window when it wants to.
|
|
116
|
+
:param decision: The button press or decision made
|
|
117
|
+
:return:
|
|
118
|
+
"""
|
|
119
|
+
self._onControl(decision)
|
|
120
|
+
|
|
121
|
+
def _onControl(self, decision, input_value=None):
|
|
122
|
+
with self.lock:
|
|
123
|
+
self._controlReceived = decision
|
|
124
|
+
self._inputReceived = input_value
|
|
125
|
+
self.userInputEvent.set()
|
|
126
|
+
self.onUserInput.emit(self.lastUserDecision(), self.lastUserInput())
|
|
127
|
+
|
|
128
|
+
@Slot(str)
|
|
129
|
+
def onExternalInput(self, input_value):
|
|
130
|
+
"""
|
|
131
|
+
A callback providing input from a relevant external control. The control should be supplied externally to
|
|
132
|
+
this component and provide complete string input.
|
|
133
|
+
"""
|
|
134
|
+
if input_value:
|
|
135
|
+
self._onControl(self.USER_INPUT, input_value)
|
|
136
|
+
|
|
137
|
+
def cancelWaiting(self):
|
|
138
|
+
self._controlReceived = None
|
|
139
|
+
self.userInputEvent.set()
|
|
140
|
+
|
|
141
|
+
def setButtons(self, buttonTextList):
|
|
142
|
+
info = buttonTextList
|
|
143
|
+
if type(buttonTextList) is str:
|
|
144
|
+
info = [buttonTextList]
|
|
145
|
+
with self.lock:
|
|
146
|
+
if type(info) is list:
|
|
147
|
+
i = 0
|
|
148
|
+
for txt in info:
|
|
149
|
+
self._buttons[i].text = txt
|
|
150
|
+
i = i + 1
|
|
151
|
+
for j in range(i, len(self._buttons)):
|
|
152
|
+
self._buttons[j].text = None
|
|
153
|
+
present = False
|
|
154
|
+
if buttonTextList:
|
|
155
|
+
present = len(info) > 0
|
|
156
|
+
return present
|
|
157
|
+
|
|
158
|
+
def lastWasUserInput(self):
|
|
159
|
+
return self.lastUserDecision() == self.USER_INPUT
|
|
160
|
+
|
|
161
|
+
# TODO: Consider modifying to provide more buttons, rather than fixed right left.
|
|
162
|
+
def _getleftButton(self):
|
|
163
|
+
""" Getter for leftButton Property """
|
|
164
|
+
with self.lock:
|
|
165
|
+
b = self._buttons[0]
|
|
166
|
+
return b
|
|
167
|
+
|
|
168
|
+
leftButton_changed = Signal()
|
|
169
|
+
leftButton = Property(QObject, _getleftButton, None, notify=leftButton_changed)
|
|
170
|
+
|
|
171
|
+
def _getrightButton(self):
|
|
172
|
+
""" Getter for rightButton Property """
|
|
173
|
+
with self.lock:
|
|
174
|
+
b = self._buttons[1]
|
|
175
|
+
return b
|
|
176
|
+
|
|
177
|
+
rightButton_changed = Signal()
|
|
178
|
+
rightButton = Property(QObject, _getrightButton, None, notify=rightButton_changed)
|
|
179
|
+
|
|
180
|
+
USER_INPUT = 'te_input'
|
|
181
|
+
|
|
182
|
+
class InstructionModel(QObject):
|
|
183
|
+
|
|
184
|
+
def __init__(self):
|
|
185
|
+
QObject.__init__(self)
|
|
186
|
+
self.lock = threading.RLock()
|
|
187
|
+
self._instructionText = None
|
|
188
|
+
self._control = InstructionControl()
|
|
189
|
+
self._enabled = False
|
|
190
|
+
self._input_hidden = False
|
|
191
|
+
self._instructionTitle = None
|
|
192
|
+
self._instructionText = None
|
|
193
|
+
|
|
194
|
+
def clear(self):
|
|
195
|
+
self.instructionTitle = ""
|
|
196
|
+
self.instructionText = ""
|
|
197
|
+
self.control.clear()
|
|
198
|
+
|
|
199
|
+
@Slot(str, str, list, bool)
|
|
200
|
+
def userDecision(self, title, message, control, enable=True):
|
|
201
|
+
"""
|
|
202
|
+
Method called to send instructions to the user
|
|
203
|
+
The caller will use control.lastUserDecision() to obtain the decision chosen, implementing a wait state
|
|
204
|
+
if required.
|
|
205
|
+
If no control information is given, then this model will remain disabled as their is no expectation for the
|
|
206
|
+
user to provide input.
|
|
207
|
+
:param name: unique name of the test, not used
|
|
208
|
+
:param message: class containing information to display to the user
|
|
209
|
+
:param control: list of text to display on decision/control buttons
|
|
210
|
+
:param enable: enable text widget even if no buttons so highlighted colour is shown
|
|
211
|
+
:return: None
|
|
212
|
+
"""
|
|
213
|
+
self.instructionTitle = title
|
|
214
|
+
self.instructionText = message
|
|
215
|
+
buttons_enabled = self.control.requestDecision(control)
|
|
216
|
+
self._internal_setenabled(buttons_enabled | enable)
|
|
217
|
+
|
|
218
|
+
@Slot(str, str, list, str, bool, tuple, bool)
|
|
219
|
+
def userInputRequest(self, title, message, control, default_value=None, hidden=False, values=None, enable=True):
|
|
220
|
+
"""
|
|
221
|
+
Method called to send instructions to the user
|
|
222
|
+
The caller will use control.lastUserDecision() to obtain the decision chosen, implementing a wait state
|
|
223
|
+
if required.
|
|
224
|
+
If no control information is given, then this model will remain disabled as their is no expectation for the
|
|
225
|
+
user to provide input.
|
|
226
|
+
:param name: unique name of the test, not used
|
|
227
|
+
:param message: class containing information to display to the user
|
|
228
|
+
:param control: list of text to display on decision/control buttons
|
|
229
|
+
:param default_value: value to provide as a default to the user - future
|
|
230
|
+
:param values: enable text widget even if no buttons so highlighted colour is shown - future
|
|
231
|
+
:param enable: enable text widget even if no buttons so highlighted colour is shown
|
|
232
|
+
:param hidden: hide text as entered
|
|
233
|
+
:return: None
|
|
234
|
+
"""
|
|
235
|
+
self.instructionTitle = title
|
|
236
|
+
self.instructionText = message
|
|
237
|
+
self.input_hidden = hidden
|
|
238
|
+
buttons_enabled = self.control.requestDecision(control)
|
|
239
|
+
self._internal_setenabled(buttons_enabled | enable)
|
|
240
|
+
|
|
241
|
+
def userDecisionWait(self):
|
|
242
|
+
"""
|
|
243
|
+
If the caller wishes to block until the user decision requested is made, they can call this immediately
|
|
244
|
+
after calling userDecision.
|
|
245
|
+
"""
|
|
246
|
+
self.control.userInputEvent.clear()
|
|
247
|
+
self.control.userInputEvent.wait()
|
|
248
|
+
|
|
249
|
+
def _setinstructionText(self, instructionText):
|
|
250
|
+
""" Setter for instructionText Property """
|
|
251
|
+
changed = False
|
|
252
|
+
with self.lock:
|
|
253
|
+
if self._instructionText != instructionText:
|
|
254
|
+
changed = True
|
|
255
|
+
self._instructionText = instructionText
|
|
256
|
+
if instructionText and "html" not in instructionText.lower():
|
|
257
|
+
self._instructionText = Qt.convertFromPlainText(instructionText, Qt.WhiteSpaceNormal)
|
|
258
|
+
|
|
259
|
+
if changed:
|
|
260
|
+
self.instructionText_changed.emit()
|
|
261
|
+
|
|
262
|
+
def _getinstructionText(self):
|
|
263
|
+
""" Getter for instructionText Property """
|
|
264
|
+
with self.lock:
|
|
265
|
+
instr = self._instructionText
|
|
266
|
+
return instr
|
|
267
|
+
|
|
268
|
+
instructionText_changed = Signal()
|
|
269
|
+
instructionText = Property(str, _getinstructionText, _setinstructionText, notify=instructionText_changed)
|
|
270
|
+
|
|
271
|
+
def _setinstructionTitle(self, instructionTitle):
|
|
272
|
+
""" Setter for instructionTitle Property """
|
|
273
|
+
changed = False
|
|
274
|
+
with self.lock:
|
|
275
|
+
if self._instructionTitle != instructionTitle:
|
|
276
|
+
self._instructionTitle = instructionTitle
|
|
277
|
+
changed = True
|
|
278
|
+
if changed:
|
|
279
|
+
self.instructionTitle_changed.emit()
|
|
280
|
+
|
|
281
|
+
def _getinstructionTitle(self):
|
|
282
|
+
""" Getter for instructionTitle Property """
|
|
283
|
+
with self.lock:
|
|
284
|
+
t = self._instructionTitle
|
|
285
|
+
return t
|
|
286
|
+
|
|
287
|
+
instructionTitle_changed = Signal()
|
|
288
|
+
instructionTitle = Property(str, _getinstructionTitle, _setinstructionTitle, notify=instructionTitle_changed)
|
|
289
|
+
|
|
290
|
+
def _setcontrol(self, control):
|
|
291
|
+
""" Setter for control Property """
|
|
292
|
+
changed = False
|
|
293
|
+
with self.lock:
|
|
294
|
+
if self._control != control:
|
|
295
|
+
self._control = control
|
|
296
|
+
changed = True
|
|
297
|
+
if changed:
|
|
298
|
+
self.control_changed.emit()
|
|
299
|
+
|
|
300
|
+
def _getcontrol(self):
|
|
301
|
+
""" Getter for control Property """
|
|
302
|
+
with self.lock:
|
|
303
|
+
c = self._control
|
|
304
|
+
return c
|
|
305
|
+
|
|
306
|
+
control_changed = Signal()
|
|
307
|
+
control = Property(QObject, _getcontrol, _setcontrol, notify=control_changed)
|
|
308
|
+
|
|
309
|
+
def _internal_setenabled(self, enabled):
|
|
310
|
+
changed = False
|
|
311
|
+
with self.lock:
|
|
312
|
+
if self._enabled != enabled:
|
|
313
|
+
self._enabled = enabled
|
|
314
|
+
changed = True
|
|
315
|
+
if changed:
|
|
316
|
+
self.enabled_changed.emit()
|
|
317
|
+
|
|
318
|
+
def _setenabled(self, enabled):
|
|
319
|
+
""" Setter for enabled Property """
|
|
320
|
+
if not enabled:
|
|
321
|
+
# If we have been disabled, ensure there is no pending control blocking operation
|
|
322
|
+
self.control.cancelWaiting()
|
|
323
|
+
self._internal_setenabled(enabled)
|
|
324
|
+
|
|
325
|
+
def _getenabled(self):
|
|
326
|
+
""" Getter for enabled Property """
|
|
327
|
+
with self.lock:
|
|
328
|
+
e = self._enabled
|
|
329
|
+
return e
|
|
330
|
+
|
|
331
|
+
enabled_changed = Signal()
|
|
332
|
+
enabled = Property(bool, _getenabled, _setenabled, notify=enabled_changed)
|
|
333
|
+
|
|
334
|
+
def _set_input_hidden(self, input_hidden):
|
|
335
|
+
""" Setter for _input_hidden Property """
|
|
336
|
+
changed = False
|
|
337
|
+
with self.lock:
|
|
338
|
+
if self._input_hidden != input_hidden:
|
|
339
|
+
self._input_hidden = input_hidden
|
|
340
|
+
changed = True
|
|
341
|
+
if changed:
|
|
342
|
+
self.input_hidden_changed.emit()
|
|
343
|
+
|
|
344
|
+
def _get_input_hidden(self):
|
|
345
|
+
""" Getter for _input_hidden Property """
|
|
346
|
+
with self.lock:
|
|
347
|
+
e = self._input_hidden
|
|
348
|
+
return e
|
|
349
|
+
|
|
350
|
+
input_hidden_changed = Signal()
|
|
351
|
+
input_hidden = Property(bool, _get_input_hidden, _set_input_hidden, notify=input_hidden_changed)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Copyright 2020 Direkt, Australia
|
|
2
|
+
# Copyright 2021 Direkt Embedded Pty Ltd
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
# This Python file uses the following encoding: utf-8
|
|
17
|
+
import threading
|
|
18
|
+
from PySide6.QtCore import QAbstractListModel
|
|
19
|
+
from PySide6.QtCore import Qt
|
|
20
|
+
from PySide6.QtCore import QObject
|
|
21
|
+
from PySide6.QtCore import QModelIndex
|
|
22
|
+
from PySide6.QtCore import Property, Signal
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class KeyValue(QObject):
|
|
26
|
+
def __init__(self, label, value):
|
|
27
|
+
QObject.__init__(self)
|
|
28
|
+
self.lock = threading.RLock()
|
|
29
|
+
self._value = value
|
|
30
|
+
self._label = label
|
|
31
|
+
|
|
32
|
+
def _setvalue(self, value):
|
|
33
|
+
""" Setter for value Property """
|
|
34
|
+
changed = False
|
|
35
|
+
with self.lock:
|
|
36
|
+
if self._value != value:
|
|
37
|
+
self._value = value
|
|
38
|
+
changed = True
|
|
39
|
+
if changed:
|
|
40
|
+
self.value_changed.emit()
|
|
41
|
+
|
|
42
|
+
def _getvalue(self):
|
|
43
|
+
""" Getter for value Property """
|
|
44
|
+
with self.lock:
|
|
45
|
+
v = self._value
|
|
46
|
+
return v
|
|
47
|
+
|
|
48
|
+
value_changed = Signal()
|
|
49
|
+
value = Property(str, _getvalue, _setvalue, notify=value_changed)
|
|
50
|
+
|
|
51
|
+
def _setlabel(self, label):
|
|
52
|
+
""" Setter for label Property """
|
|
53
|
+
changed = False
|
|
54
|
+
with self.lock:
|
|
55
|
+
if self._label != label:
|
|
56
|
+
self._label = label
|
|
57
|
+
changed = True
|
|
58
|
+
if changed:
|
|
59
|
+
self.label_changed.emit()
|
|
60
|
+
|
|
61
|
+
def _getlabel(self):
|
|
62
|
+
""" Getter for label Property """
|
|
63
|
+
with self.lock:
|
|
64
|
+
l = self._label
|
|
65
|
+
return l
|
|
66
|
+
|
|
67
|
+
label_changed = Signal()
|
|
68
|
+
label = Property(str, _getlabel, _setlabel, notify=label_changed)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class KeyValueModel(QAbstractListModel):
|
|
73
|
+
|
|
74
|
+
KeyRole = Qt.UserRole + 1
|
|
75
|
+
ValueRole = Qt.UserRole + 2
|
|
76
|
+
PossibleValuesRole = Qt.UserRole + 3
|
|
77
|
+
KeyKey = b'key'
|
|
78
|
+
ValueKey = b'value'
|
|
79
|
+
PossibleValuesKey = b'possibles'
|
|
80
|
+
|
|
81
|
+
def __init__(self, parent = None, clone = None):
|
|
82
|
+
QAbstractListModel.__init__(self, parent)
|
|
83
|
+
self.lock = threading.RLock()
|
|
84
|
+
if clone:
|
|
85
|
+
self._data = clone._data
|
|
86
|
+
else:
|
|
87
|
+
self._data = []
|
|
88
|
+
|
|
89
|
+
def rowCount(self, index):
|
|
90
|
+
return len(self._data)
|
|
91
|
+
|
|
92
|
+
def roleNames(self):
|
|
93
|
+
return {KeyValueModel.KeyRole: KeyValueModel.KeyKey,
|
|
94
|
+
KeyValueModel.PossibleValuesRole: KeyValueModel.PossibleValuesKey,
|
|
95
|
+
KeyValueModel.ValueRole: KeyValueModel.ValueKey}
|
|
96
|
+
|
|
97
|
+
def data(self, index, role=None):
|
|
98
|
+
with self.lock:
|
|
99
|
+
d = self._data[index.row()]
|
|
100
|
+
if role == KeyValueModel.KeyRole:
|
|
101
|
+
return d[KeyValueModel.KeyKey]
|
|
102
|
+
elif role == KeyValueModel.ValueRole:
|
|
103
|
+
return d[KeyValueModel.ValueKey]
|
|
104
|
+
elif role == KeyValueModel.PossibleValuesRole:
|
|
105
|
+
return d[KeyValueModel.PossibleValuesKey]
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
def add(self, key, value, possibleValues=None):
|
|
109
|
+
if possibleValues is None:
|
|
110
|
+
possibleValues = []
|
|
111
|
+
rowCount = self.rowCount(QModelIndex())
|
|
112
|
+
self.beginInsertRows(QModelIndex(), rowCount, rowCount)
|
|
113
|
+
with self.lock:
|
|
114
|
+
self._data.append({KeyValueModel.KeyKey: key, KeyValueModel.ValueKey: value, KeyValueModel.PossibleValuesKey: possibleValues})
|
|
115
|
+
self.endInsertRows()
|
|
116
|
+
|
|
117
|
+
def setData(self, index, value, role=None):
|
|
118
|
+
with self.lock:
|
|
119
|
+
self._data[index.row()] = value
|
|
120
|
+
self.dataChanged.emit(index, index, self.roleNames())
|
|
121
|
+
|
|
122
|
+
def setValue(self, key, value=None, label=None):
|
|
123
|
+
changed = False
|
|
124
|
+
for row in range(len(self._data)):
|
|
125
|
+
with self.lock:
|
|
126
|
+
if self._data[row][KeyValueModel.KeyKey] == key:
|
|
127
|
+
if value:
|
|
128
|
+
self._data[row][KeyValueModel.ValueKey].value = value
|
|
129
|
+
if label:
|
|
130
|
+
self._data[row][KeyValueModel.ValueKey].label = label
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
def setLabel(self, key, label):
|
|
134
|
+
self.setValue(key, value=None, label=label)
|
|
135
|
+
|
|
136
|
+
def setPossibleValues(self, key, values):
|
|
137
|
+
with self.lock:
|
|
138
|
+
for row in range(len(self._data)):
|
|
139
|
+
if self._data[row][KeyValueModel.KeyKey] == key:
|
|
140
|
+
self._data[row][KeyValueModel.PossibleValuesKey] = values
|
|
141
|
+
ix = self.index(row, 0)
|
|
142
|
+
changed = True
|
|
143
|
+
break
|
|
144
|
+
if changed:
|
|
145
|
+
self.dataChanged.emit(ix, ix, self.roleNames())
|
|
146
|
+
|
|
147
|
+
def getValue(self, key):
|
|
148
|
+
value = None
|
|
149
|
+
for row in range(len(self._data)):
|
|
150
|
+
with self.lock:
|
|
151
|
+
if self._data[row][KeyValueModel.KeyKey] == key:
|
|
152
|
+
value = self._data[row][KeyValueModel.ValueKey].value
|
|
153
|
+
return value
|
|
154
|
+
|
|
155
|
+
def getValues(self):
|
|
156
|
+
"""
|
|
157
|
+
Returns dictionary with key:value pairs as string:string. Note the label is not included.
|
|
158
|
+
:return: Returns dictionary with key:value pairs
|
|
159
|
+
"""
|
|
160
|
+
values = {}
|
|
161
|
+
with self.lock:
|
|
162
|
+
for row in range(len(self._data)):
|
|
163
|
+
item = self._data[row]
|
|
164
|
+
values[item[KeyValueModel.KeyKey]] = item[KeyValueModel.ValueKey].value
|
|
165
|
+
return values
|
|
166
|
+
|
|
167
|
+
def clearData(self):
|
|
168
|
+
"""
|
|
169
|
+
Clear the data content, namely the values and active value.
|
|
170
|
+
:return: None
|
|
171
|
+
"""
|
|
172
|
+
for row in range(len(self._data)):
|
|
173
|
+
with self.lock:
|
|
174
|
+
self._data[row][KeyValueModel.ValueKey].value = ""
|
|
175
|
+
self._data[row][KeyValueModel.PossibleValuesKey] = []
|
|
176
|
+
ix = self.index(row, 0)
|
|
177
|
+
self.dataChanged.emit(ix, ix, self.roleNames())
|