Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • software_public/passoft/sohstationviewer
1 result
Show changes
Commits on Source (2)
import sys
import platform
import os
from typing import Optional, Dict
from PySide6 import QtWidgets
from PySide6.QtWidgets import QWidget, QDialog, QLineEdit
from PySide6.QtWidgets import QWidget, QDialog
from sohstationviewer.view.util.plot_type_info import plot_types
......@@ -15,26 +13,26 @@ from sohstationviewer.database.extract_data import (
from sohstationviewer.conf.dbSettings import modify_db_path
from sohstationviewer.view.db_config.value_color_helper.value_color_edit \
import ValueColorEdit
class EditSingleParamDialog(QDialog):
"""
Dialog to add info for channel not in database or edit the existing channel
"""
def __init__(self, parent: Optional[QWidget],
param: str):
def __init__(self, parent: Optional[QWidget], param: str):
"""
:param parent: the parent widget
:param chan_id: name of channel to be added/edited
:param data_type: type of the data being processed
:param param: parameter that categorizes the channel
"""
self.param = param
# # To skip on_param_chkbox_changed() when param is changed by the
# # program at the beginning
# self.param_changed_by_signal = False
# database info of the channel
self.channel_info: Dict = {}
# database info of the channel's parameter
self.param_info: Dict = {}
self.param_info: Dict = get_param_info(self.param)
# layout for all values' labels and editing widgets
self.all_values_layout = QtWidgets.QGridLayout()
super(EditSingleParamDialog, self).__init__(parent)
# parameter's plot type which decides the shape of the plot
......@@ -42,15 +40,9 @@ class EditSingleParamDialog(QDialog):
self.plot_type_cbo_box.addItems([""] + list(plot_types.keys()))
# value color in black mode
self.value_colorb_widget = QLineEdit(self)
self.value_colorb_widget.setPlaceholderText(
"Click edit button to add value color string")
self.value_colorb_widget.setToolTip("Priority from left to right")
self.value_colorb_widget = ValueColorEdit(self, 'B', '')
# value, color in white mode
self.value_colorw_widget = QLineEdit(self)
self.value_colorw_widget.setPlaceholderText(
"Click edit button to add value color string")
self.value_colorw_widget.setToolTip("Priority from left to right")
self.value_colorw_widget = ValueColorEdit(self, 'W', '')
# height of the plot
self.height_spnbox = QtWidgets.QSpinBox()
......@@ -79,26 +71,27 @@ class EditSingleParamDialog(QDialog):
main_layout = QtWidgets.QVBoxLayout()
self.setLayout(main_layout)
param_layout = QtWidgets.QGridLayout()
main_layout.addLayout(param_layout)
main_layout.addLayout(self.all_values_layout)
param_layout.addWidget(QtWidgets.QLabel('Plot Type'), 0, 0, 1, 1)
param_layout.addWidget(self.plot_type_cbo_box, 0, 1, 1, 1)
self.all_values_layout.addWidget(
QtWidgets.QLabel('Plot Type'), 0, 0, 1, 1)
self.all_values_layout.addWidget(self.plot_type_cbo_box, 0, 1, 1, 1)
param_layout.addWidget(QtWidgets.QLabel(
self.all_values_layout.addWidget(QtWidgets.QLabel(
'Value Color (black)'), 1, 0, 1, 1)
param_layout.addWidget(self.value_colorb_widget, 1, 1, 1, 1)
self.all_values_layout.addWidget(self.value_colorb_widget, 1, 1, 1, 1)
param_layout.addWidget(QtWidgets.QLabel(
self.all_values_layout.addWidget(QtWidgets.QLabel(
'Value Color (white)'), 2, 0, 1, 1)
param_layout.addWidget(self.value_colorw_widget, 2, 1, 1, 1)
self.all_values_layout.addWidget(self.value_colorw_widget, 2, 1, 1, 1)
param_layout.addWidget(QtWidgets.QLabel('Height'), 3, 0, 1, 1)
param_layout.addWidget(self.height_spnbox, 3, 1, 1, 1)
self.all_values_layout.addWidget(
QtWidgets.QLabel('Height'), 3, 0, 1, 1)
self.all_values_layout.addWidget(self.height_spnbox, 3, 1, 1, 1)
param_layout.addWidget(self.height_warning_label, 4, 0, 1, 2)
param_layout.addWidget(self.cancel_btn, 5, 0, 1, 1)
param_layout.addWidget(self.save_param_btn, 5, 1, 1, 1)
self.all_values_layout.addWidget(self.height_warning_label, 4, 0, 1, 2)
self.all_values_layout.addWidget(self.cancel_btn, 5, 0, 1, 1)
self.all_values_layout.addWidget(self.save_param_btn, 5, 1, 1, 1)
def connect_signals(self) -> None:
self.plot_type_cbo_box.currentTextChanged.connect(self.set_plot_type)
......@@ -128,10 +121,40 @@ class EditSingleParamDialog(QDialog):
self.height_spnbox.setValue(0)
else:
self.plot_type_cbo_box.setCurrentText(plot_type)
value_color_b = self.param_info['valueColorsB']
value_color_w = self.param_info['valueColorsW']
self.value_colorb_widget.setText(value_color_b)
self.value_colorw_widget.setText(value_color_w)
# value color in black mode
self.value_colorb_widget = self.set_value_color_widget(
'B', plot_type, self.param_info['valueColorsB'])
# value, color in white mode
self.value_colorw_widget = self.set_value_color_widget(
'W', plot_type, self.param_info['valueColorsW'])
self.all_values_layout.update()
def set_value_color_widget(self, background_color: str,
plot_type: str,
value_color: str) -> ValueColorEdit:
"""
Replace value_color_widget for the given background color with
the new one with new plot_type and value color.
:param background_color: 'B'/'W': flag indicating background color
:param plot_type: define type to plot the channel
:param value_color: define colors and how it is applied in the channel
plot
"""
if background_color == 'B':
widget = self.value_colorb_widget
row_idx = 1
errmsg_header = "Value Color(black) validation failed: "
else:
widget = self.value_colorw_widget
row_idx = 2
errmsg_header = "Value Color(white) validation failed: "
self.all_values_layout.removeWidget(widget)
widget.close()
widget = ValueColorEdit(
self, background_color, plot_type, value_color, errmsg_header)
self.all_values_layout.addWidget(widget, row_idx, 1, 1, 1)
return widget
def on_save_param(self):
"""
......@@ -140,9 +163,9 @@ class EditSingleParamDialog(QDialog):
plot_type = create_assign_string_for_db_query(
'plotType', self.plot_type_cbo_box.currentText())
value_colorb = create_assign_string_for_db_query(
'valueColorsB', self.value_colorb_widget.text())
'valueColorsB', self.value_colorb_widget.value_color_str)
value_colorw = create_assign_string_for_db_query(
'valueColorsW', self.value_colorw_widget.text())
'valueColorsW', self.value_colorw_widget.value_color_str)
height = f"height={self.height_spnbox.value()}"
sql = (f"UPDATE Parameters SET {plot_type}, {value_colorb}, "
f"{value_colorw}, {height} WHERE param='{self.param}'")
......@@ -152,9 +175,6 @@ class EditSingleParamDialog(QDialog):
if __name__ == '__main__':
modify_db_path()
os_name, version, *_ = platform.platform().split('-')
if os_name == 'macOS':
os.environ['QT_MAC_WANTS_LAYER'] = '1'
app = QtWidgets.QApplication(sys.argv)
# test linesDots. Ex: param: Input power supply current
......
......@@ -12,6 +12,8 @@ from PySide6.QtWidgets import QComboBox, QWidget
from sohstationviewer.conf.constants import ColorMode, ALL_COLOR_MODES
from sohstationviewer.view.db_config.value_color_helper.value_color_edit \
import ValueColorEdit
from sohstationviewer.view.db_config.param_helper import \
validate_value_color_str
from sohstationviewer.view.util.plot_type_info import plot_types
from sohstationviewer.view.db_config.db_config_dialog import (
UiDBInfoDialog,
......@@ -94,18 +96,24 @@ class ParamDialog(UiDBInfoDialog):
# used to make sure that the ValueColorEdit created later is forced
# to use the default value colors for the passed in plot type, so
# the actual string does not matter.
BAD_VALUE_COLORS = 'VC'
value_colors = (
widget_content
if original_plot_type == plot_type
else BAD_VALUE_COLORS
else plot_types[plot_type].get('default_value_color', '')
)
param = self.database_rows[row_idx][0]
errmsg_header = f"Row {row_idx} - Parameter '{param}': "
# We create a new ValueColorEdit and let the created widget decides
# what its content should be. This is far easier than managing the
# value we want the created widget to have.
widget = ValueColorEdit(self.data_table_widget, self.color_mode,
plot_type, value_colors)
plot_type, value_colors, errmsg_header)
change_signal = widget.value_color_edited
# When widget isn't valid, value color string in the widget will
# be set to default value, widget's value need to be marked as
# changed.
if not widget.is_valid:
self.changes_array[row_idx][2] = True
else:
widget, change_signal = super().create_widget(
widget_content, choices, range_values, field_name
......@@ -203,34 +211,27 @@ class ParamDialog(UiDBInfoDialog):
if not is_valid_general_row:
return is_valid_general_row, msg
param = row_content[0]
plot_type = row_content[1]
value_colors_string = row_content[2]
header = f"Row {row_id} - Parameter '{param}': "
if value_colors_string == "" and plot_type == 'multiColorDots':
err_msg = (f"Row {row_id}: multiColorDots type requires some "
err_msg = (f"{header}multiColorDots type requires some "
f"ValueColors value.")
return False, err_msg
if value_colors_string != "":
if plot_type in self.plot_types_with_value_colors:
value_colors = value_colors_string.split("|")
for vc in value_colors:
vc = vc.strip()
plot_type_info = plot_types.get(plot_type)
if plot_type_info is None:
continue
if not plot_type_info['pattern'].match(vc):
msg = (f"Row {row_id}: The ValueColors in this row is "
f"'{vc}', which doesn't match the required "
f"format.\n\n"
f"{plot_type_info['instruction']}")
return False, msg
is_valid, err_msg = validate_value_color_str(
value_colors_string, plot_type)
if not is_valid:
return False, f"{header}{err_msg}"
else:
if plot_type == "":
msg = (f"Row {row_id}: No ValueColors should be entered "
msg = (f"{header}No ValueColors should be entered "
f"when no Plot Type is selected.")
else:
msg = (f"Row {row_id}: No ValueColors is required for "
msg = (f"{header}No ValueColors is required for "
f"Plot Type {plot_type}.")
return False, msg
return True, ''
......
from typing import Tuple
import re
from sohstationviewer.view.util.plot_type_info import plot_types
def validate_value_color_str(value_colors_string: str,
plot_type: str) -> Tuple[bool, str]:
"""
Validate the given value_colors_string to see if:
+ Total ValueColors match with requirement if any.
+ It match the pattern of the plot_type.
+ The value sections aren't duplicated.
+ Numbers are in increasing order.
:param value_colors_string: string of value colors split by '|'
:param plot_type: type of plot of which info need to be used to validate
the value_colors_string.
:return: validation flag, error message.
"""
plot_type_info = plot_types.get(plot_type)
value_colors = value_colors_string.split("|")
if 'total_value_color_required' in plot_type_info:
if len(value_colors) != plot_type_info['total_value_color_required']:
msg = (f"Total ValueColors in "
f"'{value_colors_string}' "
f"is {len(value_colors)} while it requires "
f"{plot_type_info['total_value_color_required']} for "
f"plot type {plot_type}.")
return False, msg
values = []
numbers = []
for vc in value_colors:
vc = vc.strip()
if plot_type_info is None:
continue
if not plot_type_info['pattern'].match(vc):
msg = (f"A ValueColor in '{value_colors_string}', '{vc}', "
f"doesn't match the required format of\n\n"
f"{plot_type_info['description']}")
return False, msg
val = vc.split(":")[0]
if val in values:
msg = (f"Duplicated value '{val}' in ValueColors string "
f"'{value_colors_string}' isn't allowed.")
return False, msg
number = re.sub('<|=', '', val)
try:
number = float(number)
if numbers and number < max(numbers):
msg = (f"Number '{number}' is out of increasing order in "
f"ValueColors string '{value_colors_string}'.")
return False, msg
numbers.append(number)
except ValueError:
pass
values.append(val)
return True, ''
......@@ -2,7 +2,7 @@ from typing import Optional, Type
from PySide6 import QtWidgets, QtGui, QtCore
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QWidget, QTextEdit
from PySide6.QtWidgets import QWidget, QTextEdit, QMessageBox
from sohstationviewer.conf.constants import ROOT_PATH
from sohstationviewer.view.db_config.value_color_helper.\
......@@ -20,6 +20,8 @@ from sohstationviewer.view.db_config.value_color_helper.\
edit_value_color_dialog import DotForTimeDialog
from sohstationviewer.view.db_config.value_color_helper.functions import \
prepare_value_color_html
from sohstationviewer.view.db_config.param_helper import \
validate_value_color_str
from sohstationviewer.view.util.plot_type_info import plot_types
......@@ -29,12 +31,18 @@ plot_types_with_value_colors = [
class ValueColorEdit(QTextEdit):
"""
Widget to display valueColors and call a dialog to edit tha value
"""
value_color_edited = QtCore.Signal(str)
def __init__(self, parent: Optional[QWidget], background: str,
plot_type: str, value_color_str: str = ''):
plot_type: str, value_color_str: str = '',
errmsg_header: str = ''):
"""
Widget to display valueColors and call a dialog to edit tha value
value_color_str will be validated, if not valid
'default_value_color' of the plot_type will be used instead of
the given value_color_str.
:param parent: the parent widget
:param background: 'B'/'W': flag indicating background color
:param plot_type: the plot type the editor used to format and validate
......@@ -42,16 +50,35 @@ class ValueColorEdit(QTextEdit):
:param value_color_str: the initial value colors shown in the editor.
If this does not fit the format required by the given plot type,
use the default value colors for the plot type instead.
:param errmsg_header: header for error message to help user identify
the box that has value color string error.
"""
QtWidgets.QTextEdit.__init__(self, parent)
self.set_background(background)
# type of channel's plot
# Background color for setting border color match with background
self.background: str = 'black' if background == 'B' else 'white'
self.set_background_color(background)
# Type of channel's plot
self.plot_type: str = plot_type
# Original value color string to know if value color string has been
# changed.
self.org_value_color_str: str = value_color_str
# Current value color string displayed on widget
self.value_color_str: str = ''
# Flag showing if original value color string passes validation
self.is_valid: bool = True
if plot_type in plot_types_with_value_colors:
try:
self.is_valid, err_msg = validate_value_color_str(
value_color_str, plot_type)
if not self.is_valid:
raise ValueError(
f"{errmsg_header}{err_msg}\n\n The ValueColors string "
f"will be replaced with {plot_type}'s default one.")
self.set_value_color(value_color_str)
except ValueError:
except ValueError as e:
QMessageBox.critical(
self, f'Invalid ValueColors:{plot_type}', str(e))
print(str(e)) # Show on terminal to track back later.
# We set the value colors to the default value (specified in
# plot_type_info.py) when there is a problem with the given
# value colors string.
......@@ -97,7 +124,14 @@ class ValueColorEdit(QTextEdit):
lambda: self.value_color_edited.emit(self.value_color_str)
)
def set_background(self, background: str):
def set_border_color(self, color):
"""
Set border color for the widget.
:param color: color to set to border
"""
self.setStyleSheet("QTextEdit {border: 2px solid %s;}" % color)
def set_background_color(self, background: str):
"""
Set black background for user to have better feeling how the colors
displayed on black background. Text and PlaceholderText's colors
......@@ -115,12 +149,27 @@ class ValueColorEdit(QTextEdit):
def set_value_color(self, value_color_str: str) -> None:
"""
Set value_color_str, value_color_edit_dialog and display value color
string in html to show color for user to have the feeling.
string in html to show color for user to have the feeling.
:param value_color_str: string for value color to be saved in DB
"""
self.value_color_str = value_color_str
value_color_html = prepare_value_color_html(self.value_color_str)
self.setHtml(value_color_html)
self.set_border_color_according_to_value_color_status()
def set_border_color_according_to_value_color_status(self):
"""
Set border color according to current value color string.
+ Red for changed
+ Same color as background (no border) for other case.
"""
if self.value_color_str != self.org_value_color_str:
# Show that value_color_str has been changed
self.set_border_color('red')
else:
# Reset border the same color as background
self.set_border_color(self.background)
def edit(self):
"""
......@@ -136,10 +185,18 @@ class ValueColorEdit(QTextEdit):
'multiColorDotsEqualOnLowerBound': MultiColorDotLowerEqualDialog,
'upDownDots': UpDownDialog,
}
# set border color blue showing that the widget is being edited
self.set_border_color('blue')
edit_dialog = plot_type_dialog_map[self.plot_type](
self, self.value_color_str
)
edit_dialog.accepted.connect(
lambda: self.set_value_color(edit_dialog.value_color_str)
)
# To set border in case cancel is clicked. It's necessary because the
# above set_value_color won't be called for clicking cancel.
edit_dialog.finished.connect(
self.set_border_color_according_to_value_color_status)
edit_dialog.open()
......@@ -20,19 +20,7 @@ plot_types = {
),
"plot_function": "plot_lines_dots",
"value_pattern": re.compile('^(L|D|Z|Line|Dot|Zero)$'),
"pattern": re.compile(f'^(?:Line|Dot|Zero):{color_regex}$'),
"instruction": (
"Ex: Line:#00FF00|Dot:#FF0000 means\n"
" Lines are plotted with color #00FF00\n"
" Dots are plotted with color #FF0000\n"
"If Dot is not defined, dots won't be displayed.\n"
"If L is not defined, lines will be plotted with color "
"#00FF00.\n"
"Optionally, a color for points with value 0 can be defined "
"This is currently only used for channel GPS Lk/Unlk.\n"
"Ex: Zero:#0000FF means points with value are plotted with "
"color #0000FF."
),
"pattern": re.compile(f'^$|^(?:Line|Dot|Zero):{color_regex}$'),
"default_value_color": "Line:#00FF00"
},
'linesSRate': {
......@@ -44,12 +32,18 @@ plot_types = {
"plot_function": "plot_lines_mass_pos"
},
'triColorLines': {
"description": "Three lines with three different colors for "
"values -1, 0, 1.",
"description": (
"Three values -1,0,1 in three lines with three "
"different colors according to valueColors:\n"
"Ex: -1:#FF0000|0:#00FF00|1:#0000FF means\n"
"value = -1 => plot on line y=-1 with #FF0000 color\n"
"value = 0 => plot on line y=0 with #00FF00 color\n"
"value = 1 => plot on line y=1 with #0000FF color"),
"plot_function": "plot_tri_colors",
"value_pattern": re.compile('^-?[10]?$'),
"pattern": re.compile(f'^-?[10]:{color_regex}$'),
"default_value_color": "-1:#FF0000|0:#00FF00|1:#0000FF"
"default_value_color": "-1:#FF0000|0:#00FF00|1:#0000FF",
"total_value_color_required": 3
},
'dotForTime': {
"description": (
......@@ -58,8 +52,9 @@ plot_types = {
"Ex: Color:#00FF00"),
"plot_function": "plot_dot_for_time",
"value_pattern": re.compile('^C|(Color)$'),
"pattern": re.compile(f'^Color:{color_regex}$'),
"default_value_color": "Color:#00FF00"
"pattern": re.compile(f'^$|^Color:{color_regex}$'),
"default_value_color": "Color:#00FF00",
"total_value_color_required": 1
},
'multiColorDotsEqualOnUpperBound': {
"description": (
......@@ -75,12 +70,6 @@ plot_types = {
"pattern": re.compile(
fr'^(\+|<=)?[0-9]+\.?[0-9]?<?:(?:{color_regex}|not plot)$'
),
"instruction": (
"Ex: <=-1:not plot|<=0:#FF0000|0<:#FF00FF means:\n"
" value <= -1 => not plot\n"
" -1 < value <= 0 => plot with #FF0000 color\n"
" 0 < value => plot with #FF00FF color\n"
),
"default_value_color": "<=0:#FF0000|0<:#FF00FF"
},
'multiColorDotsEqualOnLowerBound': {
......@@ -97,26 +86,19 @@ plot_types = {
"pattern": re.compile(
fr'^[=<]?[0-9]\.?[0-9]?:(?:{color_regex}|not plot)'
),
"instruction": (
"Ex: <-1:not plot|<0:#FF0000|=0:#FF00FF means:\n"
" value < -1 => not plot\n"
" -1 =< value < 0 => plot with #FF0000 color\n"
" value >= 0 => plot with #FF00FF color\n"
),
"default_value_color": "<0:#FF0000|=0:#00FF00"
},
'upDownDots': {
"description": (
"Show data with 2 different values: first down/ second up. "
"Colors defined by ValueColors.\nEx: Down:#FF0000|Up:#00FFFF"),
"plot_function": 'plot_up_down_dots',
"value_pattern": re.compile("^(0|1|Up|Down)$"),
"pattern": re.compile(f"^(?:Up|Down):{color_regex}$"),
"instruction": (
"Colors looks like #12ABEF\n"
"Two different values: one above the center line, the other "
"under it. Colors defined by ValueColors.\n"
"Ex: Down:#FF0000|Up:#00FFFF means:\n"
" value == 1 => plot above center line with #00FFFF color\n"
" value == 0 => plot under center line with #FF0000 color"),
"default_value_color": "Down:#FF0000|Up:#00FFFF"
"plot_function": 'plot_up_down_dots',
"value_pattern": re.compile("^(0|1|Up|Down)$"),
"pattern": re.compile(f"^(?:Up|Down):{color_regex}$"),
"default_value_color": "Down:#FF0000|Up:#00FFFF",
"total_value_color_required": 2
}
}
from sohstationviewer.view.db_config.param_helper import \
validate_value_color_str
from tests.base_test_case import BaseTestCase
class TestValidateValueColorStr(BaseTestCase):
def test_lines_dots(self):
with self.subTest("Both line and dot value"):
result = validate_value_color_str(
"Line:#00FF00|Dot:#00FF00", 'linesDots')
self.assertEqual(result, (True, ''))
with self.subTest("Only line value"):
result = validate_value_color_str("Line:#00FF00", 'linesDots')
self.assertEqual(result, (True, ''))
with self.subTest("Only dot value"):
result = validate_value_color_str("Dot:#00FF00", 'linesDots')
self.assertEqual(result, (True, ''))
with self.subTest("Line, dot, zero"):
result = validate_value_color_str(
"Line:#00FF00|Dot:#00FF00|Zero:#FF0000", 'linesDots')
self.assertEqual(result, (True, ''))
with self.subTest("Empty str"):
result = validate_value_color_str("", 'linesDots')
self.assertEqual(result, (True, ''))
with self.subTest("Invalid string with row_id != -1"):
result = validate_value_color_str("Li:#00FF00", 'linesDots')
self.assertEqual(
result,
(False,
"A ValueColor in 'Li:#00FF00', 'Li:#00FF00',"
" doesn't match the required format of\n\n"
"Lines, one color dots. Dot or line/color mapping "
"defined by ValueColors.\n"
"Ex: Line:#00FF00|Dot:#FF0000 means\n"
" Lines are plotted with color #00FF00\n"
" Dots are plotted with color #FF0000\n"
"If Dot is not defined, dots won't be displayed.\n"
"If L is not defined, lines will be plotted with color "
"#00FF00.\n"
"Optionally, a color for points with value 0 can be defined "
"This is currently only used for channel GPS Lk/Unlk.\n"
"Ex: Zero:#0000FF means points with value are plotted with "
"color #0000FF.")
)
def test_up_down_dots(self):
with self.subTest("Valid value color string"):
result = validate_value_color_str(
"Down:#FF0000|Up:#00FF00", 'upDownDots')
self.assertEqual(result, (True, ''))
with self.subTest("Only Down value"):
result = validate_value_color_str(
"Down:#FF0000", 'upDownDots')
self.assertEqual(
result,
(False,
"Total ValueColors in 'Down:#FF0000' "
"is 1 while it requires 2 for plot type upDownDots."))
with self.subTest("Only Up value"):
result = validate_value_color_str("Up:#FF0000", 'upDownDots')
self.assertEqual(
result,
(False,
"Total ValueColors in 'Up:#FF0000' is 1 "
"while it requires 2 for plot type upDownDots."))
with self.subTest("Invalid value color string"):
result = validate_value_color_str(
"Up:#FF0000|Do:#FFFF00", 'upDownDots')
self.assertEqual(
result,
(False,
"A ValueColor in "
"'Up:#FF0000|Do:#FFFF00', 'Do:#FFFF00', "
"doesn't match the required format of\n\n"
"Two different values: one above the center line, "
"the other under it. Colors defined by ValueColors.\n"
"Ex: Down:#FF0000|Up:#00FFFF means:\n"
" value == 1 => plot above center line with #00FFFF color\n"
" value == 0 => plot under center line with #FF0000 color"))
def test_tri_color_line(self):
with self.subTest("Valid value color string"):
result = validate_value_color_str(
'-1:#FF00FF|0:#FF0000|1:#00FF00', 'triColorLines')
self.assertEqual(result, (True, ''))
with self.subTest("Missing one value color"):
result = validate_value_color_str(
'-1:#FF00FF|0:#FF0000', 'triColorLines')
self.assertEqual(
result,
(False,
"Total ValueColors in "
"'-1:#FF00FF|0:#FF0000' is 2 while "
'it requires 3 for plot type triColorLines.'))
with self.subTest("Invalid value color string"):
result = validate_value_color_str(
'-1:#FF00FF|0:#FF0000|2:#00FF00', 'triColorLines')
self.assertEqual(
result,
(False,
"A ValueColor in "
"'-1:#FF00FF|0:#FF0000|2:#00FF00', '2:#00FF00', "
"doesn't match the required format of\n\n"
"Three values -1,0,1 in three lines with three different "
"colors according to valueColors:\n"
"Ex: -1:#FF0000|0:#00FF00|1:#0000FF means\n"
"value = -1 => plot on line y=-1 with #FF0000 color\n"
"value = 0 => plot on line y=0 with #00FF00 color\n"
"value = 1 => plot on line y=1 with #0000FF color"
))
def test_dot_for_time(self):
with self.subTest("Valid value color string"):
result = validate_value_color_str("Color:#00FF00", 'dotForTime')
self.assertEqual(result, (True, ''))
with self.subTest("Empty string"):
result = validate_value_color_str("", 'dotForTime')
self.assertEqual(result, (True, ''))
with self.subTest("Missing one value color"):
result = validate_value_color_str(
'Color:#FF00FF|Color:#FF0000', 'dotForTime')
self.assertEqual(
result,
(False,
"Total ValueColors in "
"'Color:#FF00FF|Color:#FF0000' is 2 while "
"it requires 1 for plot type dotForTime."))
with self.subTest("Invalid value color string"):
result = validate_value_color_str("Colo:#00FF00", 'dotForTime')
self.assertEqual(
result,
(False,
"A ValueColor in "
"'Colo:#00FF00', 'Colo:#00FF00', "
"doesn't match the required format of\n\n"
"Dots according to timestamp.\n"
"Color defined by ValueColors.\n"
"Ex: Color:#00FF00"
))
def test_multi_color_dots_equal_on_upper_bound(self):
with self.subTest("Valid value color string"):
result = validate_value_color_str(
'<=0:not plot|<=1:#FFFF00|<=2:#00FF00|2<:#FF00FF',
'multiColorDotsEqualOnUpperBound')
self.assertEqual(result, (True, ''))
with self.subTest("Repeated value"):
result = validate_value_color_str(
'<=0:not plot|<=2:#FFFF00|<=2:#00FF00|2<:#FF00FF',
'multiColorDotsEqualOnUpperBound')
self.assertEqual(
result,
(False,
"Duplicated value '<=2' "
"in ValueColors string "
"'<=0:not plot|<=2:#FFFF00|<=2:#00FF00|2<:#FF00FF' "
"isn't allowed."))
with self.subTest("Disordered value"):
result = validate_value_color_str(
'<=0:not plot|<=2:#FFFF00|<=1:#00FF00|2<:#FF00FF',
'multiColorDotsEqualOnUpperBound')
self.assertEqual(
result,
(False,
"Number '1.0' is out of "
"increasing order in ValueColors string "
"'<=0:not plot|<=2:#FFFF00|<=1:#00FF00|2<:#FF00FF'."))
def test_multi_color_dots_equal_on_lower_bound(self):
with self.subTest("Valid value color string"):
result = validate_value_color_str(
'<3:#FF0000|<3.3:#FFFF00|=3.3:#00FF00',
'multiColorDotsEqualOnLowerBound')
self.assertEqual(result, (True, ''))
with self.subTest("Repeated value"):
result = validate_value_color_str(
'<3:#FF0000|<3.3:#FFFA00|<3.3:#FFFF00|=3.3:#00FF00',
'multiColorDotsEqualOnLowerBound')
self.assertEqual(
result,
(False,
"Duplicated value '<3.3' in "
"ValueColors string "
"'<3:#FF0000|<3.3:#FFFA00|<3.3:#FFFF00|=3.3:#00FF00' "
"isn't allowed."))
with self.subTest("Disordered value"):
result = validate_value_color_str(
'<3:#FF0000|<3.3:#FFFA00|<3.1:#FFFF00|=3.3:#00FF00',
'multiColorDotsEqualOnLowerBound')
self.assertEqual(
result,
(False,
"Number '3.1' is out of "
"increasing order in ValueColors string "
"'<3:#FF0000|<3.3:#FFFA00|<3.1:#FFFF00|=3.3:#00FF00'."))