Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
beat
beat.editor
Commits
119f462f
Commit
119f462f
authored
Jul 18, 2019
by
Flavio TARSETTI
Committed by
Samuel GAIST
Jul 18, 2019
Browse files
[widgets][plotterparameterseditor] Implement PlotterParametersEditor
parent
378e9f10
Changes
2
Hide whitespace changes
Inline
Side-by-side
beat/editor/test/test_plotterparameterseditor.py
View file @
119f462f
...
...
@@ -23,17 +23,308 @@
# #
###############################################################################
import
pytest
from
PyQt5
import
QtCore
from
PyQt5.QtWidgets
import
QDialogButtonBox
from
PyQt5.QtWidgets
import
QMessageBox
from
..backend.asset
import
AssetType
from
..backend.asset
import
Asset
from
..backend.assetmodel
import
AssetModel
from
..widgets.plotterparameterseditor
import
PlotterParametersEditor
from
..widgets.plotterparameterseditor
import
PlotterParameterViewer
from
..widgets.plotterparameterseditor
import
RestrictedParameterWidget
from
..widgets.plotterparameterseditor
import
ParameterChoiceDialog
from
.conftest
import
sync_prefix
from
.conftest
import
prefix
@
pytest
.
fixture
()
def
plotter_model
(
test_prefix
):
asset_model
=
AssetModel
()
asset_model
.
asset_type
=
AssetType
.
PLOTTER
asset_model
.
setLatestOnlyEnabled
(
False
)
asset_model
.
prefix_path
=
test_prefix
return
asset_model
def
get_plotterparameter
(
test_prefix
):
sync_prefix
()
model
=
AssetModel
()
model
.
asset_type
=
AssetType
.
PLOTTERPARAMETER
model
.
prefix_path
=
test_prefix
model
.
setLatestOnlyEnabled
(
False
)
return
[
plotterparameter
for
plotterparameter
in
model
.
stringList
()
if
all
(
invalid
not
in
plotterparameter
for
invalid
in
[
"invalid"
])
]
@
pytest
.
fixture
()
def
reference_parameter_json
():
return
{
"axis-fontsize"
:
{
"default"
:
10
,
"description"
:
"Controls the axis font size (labels and values)"
,
"type"
:
"uint16"
,
}
}
@
pytest
.
fixture
()
def
modified_parameter_json
():
return
{
"axis-fontsize"
:
25
}
@
pytest
.
fixture
()
def
reference_input_string_list
():
return
[
"some_text_1"
,
"some_text_2"
,
"some_text_3"
]
class
TestParameterChoiceDialog
:
"""Test the addition of parameters from the plotter works correctly"""
def
test_dialog
(
self
,
qtbot
,
reference_input_string_list
):
dialog
=
ParameterChoiceDialog
(
reference_input_string_list
)
qtbot
.
addWidget
(
dialog
)
qtbot
.
mouseClick
(
dialog
.
buttons
.
button
(
QDialogButtonBox
.
Ok
),
QtCore
.
Qt
.
LeftButton
)
assert
dialog
.
value
()
==
reference_input_string_list
[
0
]
class
TestRestrictedParameterWidget
:
"""Test that the restricted parameter editor works correctly"""
def
test_load_and_dump
(
self
,
qtbot
,
reference_parameter_json
,
modified_parameter_json
):
name
=
next
(
iter
(
reference_parameter_json
))
data
=
reference_parameter_json
.
get
(
name
,
{})
restricted_parameter_widget
=
RestrictedParameterWidget
(
data
)
modified_data
=
modified_parameter_json
.
get
(
name
,
0
)
assert
restricted_parameter_widget
.
dump
()
==
data
[
"default"
]
restricted_parameter_widget
.
load
(
modified_data
)
assert
restricted_parameter_widget
.
dump
()
==
modified_data
class
TestPlotterParameterViewer
:
"""Test that the viewer dedicated to the parameters work correctly"""
def
test_load_and_dump
(
self
,
qtbot
,
reference_parameter_json
,
modified_parameter_json
):
name
=
next
(
iter
(
reference_parameter_json
))
data
=
reference_parameter_json
.
get
(
name
,
{})
parameter_viewer
=
PlotterParameterViewer
(
name
,
data
)
modified_data
=
modified_parameter_json
.
get
(
name
,
0
)
parameter_viewer
.
load
(
modified_data
)
assert
parameter_viewer
.
dump
()
==
modified_parameter_json
def
test_get_name
(
self
,
qtbot
,
reference_parameter_json
,
modified_parameter_json
):
name
=
next
(
iter
(
reference_parameter_json
))
data
=
reference_parameter_json
.
get
(
name
,
{})
parameter_viewer
=
PlotterParameterViewer
(
name
,
data
)
modified_data
=
modified_parameter_json
.
get
(
name
,
0
)
assert
parameter_viewer
.
dump
()
==
{
name
:
reference_parameter_json
[
name
][
"default"
]
}
parameter_viewer
.
load
(
modified_data
)
assert
parameter_viewer
.
dump
()
==
modified_parameter_json
assert
parameter_viewer
.
name
()
==
name
class
TestPlotterParametersEditor
:
"""Test that the mock editor works correctly"""
def
test_load_and_dump
(
self
,
qtbot
):
reference_json
=
{
"description"
:
"test"
}
@
pytest
.
mark
.
parametrize
(
"plotterparameter"
,
get_plotterparameter
(
prefix
))
def
test_load_and_dump
(
self
,
qtbot
,
beat_context
,
plotter_model
,
test_prefix
,
plotterparameter
):
asset_name
=
plotterparameter
asset
=
Asset
(
test_prefix
,
AssetType
.
PLOTTERPARAMETER
,
asset_name
)
editor
=
PlotterParametersEditor
()
editor
.
set_context
(
beat_context
)
editor
.
set_plotter_model_to_combobox
(
plotter_model
)
editor
.
load_json
(
asset
.
declaration
)
qtbot
.
addWidget
(
editor
)
assert
editor
.
dump_json
()
==
asset
.
declaration
def
test_change_plotter
(
self
,
qtbot
,
beat_context
,
plotter_model
,
test_prefix
):
asset_name
=
"plot/config/1"
asset
=
Asset
(
test_prefix
,
AssetType
.
PLOTTERPARAMETER
,
asset_name
)
editor
=
PlotterParametersEditor
()
editor
.
set_context
(
beat_context
)
editor
.
set_plotter_model_to_combobox
(
plotter_model
)
editor
.
load_json
(
asset
.
declaration
)
qtbot
.
addWidget
(
editor
)
assert
editor
.
dump_json
()
==
asset
.
declaration
asset_list
=
plotter_model
.
stringList
()
qtbot
.
keyClicks
(
editor
.
plotter_combobox
,
asset_list
[
1
])
assert
editor
.
dump_json
()[
"plotter"
]
==
asset_list
[
1
]
def
test_add_plotter_parameter
(
self
,
qtbot
,
monkeypatch
,
beat_context
,
plotter_model
,
test_prefix
):
asset_name
=
"plot/config/1"
asset
=
Asset
(
test_prefix
,
AssetType
.
PLOTTERPARAMETER
,
asset_name
)
editor
=
PlotterParametersEditor
()
editor
.
set_context
(
beat_context
)
editor
.
set_plotter_model_to_combobox
(
plotter_model
)
plotter_name
=
asset
.
declaration
.
get
(
"plotter"
,
None
)
asset_plotter
=
Asset
(
test_prefix
,
AssetType
.
PLOTTER
,
plotter_name
)
editor
.
load_json
(
asset
.
declaration
)
qtbot
.
addWidget
(
editor
)
parameters_used
=
editor
.
dump_json
()[
"data"
]
reference_plotter_parameters
=
asset_plotter
.
declaration
.
get
(
"parameters"
,
{})
unused_plotterparameters
=
[]
for
name
,
data
in
reference_plotter_parameters
.
items
():
if
name
not
in
parameters_used
:
unused_plotterparameters
.
append
(
name
)
assert
editor
.
dump_json
()
==
asset
.
declaration
monkeypatch
.
setattr
(
ParameterChoiceDialog
,
"getParameterObject"
,
classmethod
(
lambda
*
args
:
(
unused_plotterparameters
[
0
],
True
)),
)
qtbot
.
mouseClick
(
editor
.
add_parameter_button
,
QtCore
.
Qt
.
LeftButton
)
updated_plotterparameter
=
asset
.
declaration
updated_plotterparameter
[
"data"
][
unused_plotterparameters
[
0
]
]
=
reference_plotter_parameters
[
unused_plotterparameters
[
0
]][
"default"
]
assert
editor
.
dump_json
()
==
updated_plotterparameter
def
test_remove_plotter_parameter
(
self
,
qtbot
,
monkeypatch
,
beat_context
,
plotter_model
,
test_prefix
):
asset_name
=
"plot/config/1"
asset
=
Asset
(
test_prefix
,
AssetType
.
PLOTTERPARAMETER
,
asset_name
)
editor
=
PlotterParametersEditor
()
editor
.
set_context
(
beat_context
)
editor
.
set_plotter_model_to_combobox
(
plotter_model
)
plotter_name
=
asset
.
declaration
.
get
(
"plotter"
,
None
)
asset_plotter
=
Asset
(
test_prefix
,
AssetType
.
PLOTTER
,
plotter_name
)
editor
.
load_json
(
asset
.
declaration
)
qtbot
.
addWidget
(
editor
)
parameters_used
=
editor
.
dump_json
()[
"data"
]
reference_plotter_parameters
=
asset_plotter
.
declaration
.
get
(
"parameters"
,
{})
unused_plotterparameters
=
[]
for
name
,
data
in
reference_plotter_parameters
.
items
():
if
name
not
in
parameters_used
:
unused_plotterparameters
.
append
(
name
)
assert
editor
.
dump_json
()
==
asset
.
declaration
parameters_length_before_deletion
=
len
(
editor
.
scroll_widget
.
widget_list
)
qtbot
.
mouseClick
(
editor
.
scroll_widget
.
widget_list
[
0
].
delete_button
,
QtCore
.
Qt
.
LeftButton
)
assert
len
(
editor
.
scroll_widget
.
widget_list
)
==
(
parameters_length_before_deletion
-
1
)
def
test_disabled_enabled_add_plotter_parameter_button
(
self
,
qtbot
,
monkeypatch
,
beat_context
,
plotter_model
,
test_prefix
):
asset_name
=
"plot/config/1"
asset
=
Asset
(
test_prefix
,
AssetType
.
PLOTTERPARAMETER
,
asset_name
)
editor
=
PlotterParametersEditor
()
editor
.
set_context
(
beat_context
)
editor
.
set_plotter_model_to_combobox
(
plotter_model
)
plotter_name
=
asset
.
declaration
.
get
(
"plotter"
,
None
)
asset_plotter
=
Asset
(
test_prefix
,
AssetType
.
PLOTTER
,
plotter_name
)
editor
.
load_json
(
asset
.
declaration
)
qtbot
.
addWidget
(
editor
)
parameters_used
=
editor
.
dump_json
()[
"data"
]
reference_plotter_parameters
=
asset_plotter
.
declaration
.
get
(
"parameters"
,
{})
unused_plotterparameters
=
[]
for
name
,
data
in
reference_plotter_parameters
.
items
():
if
name
not
in
parameters_used
:
unused_plotterparameters
.
append
(
name
)
assert
editor
.
dump_json
()
==
asset
.
declaration
updated_plotterparameter
=
asset
.
declaration
# Add all possible parameters
for
parameter
in
unused_plotterparameters
:
monkeypatch
.
setattr
(
ParameterChoiceDialog
,
"getParameterObject"
,
classmethod
(
lambda
*
args
:
(
parameter
,
True
)),
)
monkeypatch
.
setattr
(
QMessageBox
,
"information"
,
lambda
*
args
:
QMessageBox
.
Ok
)
# Check enabled button
assert
editor
.
add_parameter_button
.
isEnabled
()
is
True
qtbot
.
mouseClick
(
editor
.
add_parameter_button
,
QtCore
.
Qt
.
LeftButton
)
updated_plotterparameter
[
"data"
][
parameter
]
=
reference_plotter_parameters
[
parameter
][
"default"
]
assert
editor
.
dump_json
()
==
updated_plotterparameter
if
(
unused_plotterparameters
.
index
(
parameter
)
==
len
(
unused_plotterparameters
)
-
1
):
# Check disabled button change
assert
editor
.
add_parameter_button
.
isEnabled
()
is
False
editor
.
load_json
(
reference_json
)
# Check button gets re-enabled on parameter deletion
qtbot
.
mouseClick
(
editor
.
scroll_widget
.
widget_list
[
0
].
delete_button
,
QtCore
.
Qt
.
LeftButton
)
assert
editor
.
dump_json
()
==
reference_json
assert
editor
.
add_parameter_button
.
isEnabled
()
is
True
beat/editor/widgets/plotterparameterseditor.py
View file @
119f462f
...
...
@@ -23,25 +23,484 @@
# #
###############################################################################
from
PyQt5.QtCore
import
Qt
from
PyQt5.QtCore
import
pyqtSignal
from
PyQt5.QtCore
import
pyqtSlot
from
PyQt5.QtCore
import
QTimer
from
PyQt5.QtWidgets
import
QComboBox
from
PyQt5.QtWidgets
import
QDialog
from
PyQt5.QtWidgets
import
QDialogButtonBox
from
PyQt5.QtWidgets
import
QMessageBox
from
PyQt5.QtWidgets
import
QCheckBox
from
PyQt5.QtWidgets
import
QFormLayout
from
PyQt5.QtWidgets
import
QHBoxLayout
from
PyQt5.QtWidgets
import
QVBoxLayout
from
PyQt5.QtWidgets
import
QLabel
from
PyQt5.QtWidgets
import
QLineEdit
from
PyQt5.QtWidgets
import
QPushButton
from
PyQt5.QtWidgets
import
QWidget
from
..backend.asset
import
Asset
from
..backend.asset
import
AssetType
from
..backend.assetmodel
import
AssetModel
from
..decorators
import
frozen
from
.scrollwidget
import
ScrollWidget
from
.editor
import
AbstractAssetEditor
from
.parameterwidget
import
InputType
from
.spinboxes
import
NumpySpinBox
class
ParameterChoiceDialog
(
QDialog
):
"""Dialog to retrieve a value to to add to the editable parameters"""
def
__init__
(
self
,
unused_parameters_list
,
parent
=
None
):
"""Constructor
:param unused_parameters_list: parameters that can still be edited
:param parent QWidget: parent widget
"""
super
(
ParameterChoiceDialog
,
self
).
__init__
(
parent
)
self
.
setWindowTitle
(
self
.
tr
(
"Input"
))
self
.
label
=
QLabel
(
self
.
tr
(
"Add Parameter:"
))
self
.
combobox
=
QComboBox
()
self
.
combobox
.
addItems
(
unused_parameters_list
)
layout
=
QVBoxLayout
(
self
)
layout
.
addWidget
(
self
.
label
)
layout
.
addWidget
(
self
.
combobox
)
# OK and Cancel buttons
self
.
buttons
=
QDialogButtonBox
(
QDialogButtonBox
.
Ok
|
QDialogButtonBox
.
Cancel
,
Qt
.
Horizontal
,
self
)
layout
.
addWidget
(
self
.
buttons
)
# Signals/Slots connection
self
.
buttons
.
accepted
.
connect
(
self
.
accept
)
self
.
buttons
.
rejected
.
connect
(
self
.
reject
)
def
value
(
self
):
"""Returns the value selected"""
return
self
.
combobox
.
currentText
()
@
staticmethod
def
getParameterObject
(
unused_parameters_list
,
parent
=
None
):
"""Static method to create the dialog and return qdialog accepted/combobox value
:param unused_parameters_list: parameters that can still be edited
:param parent QWidget: parent widget
"""
dialog
=
ParameterChoiceDialog
(
unused_parameters_list
,
parent
)
result
=
dialog
.
exec_
()
value
=
None
if
result
==
QDialog
.
Accepted
:
value
=
dialog
.
value
()
return
(
value
,
result
)
class
RestrictedParameterWidget
(
QWidget
):
"""Widget representing a parameter"""
dataChanged
=
pyqtSignal
()
def
__init__
(
self
,
data
,
parent
=
None
):
"""Constructor
:param data: single parameter data
:param parent QWidget: parent widget
"""
super
(
RestrictedParameterWidget
,
self
).
__init__
(
parent
)
self
.
_type
=
data
.
get
(
"type"
,
None
)
self
.
default
=
data
.
get
(
"default"
,
None
)
if
self
.
_type
is
None
:
raise
RuntimeError
(
"Invalid parameter with no type"
)
if
self
.
default
is
None
:
raise
RuntimeError
(
"Invalid parameter with no default"
)
self
.
current_type
=
InputType
[
self
.
_type
.
upper
()]
self
.
modality
=
"single"
if
"choice"
in
data
:
self
.
modality
=
"choice"
elif
"range"
in
data
:
self
.
modality
=
"range"
layout
=
QHBoxLayout
(
self
)
if
self
.
_type
==
"string"
:
if
self
.
modality
==
"choice"
:
self
.
choices_combobox
=
QComboBox
()
layout
.
addWidget
(
self
.
choices_combobox
)
self
.
choices_combobox
.
currentIndexChanged
.
connect
(
self
.
dataChanged
)
self
.
choices_combobox
.
addItems
(
data
.
get
(
"choice"
,
[]))
self
.
__set_choice_default_value
(
self
.
default
)
else
:
self
.
single_ledit
=
QLineEdit
()
layout
.
addWidget
(
self
.
single_ledit
)
self
.
single_ledit
.
textChanged
.
connect
(
self
.
dataChanged
)
self
.
single_ledit
.
setText
(
self
.
default
)
elif
self
.
_type
==
"bool"
:
self
.
bool_checkbox
=
QCheckBox
()
layout
.
addWidget
(
self
.
bool_checkbox
)
self
.
bool_checkbox
.
stateChanged
.
connect
(
self
.
dataChanged
)
self
.
bool_checkbox
.
setChecked
(
self
.
default
)
else
:
# Numerical parameter type
if
self
.
modality
==
"choice"
:
self
.
choices_combobox
=
QComboBox
()
layout
.
addWidget
(
self
.
choices_combobox
)
self
.
choices_combobox
.
currentIndexChanged
.
connect
(
self
.
dataChanged
)
choices
=
data
.
get
(
"choice"
,
[])
str_choices
=
[
str
(
item
)
for
item
in
choices
]
self
.
choices_combobox
.
addItems
(
str_choices
)
self
.
__set_choice_default_value
(
str
(
self
.
default
))
else
:
min_value
=
self
.
current_type
.
numpy_info
.
min
max_value
=
self
.
current_type
.
numpy_info
.
max
if
self
.
modality
==
"range"
:
min_value
=
data
[
"range"
][
0
]
max_value
=
data
[
"range"
][
1
]
self
.
numerical_spinbox
=
NumpySpinBox
(
self
.
current_type
.
np_type
)
self
.
numerical_spinbox
.
setMinimum
(
min_value
)
self
.
numerical_spinbox
.
setMaximum
(
max_value
)
layout
.
addWidget
(
self
.
numerical_spinbox
)
self
.
numerical_spinbox
.
valueChanged
.
connect
(
self
.
dataChanged
)
self
.
numerical_spinbox
.
setValue
(
self
.
default
)
def
dump
(
self
):
"""Returns the json representation of this editor"""
output
=
None
if
self
.
_type
==
"string"
:
if
self
.
modality
==
"choice"
:
output
=
self
.
choices_combobox
.
currentText
()
else
:
output
=
self
.
single_ledit
.
text
()
elif
self
.
_type
==
"bool"
:
output
=
self
.
bool_checkbox
.
isChecked
()
else
:
# Numerical parameter type
if
self
.
modality
==
"choice"
:
output
=
self
.
choices_combobox
.
currentText
()
else
:
output
=
self
.
numerical_spinbox
.
value
()
return
output
def
load
(
self
,
data
):
"""Load the json object passed as parameter"""
if
self
.
_type
==
"string"
:
if
self
.
modality
==
"choice"
:
self
.
__set_choice_default_value
(
data
)
else
:
self
.
single_ledit
.
setText
(
data
)
elif
self
.
_type
==
"bool"
:
self
.
bool_checkbox
.
setChecked
(
data
)
else
:
# Numerical parameter type
if
self
.
modality
==
"choice"
:
self
.
__set_choice_default_value
(
str
(
data
))
else
:
self
.
numerical_spinbox
.
setValue
(
data
)
def
__set_choice_default_value
(
self
,
default_value
):
index
=
self
.
choices_combobox
.
findText
(
default_value
,
Qt
.
MatchFixedString
)
if
index
>=
0
:
self
.
choices_combobox
.
setCurrentIndex
(
index
)
else
:
raise
RuntimeError
(
"Invalid default value"
)
class
PlotterParameterViewer
(
QWidget
):
dataChanged
=
pyqtSignal
()
deletionRequested
=
pyqtSignal
()
def
__init__
(
self
,
name
,
data
,
parent
=
None
):
"""Constructor
:param name: parameter name
:param name: parameter data
:param parent QWidget: parent widget
"""
super
(
PlotterParameterViewer
,
self
).
__init__
(
parent
)
self
.
delete_button
=
QPushButton
(
self
.
tr
(
"-"
))
self
.
delete_button
.
setFixedSize
(
30
,
30
)
delete_layout
=
QHBoxLayout
()
delete_layout
.
addStretch
(
1
)
delete_layout
.
addWidget
(
self
.
delete_button
)
self
.
name_label
=
QLabel
()
self
.
description_label
=
QLabel
()
self
.
parameter_widget
=
RestrictedParameterWidget
(
data
)
self
.
parameter_layout
=
QHBoxLayout
()
self
.
form_layout
=
QFormLayout
()
self
.
form_layout
.
setFieldGrowthPolicy
(
QFormLayout
.
AllNonFixedFieldsGrow
)
self
.
form_layout
.
addRow
(
self
.
tr
(
"Name"
),
self
.
name_label
)
self
.
form_layout
.
addRow
(
self
.
tr
(
"Description"
),
self
.
description_label
)
self
.
form_layout
.
addRow
(
self
.
tr
(
"Parameter"
),
self
.
parameter_layout
)
layout
=
QVBoxLayout
(
self
)
layout
.
addLayout
(
delete_layout
)
layout
.
addLayout
(
self
.
form_layout
)
self
.
name_label
.
setText
(
name
)
self
.
description_label
.
setText
(
data
.
get
(
"description"
,
""
))
self
.
delete_button
.
clicked
.
connect
(
self
.
deletionRequested
)
self
.
parameter_layout
.
addWidget
(
self
.
parameter_widget
)
self
.
parameter_widget
.
dataChanged
.
connect
(
self
.
dataChanged
)
def
name
(
self
):
"""Name of the parameter"""
return
self
.
name_label
.
text
()
def
load
(
self
,
data
):
"""Load this widget with the content of json_data"""
self
.
parameter_widget
.
load
(
data
)
# self.parameter_layout.addWidget(self.parameter_widget)
def
dump
(
self
):
"""Returns the json representation of this set"""
parameter_name
=
self
.
name_label
.
text
()
parameter_data
=
self
.
parameter_widget
.
dump
()
json_data
=
{
parameter_name
:
parameter_data
}
<