diff --git a/setup.py b/setup.py index d3ebbe37e3cfb977c71ff8756d45c84ef83534b4..339530a3b58b92191dc5402a8bd69ec0bc37adcd 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ setup( ], }, install_requires=['obspy', - 'PySide6', + 'PySide2', 'matplotlib', 'numpy'], setup_requires=[], diff --git a/sohstationviewer.py b/sohstationviewer.py index 9f868de6a41cfdc409c36d1c65c6fe64367719b1..4aa32c8b2c1996cb9a7134976aeb1748990637d7 100755 --- a/sohstationviewer.py +++ b/sohstationviewer.py @@ -4,8 +4,8 @@ import os import sys import traceback -from PySide6 import QtWidgets -from PySide6.QtWidgets import QMessageBox, QErrorMessage +from PySide2 import QtWidgets +from PySide2.QtWidgets import QMessageBox from sohstationviewer.view.main_window import MainWindow from sohstationviewer.conf.config_processor import ( @@ -14,6 +14,17 @@ from sohstationviewer.conf.config_processor import ( ) +# Enable Layer-backing for MacOs version >= 11 +# Only needed if using the pyside2 library with version>=5.15. +# Layer-backing is always enabled in pyside6. +os_name, version, *_ = platform.platform().split('-') +# if os_name == 'macOS' and version >= '11': +# mac OSX 11.6 appear to be 10.16 when read with python and still required this +# environment variable +if os_name == 'macOS': + os.environ['QT_MAC_WANTS_LAYER'] = '1' + + def main(): app = QtWidgets.QApplication(sys.argv) wnd = MainWindow() @@ -51,7 +62,7 @@ def main(): sys.exit(1) config.apply_config(wnd) wnd.show() - sys.exit(app.exec()) + sys.exit(app.exec_()) if __name__ == '__main__': diff --git a/sohstationviewer/conf/config_processor.py b/sohstationviewer/conf/config_processor.py index b71710f2efbaf11bc5f193349a2746f432e7652b..17cc73d9b39a96addc8039d26ee1508d76e5cf0b 100644 --- a/sohstationviewer/conf/config_processor.py +++ b/sohstationviewer/conf/config_processor.py @@ -1,7 +1,7 @@ import configparser from pathlib import Path -from PySide6 import QtCore +from PySide2 import QtCore from sohstationviewer.conf import constants from sohstationviewer.conf.constants import CONFIG_PATH diff --git a/sohstationviewer/controller/plotting_data.py b/sohstationviewer/controller/plotting_data.py index 186ebe1d2a33c4ffb1e7968281c61b77085a4822..0476d67373fcdbba305caa2bd2be522e3625d945 100755 --- a/sohstationviewer/controller/plotting_data.py +++ b/sohstationviewer/controller/plotting_data.py @@ -1,77 +1,17 @@ """ Functions that process data for plotting """ - import math from typing import List, Union, Optional, Tuple, Dict from obspy import UTCDateTime from sohstationviewer.conf import constants as const -from sohstationviewer.view.util.enums import LogType + MAX_INT = 1E100 MAX_FLOAT = 1.0E100 -# TODO: put this in DB -mass_pos_volt_ranges = {"regular": [0.5, 2.0, 4.0, 7.0], - "trillium": [0.5, 1.8, 2.4, 3.5]} -mass_pos_color_pallets = {"B": ["C", "G", "Y", "R", "M"], - "W": ["B", "B", "B", "B", "B"]} - - -def get_masspos_value_colors( - range_opt: str, chan_id: str, c_mode: str, - processing_log: List[Tuple[str, LogType]], - ret_type: str = 'str' -) -> Optional[Union[str, List[Tuple[float, str]]]]: - """ - Create a map between value and color based on given rangeOpt and c_mode to - display mass position plots. - :param range_opt: massPosVoltRangeOpt got from Options Menu - MP coloring - in Main Window to define different values for mass position. - (regular/trillium) - :param chan_id: ID of the channel - :param c_mode: color mode (B/W) - :param processing_log: list of processing info and type - :param ret_type: request return type - :return: [(value, color), (value, color) ...] - if retType is 'str', return "value:color|value:color" - """ - - if range_opt.lower() not in mass_pos_volt_ranges.keys(): - processing_log.append( - ( - f"{chan_id}: The current selected Mass Position color range is" - f" '{range_opt}' isn't allowable. The accept ranges are: " - f"{', '.join(mass_pos_volt_ranges.keys())}", - LogType.ERROR - ) - ) - return - mass_pos_volt_range = mass_pos_volt_ranges[range_opt] - mass_pos_color_pallet = mass_pos_color_pallets[c_mode] - value_colors = [] - for i in range(len(mass_pos_volt_range)): - if ret_type == 'str': - value_colors.append( - "%s:%s" % (mass_pos_volt_range[i], mass_pos_color_pallet[i])) - else: - value_colors.append( - (mass_pos_volt_range[i], mass_pos_color_pallet[i]) - ) - if i == len(mass_pos_volt_range) - 1: - if ret_type == 'str': - value_colors.append( - "%s:+%s" % ( - mass_pos_volt_range[i], mass_pos_color_pallet[i + 1])) - else: - value_colors.append( - (mass_pos_volt_range[i], mass_pos_color_pallet[i + 1])) - if ret_type == 'str': - return '|'.join(value_colors) - return value_colors - def format_time(time: Union[UTCDateTime, float], date_mode: str, time_mode: Optional[str] = None) -> str: diff --git a/sohstationviewer/controller/processing.py b/sohstationviewer/controller/processing.py index 3c21322720572945854afb852b5f644701efd2f6..05b42c809ce343d2722bb5ac2424b24f9e469116 100644 --- a/sohstationviewer/controller/processing.py +++ b/sohstationviewer/controller/processing.py @@ -9,9 +9,9 @@ import re from pathlib import Path from typing import List, Optional, Dict, Tuple, Union -from PySide6.QtCore import QEventLoop, Qt -from PySide6.QtGui import QCursor -from PySide6.QtWidgets import QTextBrowser, QApplication +from PySide2.QtCore import QEventLoop, Qt +from PySide2.QtGui import QCursor +from PySide2.QtWidgets import QTextBrowser, QApplication from obspy.io import reftek from obspy import UTCDateTime diff --git a/sohstationviewer/controller/util.py b/sohstationviewer/controller/util.py index 862276ff49bd00e11657b443de08a1571459f035..705e82cc591ac50ff62ef1166e5d4e635841482d 100644 --- a/sohstationviewer/controller/util.py +++ b/sohstationviewer/controller/util.py @@ -9,8 +9,8 @@ from datetime import datetime from pathlib import Path from typing import Tuple, List, Union, Dict -from PySide6 import QtCore -from PySide6.QtWidgets import QTextBrowser +from PySide2 import QtCore +from PySide2.QtWidgets import QTextBrowser from obspy import UTCDateTime @@ -179,7 +179,9 @@ def get_val(text: str) -> float: Get the value part of a string with non-number substring following. :param text: value string including unit :return: value part including +/-, remove str that follows + and remove '=' prefix """ + text = text.replace('=', '') re_val = '^\+?\-?[0-9]+\.?[0-9]?' # noqa: W605 return float(re.search(re_val, text).group()) diff --git a/sohstationviewer/database/extract_data.py b/sohstationviewer/database/extract_data.py index 461358d7661118e855c85e80fd31a0e58f4dadf2..55997e90a683cc8d119fa26aebc419ebb0abe3ff 100755 --- a/sohstationviewer/database/extract_data.py +++ b/sohstationviewer/database/extract_data.py @@ -54,6 +54,10 @@ def get_chan_plot_info(org_chan_id: str, data_type: str, chan_db_info[0]['fixPoint'] = ( 0 if chan_db_info[0]['fixPoint'] is None else chan_db_info[0]['fixPoint']) + if (chan_db_info[0]['plotType'] == 'multiColorDots' and + chan_db_info[0]['valueColors'] in [None, 'None', '']): + chan_db_info[0]['valueColors'] = '*:W' if color_mode == 'B' else '*:B' + if chan_db_info[0]['label'].strip() == '': chan_db_info[0]['label'] = chan_db_info[0]['channel'] elif seismic_label is not None: diff --git a/sohstationviewer/database/soh.db b/sohstationviewer/database/soh.db index 96af7289f51f30934b2ce12d9c49aa21364a2ebe..4674efe8717e532b15c6018436d1834b1730edb9 100755 Binary files a/sohstationviewer/database/soh.db and b/sohstationviewer/database/soh.db differ diff --git a/sohstationviewer/model/data_loader.py b/sohstationviewer/model/data_loader.py index bd9793e98adc23fda87472750adee5d22b7eb47c..31f8dd1fb260ebcb6356a257366dbc4a5214be71 100644 --- a/sohstationviewer/model/data_loader.py +++ b/sohstationviewer/model/data_loader.py @@ -5,7 +5,7 @@ import traceback from pathlib import Path from typing import Union, List, Optional -from PySide6 import QtCore, QtWidgets +from PySide2 import QtCore, QtWidgets from sohstationviewer.conf import constants from sohstationviewer.controller.util import display_tracking_info @@ -57,8 +57,11 @@ class DataLoaderWorker(QtCore.QObject): # the main thread. self.notification.connect(display_tracking_info) self.end_msg = None + # to change display tracking info to error when failed + self.process_failed: bool = False def run(self): + self.process_failed = False folders = (self.list_of_rt130_paths if self.list_of_dir == [''] else self.list_of_dir) folders_str = ', '.join([dir.name for dir in folders]) @@ -98,10 +101,17 @@ class DataLoaderWorker(QtCore.QObject): fmt = traceback.format_exc() self.end_msg = (f"Some in {folders_str} can't be read " f"due to error: {str(fmt)}") + self.process_failed = True self.failed.emit() else: - self.end_msg = f'Finished loading data stored in {folders_str}' - self.finished.emit(data_object) + if data_object.selected_key is None: + self.process_failed = True + self.end_msg = ("No data was found. Please check the selected " + "time range and channel list.") + self.failed.emit() + else: + self.end_msg = f'Finished loading data stored in {folders_str}' + self.finished.emit(data_object) class DataLoader(QtCore.QObject): @@ -210,8 +220,11 @@ class DataLoader(QtCore.QObject): Currently does the following: - Set running state of self to False """ + log_type = LogType.INFO + if self.worker.process_failed: + log_type = LogType.ERROR display_tracking_info(self.worker.tracking_box, - self.worker.end_msg, LogType.INFO) + self.worker.end_msg, log_type) self.running = False @QtCore.Slot() diff --git a/sohstationviewer/model/general_data/general_data.py b/sohstationviewer/model/general_data/general_data.py index 7f82a4b8fb314315a8ba76868cc41d96145fa320..be971f03bf6f1dd06e237e2066e7dd691ea2ba73 100644 --- a/sohstationviewer/model/general_data/general_data.py +++ b/sohstationviewer/model/general_data/general_data.py @@ -7,8 +7,8 @@ import traceback from obspy import UTCDateTime -from PySide6 import QtCore -from PySide6 import QtWidgets +from PySide2 import QtCore +from PySide2 import QtWidgets from sohstationviewer.controller.util import \ display_tracking_info, get_valid_file_count, validate_file, validate_dir diff --git a/sohstationviewer/view/calendar/calendar_dialog.py b/sohstationviewer/view/calendar/calendar_dialog.py index 48a491bbf2af868bd9d7425ab3e9ec2b9e3ab718..ceb2e9ea16242dcc118b2255bb1c9203b442075a 100644 --- a/sohstationviewer/view/calendar/calendar_dialog.py +++ b/sohstationviewer/view/calendar/calendar_dialog.py @@ -1,4 +1,4 @@ -from PySide6 import QtWidgets +from PySide2 import QtWidgets from sohstationviewer.view.ui.calendar_ui_qtdesigner import Ui_CalendarDialog diff --git a/sohstationviewer/view/calendar/calendar_widget.py b/sohstationviewer/view/calendar/calendar_widget.py index 87e1343688cc661d82e14ebd40d577748a261a57..07996636a2233d8ca31fe11f1e9408a3b9a20e2f 100644 --- a/sohstationviewer/view/calendar/calendar_widget.py +++ b/sohstationviewer/view/calendar/calendar_widget.py @@ -1,4 +1,4 @@ -from PySide6 import QtCore, QtGui, QtWidgets +from PySide2 import QtCore, QtGui, QtWidgets class CalendarWidget(QtWidgets.QCalendarWidget): diff --git a/sohstationviewer/view/channel_prefer_dialog.py b/sohstationviewer/view/channel_prefer_dialog.py index 6b8b1b6b2825a9f235d8ba3a2ed27f2cc2071adb..d8c52092e14d41fcd1120b58b2085b3a8935f719 100755 --- a/sohstationviewer/view/channel_prefer_dialog.py +++ b/sohstationviewer/view/channel_prefer_dialog.py @@ -1,8 +1,8 @@ from typing import Dict, List, Union from pathlib import Path -from PySide6 import QtWidgets, QtCore -from PySide6.QtWidgets import QDialogButtonBox, QDialog, QPlainTextEdit, \ +from PySide2 import QtWidgets, QtCore +from PySide2.QtWidgets import QDialogButtonBox, QDialog, QPlainTextEdit, \ QMainWindow from sohstationviewer.database.process_db import ( diff --git a/sohstationviewer/view/create_muti_buttons_dialog.py b/sohstationviewer/view/create_muti_buttons_dialog.py index e731acceafeeaa54ee14503feb0c68d1cd54b6b0..991ae257f11c88aefbd4887cbd08d06bc27b26ce 100644 --- a/sohstationviewer/view/create_muti_buttons_dialog.py +++ b/sohstationviewer/view/create_muti_buttons_dialog.py @@ -1,6 +1,6 @@ from typing import List -from PySide6 import QtWidgets +from PySide2 import QtWidgets def create_multi_buttons_dialog( diff --git a/sohstationviewer/view/date_edit_with_doy.py b/sohstationviewer/view/date_edit_with_doy.py index a93fa143ade745f2250bc0a71a2f9567695b27e1..469d7c482421f69954a6c2d33de0e64f58a2e7b8 100644 --- a/sohstationviewer/view/date_edit_with_doy.py +++ b/sohstationviewer/view/date_edit_with_doy.py @@ -1,9 +1,9 @@ -from PySide6.QtCore import Qt, QDate -from PySide6.QtGui import ( - QKeyEvent, QWheelEvent, QContextMenuEvent, QAction +from PySide2.QtCore import Qt, QDate +from PySide2.QtGui import ( + QKeyEvent, QWheelEvent, QContextMenuEvent ) -from PySide6.QtWidgets import ( - QDateEdit, QLineEdit, QMenu, +from PySide2.QtWidgets import ( + QDateEdit, QLineEdit, QMenu, QAction, ) diff --git a/sohstationviewer/view/db_config/db_config_dialog.py b/sohstationviewer/view/db_config/db_config_dialog.py index 0678303b1554d4fc85b99c3a0a7bc51818ffe9d0..eefd87af62eea9d320108381bf2c674823ee8261 100755 --- a/sohstationviewer/view/db_config/db_config_dialog.py +++ b/sohstationviewer/view/db_config/db_config_dialog.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import Set, Dict -from PySide6 import QtWidgets, QtGui, QtCore +from PySide2 import QtWidgets, QtGui, QtCore from sohstationviewer.database.process_db import execute_db from sohstationviewer.view.util.one_instance_at_a_time import \ diff --git a/sohstationviewer/view/db_config/param_dialog.py b/sohstationviewer/view/db_config/param_dialog.py index 2a5b6f1312c7aec2efa3f04b3d7d5514bccc2638..21ecf7bcca7316e30a6b5e7253d7f1ce19ef400b 100755 --- a/sohstationviewer/view/db_config/param_dialog.py +++ b/sohstationviewer/view/db_config/param_dialog.py @@ -5,9 +5,9 @@ NOTE: Cannot remove or change params that are already used for channels. """ from typing import List -from PySide6 import QtWidgets, QtCore -from PySide6.QtCore import Qt -from PySide6.QtWidgets import QComboBox, QWidget +from PySide2 import QtWidgets, QtCore +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QComboBox, QWidget from sohstationviewer.conf.constants import ColorMode, ALL_COLOR_MODES from sohstationviewer.view.util.plot_func_names import plot_functions diff --git a/sohstationviewer/view/file_information/file_info_widget.py b/sohstationviewer/view/file_information/file_info_widget.py index e6d155e0ec9f046b4e65eae551105edb1b07e33f..ea4b7d44b47ddd3d177538ad2148e3ba25eaf847 100644 --- a/sohstationviewer/view/file_information/file_info_widget.py +++ b/sohstationviewer/view/file_information/file_info_widget.py @@ -1,9 +1,9 @@ -from PySide6 import QtCore -from PySide6.QtGui import ( +from PySide2 import QtCore +from PySide2.QtGui import ( QContextMenuEvent, QGuiApplication, QKeySequence, - QKeyEvent, QAction + QKeyEvent ) -from PySide6.QtWidgets import QListWidget, QMenu +from PySide2.QtWidgets import QListWidget, QMenu, QAction class FileInfoWidget(QListWidget): diff --git a/sohstationviewer/view/file_list_widget.py b/sohstationviewer/view/file_list_widget.py index 37389846888f619fabc8d5822f705d8134e79a6f..8af3da9fe8c01d1d385ccd8ab12e26a9a52b60f8 100644 --- a/sohstationviewer/view/file_list_widget.py +++ b/sohstationviewer/view/file_list_widget.py @@ -1,4 +1,4 @@ -from PySide6 import QtWidgets +from PySide2 import QtWidgets class FileListItem(QtWidgets.QListWidgetItem): diff --git a/sohstationviewer/view/help_view.py b/sohstationviewer/view/help_view.py index a8618b0504b339e68c4151295843376ec4b61bd5..f15cf47874e493377f6a50a402f3363a71a3534b 100644 --- a/sohstationviewer/view/help_view.py +++ b/sohstationviewer/view/help_view.py @@ -2,8 +2,8 @@ import sys from pathlib import Path from typing import Tuple, Callable, Union, List, Dict -from PySide6 import QtCore, QtGui, QtWidgets -from PySide6.QtWidgets import QStyle +from PySide2 import QtCore, QtGui, QtWidgets +from PySide2.QtWidgets import QStyle from sohstationviewer.view.util.functions import ( create_search_results_file, create_table_of_content_file) diff --git a/sohstationviewer/view/main_window.py b/sohstationviewer/view/main_window.py index 00ea1d15234dbc84efae299f9fa0b9767d7b27e1..59b711b12e0d2a228528538668311011e7d48284 100755 --- a/sohstationviewer/view/main_window.py +++ b/sohstationviewer/view/main_window.py @@ -5,10 +5,10 @@ from datetime import datetime, date from typing import List, Tuple, Union, Dict from pathlib import Path -from PySide6 import QtCore, QtWidgets, QtGui -from PySide6.QtCore import QSize -from PySide6.QtGui import QFont, QPalette, QColor -from PySide6.QtWidgets import QFrame, QListWidgetItem, QMessageBox +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QSize +from PySide2.QtGui import QFont, QPalette, QColor +from PySide2.QtWidgets import QFrame, QListWidgetItem, QMessageBox from sohstationviewer.conf import constants from sohstationviewer.model.data_loader import DataLoader @@ -1135,7 +1135,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): print(f"DEVELOPING ERROR: Type of form must be 'QWidget' instead " f"of '{form.__class__.__name__}'") if form not in self.forms_in_forms_menu: - action = QtGui.QAction(form_name, self) + action = QtWidgets.QAction(form_name, self) self.forms_menu.addAction(action) action.triggered.connect(lambda: self.raise_form(form)) self.forms_in_forms_menu.append(form) diff --git a/sohstationviewer/view/plotting/gps_plot/gps_dialog.py b/sohstationviewer/view/plotting/gps_plot/gps_dialog.py index ed3acd86ff9395dcb4a26779a636f39e78e7b1f1..ebdc9d4f1500493ea850ff21ae6a40b69196a0ac 100644 --- a/sohstationviewer/view/plotting/gps_plot/gps_dialog.py +++ b/sohstationviewer/view/plotting/gps_plot/gps_dialog.py @@ -5,7 +5,7 @@ import traceback from pathlib import Path from typing import List, Optional, Literal, Iterable -from PySide6 import QtWidgets, QtCore +from PySide2 import QtWidgets, QtCore from matplotlib.axes import Axes from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas from matplotlib.figure import Figure diff --git a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py index cbfbc4a8ddaa22e291d51454e68dd9069bb859fd..5212a06ae50b7da367fbda14c3ab5cc5de0cab30 100644 --- a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py @@ -2,7 +2,7 @@ from typing import Tuple, Union, Dict, List -from PySide6 import QtCore +from PySide2 import QtCore from sohstationviewer.model.general_data.general_data import GeneralData diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting.py b/sohstationviewer/view/plotting/plotting_widget/plotting.py index 09d07bf016cd751a6adfcc4c36bd73961b62ccb0..129772d5a83970a428e2ae0e3cc0483a8bcc962b 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting.py @@ -4,7 +4,11 @@ import numpy as np from matplotlib.axes import Axes from sohstationviewer.controller.util import get_val -from sohstationviewer.controller.plotting_data import get_masspos_value_colors +from sohstationviewer.view.plotting.plotting_widget.plotting_helper import ( + get_masspos_value_colors, + get_categorized_data_from_value_color_equal_on_lower_bound, + get_categorized_data_from_value_color_equal_on_upper_bound +) from sohstationviewer.view.util.color import clr from sohstationviewer.view.plotting.plotting_widget.plotting_helper import ( @@ -45,64 +49,40 @@ class Plotting: ax.chan_db_info = None return ax - def plot_multi_color_dots( - self, c_data: Dict, chan_db_info: Dict, chan_id: str) -> Axes: + def plot_multi_color_dots_base( + self, c_data: Dict, chan_db_info: Dict, equal_upper: bool = True): """ - plot scattered dots with colors defined by valueColors in database: - Ex: *:W means everything with white color - Ex: -1:_|0:R|2.3:Y|+2.3:G means - value <= -1 => not plot - value <= 0 => plot with R color - value <= 2.3 => plot with Y color - value > 2.3 => plot with G color + plot dots in center with colors defined by valueColors in database: Color codes are defined in colorSettings and limitted in 'valColRE' in dbSettings.py :param c_data: data of the channel which includes down-sampled (if needed) data in keys 'times' and 'data'. :param chan_db_info: info of channel from DB - :param chan_id: name of channel - :return ax: axes of the channel + :param equal_upper: + if True, plot_from_value_color_equal_on_upper_bound will be used + otherwise, plot_from_value_color_equal_on_lower_bound will be use + :return: ax in which the channel is plotted """ plot_h = self.plotting_axes.get_height(chan_db_info['height']) ax = self.plotting_axes.create_axes( self.parent.plotting_bot, plot_h, has_min_max_lines=False) - - x = [] - prev_val = -constants.HIGHEST_INT - - if chan_db_info['valueColors'] in [None, 'None', '']: - chan_db_info['valueColors'] = '*:W' - value_colors = chan_db_info['valueColors'].split('|') - colors = [] - for vc in value_colors: - v, c = vc.split(':') - colors.append(c) - val = get_val(v) - if c == '_': - prev_val = val - continue - points = [] - for times, data in zip(c_data['times'], c_data['data']): - if v.startswith('+'): - points = [times[i] - for i in range(len(data)) - if data[i] > val] - elif v == '*': - points = times - else: - points = [times[i] - for i in range(len(data)) - if prev_val < data[i] <= val] - x += points - + if equal_upper: + points_list, colors = \ + get_categorized_data_from_value_color_equal_on_upper_bound( + c_data, chan_db_info) + else: + points_list, colors = \ + get_categorized_data_from_value_color_equal_on_lower_bound( + c_data, chan_db_info) + # flatten point_list to be x + x = [item for row in points_list for item in row] + for points, c in zip(points_list, colors): ax.plot(points, len(points) * [0], linestyle="", marker='s', markersize=2, zorder=constants.Z_ORDER['DOT'], color=clr[c], picker=True, pickradius=3) - prev_val = val - total_samples = len(x) if len(colors) != 1: @@ -183,8 +163,25 @@ class Plotting: ax.chan_db_info = chan_db_info return ax - def plot_up_down_dots( - self, c_data: Dict, chan_db_info: Dict, chan_id: str) -> Axes: + def plot_multi_color_dots_equal_on_upper_bound( + self, c_data, chan_db_info, chan_id): + """ + Use plot_multi_color_dots_base() to plot channel in which colors are + identified by plot_from_value_color_equal_on_upper_bound + """ + return self.plot_multi_color_dots_base( + c_data, chan_db_info, equal_upper=True) + + def plot_multi_color_dots_equal_on_lower_bound( + self, c_data, chan_db_info, chan_id): + """ + Use plot_multi_color_dots_base() to plot channel in which colors are + identified by plot_from_value_color_equal_on_lower_bound + """ + return self.plot_multi_color_dots_base( + c_data, chan_db_info, equal_upper=False) + + def plot_up_down_dots(self, c_data, chan_db_info, chan_id): """ Plot channel with 2 different values, one above, one under center line. Each value has corresponding color defined in valueColors in database. @@ -408,8 +405,7 @@ class Plotting: return self.plot_lines_dots(c_data, chan_db_info, chan_id, info=info) def plot_lines_mass_pos( - self, c_data: Dict, chan_db_info: Dict, chan_id: str - ) -> Optional[Axes]: + self, c_data: Dict, chan_db_info: Dict, chan_id: str) -> Axes: """ Plot multi-color dots with grey line for mass position channel. Use get_masspos_value_colors() to get value_colors map based on @@ -428,13 +424,12 @@ class Plotting: if value_colors is None: return - plot_h = self.plotting_axes.get_height(chan_db_info['height']) ax = self.plotting_axes.create_axes( self.parent.plotting_bot, plot_h) - x_list, y_list = c_data['times'], c_data['data'] total_x = sum([len(x) for x in x_list]) + self.plotting_axes.set_axes_info( ax, sample_no_list=[None, total_x, None], sample_no_colors=[None, clr['W'], None], diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_helper.py b/sohstationviewer/view/plotting/plotting_widget/plotting_helper.py index ba30b9ad2229729dc4188792cb16b76c97656fc7..23ae00dafe2dccfa1536529c123bcd2032ccb33d 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_helper.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_helper.py @@ -1,7 +1,164 @@ -from typing import List, Tuple +from typing import List, Union, Tuple, Dict +from sohstationviewer.view.util.enums import LogType +from sohstationviewer.conf import constants +from sohstationviewer.controller.util import get_val from sohstationviewer.view.util.color import clr +# TODO: put this in DB +mass_pos_volt_ranges = {"regular": [0.5, 2.0, 4.0, 7.0], + "trillium": [0.5, 1.8, 2.4, 3.5]} +mass_pos_color_pallets = {"B": ["C", "G", "Y", "R", "M"], + "W": ["B", "B", "B", "B", "B"]} + + +def get_masspos_value_colors( + range_opt: str, chan_id: str, c_mode: str, + processing_log: List[Tuple[str, LogType]], + ret_type: str = 'str' +) -> Union[str, List[Tuple[float, str]], None]: + """ + Create a map between value and color based on given rangeOpt and c_mode to + display mass position plots. + :param range_opt: massPosVoltRangeOpt got from Options Menu - MP coloring + in Main Window to define different values for mass position. + (regular/trillium) + :param chan_id: ID of the channel + :param c_mode: color mode (B/W) + :param processing_log: list of processing info and type + :param ret_type: request return type + :return: [(value, color), (value, color) ...] + if retType is 'str', return "value:color|value:color" + """ + + if range_opt.lower() not in mass_pos_volt_ranges.keys(): + processing_log.append( + ( + f"{chan_id}: The current selected Mass Position color range is" + f" '{range_opt}' isn't allowable. The accept ranges are: " + f"{', '.join(mass_pos_volt_ranges.keys())}", + LogType.ERROR + ) + ) + return + mass_pos_volt_range = mass_pos_volt_ranges[range_opt] + mass_pos_color_pallet = mass_pos_color_pallets[c_mode] + value_colors = [] + for i in range(len(mass_pos_volt_range)): + if ret_type == 'str': + value_colors.append( + "%s:%s" % (mass_pos_volt_range[i], mass_pos_color_pallet[i])) + else: + value_colors.append( + (mass_pos_volt_range[i], mass_pos_color_pallet[i]) + ) + if i == len(mass_pos_volt_range) - 1: + if ret_type == 'str': + value_colors.append( + "%s:+%s" % ( + mass_pos_volt_range[i], mass_pos_color_pallet[i + 1])) + else: + value_colors.append( + (mass_pos_volt_range[i], mass_pos_color_pallet[i + 1])) + if ret_type == 'str': + return '|'.join(value_colors) + return value_colors + + +def get_categorized_data_from_value_color_equal_on_upper_bound( + c_data: Dict, chan_db_info: Dict +) -> Tuple[List[List[float]], List[str]]: + """ + Separate data points and color using valueColors in which the condition + will check if value equal to or less than upper bound and greater than + lower bound as following example + 0:R|2.3:Y|+2.3:G means + + value <= 0 => plot with R color + + 0 < value <= 2.3 => plot with Y color + + 2.3 < value => plot with G color + Beside additional signs are included: + Ex: *:W means everything with white color + Ex: -1:_ means not plotting value <= -1 + :param c_data: dict of data of the channel which includes down-sampled + data in keys 'times' and 'data'. + :param chan_db_info: dict of info of channel from DB + :return x: list of x to be plotted to get total samples + :return colors: list of color to be plotted to decide color of total sample + text. + """ + prev_val = -constants.HIGHEST_INT + value_colors = chan_db_info['valueColors'].split('|') + colors = [] + points_list = [] + for vc in value_colors: + v, c = vc.split(':') + if v == '*': + val = v # to have some value for pre_val = val + else: + val = get_val(v) + if c == '_': + prev_val = val + continue + colors.append(c) + # c_data['times'] is a list of one list (after changing to faster data + # reading) so don't need to loop through but only taking the first + # element (c_data['times'][0]). + # Same for c_data['data'] + times, data = c_data['times'][0], c_data['data'][0] + if v.startswith('+'): + points = [times[i] + for i in range(len(data)) + if data[i] > val] + elif v == '*': + points = times + else: + points = [times[i] + for i in range(len(data)) + if prev_val < data[i] <= val] + points_list.append(points) + prev_val = val + return points_list, colors + + +def get_categorized_data_from_value_color_equal_on_lower_bound( + c_data: Dict, chan_db_info: Dict +) -> Tuple[List[List[float]], List[str]]: + """ + Separate data points and color using valueColors in which the condition + will check if value equal to or greater than lower bound and less than + upper bound as the following example: + 3.:R|3.3:Y|=3.3:G means + + value < 3. => plot with R color + + 3<= value < 3.3 => plot with Y color + + value = 3.3 => plot with G color + :param c_data: dict of data of the channel which includes down-sampled + data in keys 'times' and 'data'. + :param chan_db_info: dict of info of channel from DB + :return x: list of x to be plotted to get total samples + :return colors: list of color to be plotted to decide color of total sample + text. + """ + prev_val = 0 + value_colors = chan_db_info['valueColors'].split('|') + colors = [] + points_list = [] + for vc in value_colors: + v, c = vc.split(':') + colors.append(c) + val = get_val(v) + times, data = c_data['times'][0], c_data['data'][0] + if v.startswith('='): + points = [times[i] + for i in range(len(data)) + if data[i] >= val] + else: + points = [times[i] + for i in range(len(data)) + if prev_val <= data[i] < val] + points_list.append(points) + prev_val = val + return points_list, colors + def get_colors_sizes_for_abs_y_from_value_colors( y: List[float], diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py b/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py index 5ca9a6f4be004b137640d0f78f6e574abbab37e2..764369320011cf6b6df691b599b165a937a54d4f 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py @@ -1,4 +1,4 @@ -from PySide6 import QtCore +from PySide2 import QtCore from sohstationviewer.conf import constants as const from sohstationviewer.view.plotting.plotting_widget.plotting_processor_helper\ diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py index 8fce50540eb3572652113addaa7ed0de1dac718f..14c90706340bcec9933766b06ddcd3d3b203c907 100755 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py @@ -6,9 +6,9 @@ import numpy as np import matplotlib.text from matplotlib import pyplot as pl from matplotlib.transforms import Bbox -from PySide6.QtCore import QTimer, Qt -from PySide6 import QtCore, QtWidgets -from PySide6.QtWidgets import QWidget, QApplication, QTextBrowser +from PySide2.QtCore import QTimer, Qt +from PySide2 import QtCore, QtWidgets +from PySide2.QtWidgets import QWidget, QApplication, QTextBrowser from sohstationviewer.conf import constants from sohstationviewer.view.util.color import set_color_mode @@ -599,7 +599,7 @@ class PlottingWidget(QtWidgets.QScrollArea): new_x_top_indexes.size) if hasattr(ax, 'x_center'): if not hasattr(ax, 'y_center'): - # plot_time_dots and plot_multi_color_dots + # plot_time_dots and plot_multi_color_dots(_xxx) x = ax.x_center new_x_indexes = np.where( (x >= self.min_x) & (x <= self.max_x))[0] diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_widget_helper.py b/sohstationviewer/view/plotting/plotting_widget/plotting_widget_helper.py index a116bddd61d5d708e9443a226c3a0c52ea94a34b..480e316a9651b67f2847fefd7edfd29586b5228e 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_widget_helper.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_widget_helper.py @@ -17,7 +17,8 @@ def get_index_from_data_picked( val = 1 if val == 0.5 else 0 if (chan_data['chan_db_info']['plotType'] - in ["multiColorDots", "dotForTime"]): + in ["multiColorDotsEqualOnUpperBound", + "multiColorDotsEqualOnLowerBound", "dotForTime"]): # don't check val because the plot's data don't affect the position # of the data point and overlap isn't an issue. real_indexes = np.where(chan_data['times'][0] == tm)[0] diff --git a/sohstationviewer/view/plotting/time_power_square/time_power_squared_dialog.py b/sohstationviewer/view/plotting/time_power_square/time_power_squared_dialog.py index 736f0534ee56cf73d24086b5135c21f76a32c809..3d2b0fff2c18c1456b10f03ebd38d5469cd76edb 100755 --- a/sohstationviewer/view/plotting/time_power_square/time_power_squared_dialog.py +++ b/sohstationviewer/view/plotting/time_power_square/time_power_squared_dialog.py @@ -3,7 +3,7 @@ from math import sqrt from typing import List, Tuple, Union import numpy as np -from PySide6 import QtWidgets, QtCore +from PySide2 import QtWidgets, QtCore from matplotlib.axes import Axes from sohstationviewer.conf import constants as const diff --git a/sohstationviewer/view/plotting/time_power_square/time_power_squared_processor.py b/sohstationviewer/view/plotting/time_power_square/time_power_squared_processor.py index bea8f00df6c401af7e6b5511bc0f694b0ec71137..2b862028bd3569128beca8d17ca3b0eecb6ffec9 100644 --- a/sohstationviewer/view/plotting/time_power_square/time_power_squared_processor.py +++ b/sohstationviewer/view/plotting/time_power_square/time_power_squared_processor.py @@ -1,7 +1,7 @@ from typing import Dict, Optional, List import numpy as np -from PySide6 import QtCore +from PySide2 import QtCore from sohstationviewer.view.plotting.time_power_square.\ time_power_squared_helper import get_tps_for_discontinuous_data diff --git a/sohstationviewer/view/plotting/waveform_dialog.py b/sohstationviewer/view/plotting/waveform_dialog.py index d22354a5cb39402290567945e3bd254ebd1da64d..8d59a462c50998d9f6e2a2a34ae4c25c9b49ba46 100755 --- a/sohstationviewer/view/plotting/waveform_dialog.py +++ b/sohstationviewer/view/plotting/waveform_dialog.py @@ -1,7 +1,7 @@ # Drawing waveform and mass position from typing import Tuple, Union, Dict -from PySide6 import QtCore, QtWidgets +from PySide2 import QtCore, QtWidgets from sohstationviewer.model.general_data.general_data import GeneralData diff --git a/sohstationviewer/view/save_plot_dialog.py b/sohstationviewer/view/save_plot_dialog.py index d5e7a5fc1732d2d00de0bdf445fe41ef2bffaeca..53b1c741259bde9a1b1087ae355800e84f11e67b 100644 --- a/sohstationviewer/view/save_plot_dialog.py +++ b/sohstationviewer/view/save_plot_dialog.py @@ -4,8 +4,8 @@ import os from pathlib import Path from typing import Union, Optional -from PySide6 import QtWidgets, QtCore, QtGui -from PySide6.QtWidgets import QApplication, QWidget, QDialog +from PySide2 import QtWidgets, QtCore, QtGui +from PySide2.QtWidgets import QApplication, QWidget, QDialog from sohstationviewer.conf import constants diff --git a/sohstationviewer/view/search_message/highlight_delegate.py b/sohstationviewer/view/search_message/highlight_delegate.py index 50f3f86e94cdbe5b762807414ceef50567f9fd32..cda425ca1c27191f8c0376f048fc36c06ce8d2ef 100644 --- a/sohstationviewer/view/search_message/highlight_delegate.py +++ b/sohstationviewer/view/search_message/highlight_delegate.py @@ -2,8 +2,8 @@ Credit: https://stackoverflow.com/questions/53353450/how-to-highlight-a-words-in-qtablewidget-from-a-searchlist # noqa """ from typing import List, Dict -from PySide6 import QtCore, QtGui, QtWidgets -from PySide6.QtGui import QPen, QTextCursor, QTextCharFormat, QColor +from PySide2 import QtCore, QtGui, QtWidgets +from PySide2.QtGui import QPen, QTextCursor, QTextCharFormat, QColor class HighlightDelegate(QtWidgets.QStyledItemDelegate): diff --git a/sohstationviewer/view/search_message/search_message_dialog.py b/sohstationviewer/view/search_message/search_message_dialog.py index dc4ffd15acb5bd6b81db2535c0bacabf658f7ad0..c67d4876019b55a70f870a9be5f8297906d1f398 100644 --- a/sohstationviewer/view/search_message/search_message_dialog.py +++ b/sohstationviewer/view/search_message/search_message_dialog.py @@ -3,9 +3,9 @@ import os from pathlib import PosixPath, Path from typing import Dict, List, Tuple, Callable, Union, Optional -from PySide6 import QtGui, QtCore, QtWidgets -from PySide6.QtWidgets import QStyle, QAbstractItemView -from PySide6.QtGui import QPalette +from PySide2 import QtGui, QtCore, QtWidgets +from PySide2.QtWidgets import QStyle, QAbstractItemView +from PySide2.QtGui import QPalette from sohstationviewer.view.search_message.highlight_delegate import ( HighlightDelegate) diff --git a/sohstationviewer/view/ui/about_ui_qtdesigner.py b/sohstationviewer/view/ui/about_ui_qtdesigner.py index 8c402dd5ad050f0084e538e6ce1b1d36d2782d72..e1ecbbe2258d2ee9909d583d3ec53d7d548f4af6 100644 --- a/sohstationviewer/view/ui/about_ui_qtdesigner.py +++ b/sohstationviewer/view/ui/about_ui_qtdesigner.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide6 import QtCore, QtGui, QtWidgets +from PySide2 import QtCore, QtGui, QtWidgets class Ui_About(object): def setupUi(self, About): diff --git a/sohstationviewer/view/ui/calendar_ui_qtdesigner.py b/sohstationviewer/view/ui/calendar_ui_qtdesigner.py index 4e225c0eafb962bb5748971aa948936558c68d5e..81edc2dd9a6fa013340ce159ef58cd58e61b5b52 100644 --- a/sohstationviewer/view/ui/calendar_ui_qtdesigner.py +++ b/sohstationviewer/view/ui/calendar_ui_qtdesigner.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide6 import QtCore, QtWidgets +from PySide2 import QtCore, QtWidgets from sohstationviewer.view.calendar.calendar_widget import CalendarWidget diff --git a/sohstationviewer/view/ui/main_ui.py b/sohstationviewer/view/ui/main_ui.py index ddd7e7fe2fcd388c50924a4d4a18d7c9f96eee60..3faa3ce6e03a48ca838834225f14b1f05b19ee7e 100755 --- a/sohstationviewer/view/ui/main_ui.py +++ b/sohstationviewer/view/ui/main_ui.py @@ -2,16 +2,15 @@ import configparser from typing import Union, List, Optional -from PySide6 import QtCore, QtGui, QtWidgets -from PySide6.QtWidgets import ( +from PySide2 import QtCore, QtGui, QtWidgets +from PySide2.QtWidgets import ( QMainWindow, QWidget, QTextBrowser, QPushButton, QLineEdit, QDateEdit, QListWidget, QCheckBox, QRadioButton, QMenu, QLabel, QFrame, QVBoxLayout, QHBoxLayout, QGridLayout, QAbstractItemView, QButtonGroup, ) -from PySide6.QtGui import ( +from PySide2.QtWidgets import ( QAction, QActionGroup, QShortcut ) - from sohstationviewer.view.calendar.calendar_widget import CalendarWidget from sohstationviewer.view.date_edit_with_doy import \ DayOfYearSupportedDateTextBox diff --git a/sohstationviewer/view/util/one_instance_at_a_time.py b/sohstationviewer/view/util/one_instance_at_a_time.py index dd6cd416df8a2d4136b166a6c55e301e128c9000..f0bbf7a34e8103287bd7f4bbe3dd0fa988491304 100644 --- a/sohstationviewer/view/util/one_instance_at_a_time.py +++ b/sohstationviewer/view/util/one_instance_at_a_time.py @@ -1,7 +1,7 @@ from __future__ import annotations -from PySide6.QtGui import QCloseEvent -from PySide6.QtWidgets import QWidget +from PySide2.QtGui import QCloseEvent +from PySide2.QtWidgets import QWidget class DialogAlreadyOpenedError(Exception): diff --git a/sohstationviewer/view/util/plot_func_names.py b/sohstationviewer/view/util/plot_func_names.py index ebb2a829bc0c4d8a2d336432e49494de26c2dddc..2fa9ba7b43bf71a1e36c133af768fab957ede50d 100644 --- a/sohstationviewer/view/util/plot_func_names.py +++ b/sohstationviewer/view/util/plot_func_names.py @@ -3,8 +3,8 @@ plot_functions = { "Lines, one color dots. Dot or line/color mapping defined by " "ValueColors with available colors: RYGMCW.\n" "Ex: L:G|D:W\n" - " means Lines are plotted with color G\n" - " Dots are plotted with color W\n" + " means \tLines are plotted with color G\n" + "\tDots are plotted with color W\n" "If D is not defined, dots won't be displayed.\n" "If L is not defined, lines will be plotted with color G", "plot_lines_dots"), @@ -18,23 +18,36 @@ plot_functions = { "Three lines with three different colors for values -1, 0, 1.", "plot_tri_colors"), 'dotForTime': ( - "Dots according to timestamp." + "Dots according to timestamp.\n" "Color defined by ValueColors with available colors: RYGMCW.\n" "Ex: G", "plot_time_dots"), - 'multiColorDots': ( - ("Multicolor dots with value/colors mapping defined by " + 'multiColorDotsEqualOnUpperBound': ( + ("Multicolor dots in center with value/colors mapping defined by " "ValueColors with available colors: RYGMCW.\n" "Value from low to high.\n" - "Ex: *:W\n" - " means all values represent by white dots." + "Ex: *:W in which all values represent by white dots.\n" "Ex: -1:_|0:R|2.3:Y|+2.3:G\n" - " in which '-1:_' means no plotting for value <-1"), - "plot_multi_color_dots" + " in which \tvalue <= -1 => not plot\n" + "\tvalue <= 0 => plot with R color\n" + "\tvalue <= 2.3 => plot with Y color\n" + "\tvalue > 2.3 => plot with G color\n"), + "plot_multi_color_dots_equal_on_lower_bound" + ), + 'multiColorDotsEqualOnLowerBound': ( + ("Multicolor dots in center with value/colors mapping defined by " + "ValueColors with available colors: RYGMCW.\n" + "Value from low to high.\n" + "Ex: 3.:R|3.3:Y|=3.3:G\n" + " in which" + "\tvalue < 3. => plot with R color\n" + "\t3 <= value < 3.3 => plot with Y color\n" + "\tvalue = 3.3 => plot with G color\n"), + "plot_multi_color_dots_equal_on_lower_bound" ), 'upDownDots': ( ("Show data with 2 different values: first down/ second up. " - "With colors defined by ValueColors.\nEx: 1:R|0:Y"), + "Colors defined by ValueColors.\nEx: 1:R|0:Y"), 'plot_up_down_dots' ) } diff --git a/tests/controller/test_plotting_data.py b/tests/controller/test_plotting_data.py index 731c91e3a8ff2d4470b5d017f6f7d0866615eb39..49ee4b13dd3b4f4aa7e9ac85e333d60a3321f9bd 100644 --- a/tests/controller/test_plotting_data.py +++ b/tests/controller/test_plotting_data.py @@ -5,7 +5,6 @@ from unittest import TestCase from obspy import UTCDateTime from sohstationviewer.controller.plotting_data import ( - get_masspos_value_colors, format_time, get_title, get_gaps, @@ -68,98 +67,6 @@ class TestGetDayTicks(TestCase): self.assertTupleEqual(get_day_ticks(), expected) -class TestGetMassposValue(TestCase): - """Test suite for getMasspossValue""" - - def test_string_output(self): - """ - Test basic functionality of get_masspos_value_colors - the range option - and color mode are correct, and the output is a string. - """ - expected_input_output_pairs = { - ('regular', 'B'): '0.5:C|2.0:G|4.0:Y|7.0:R|7.0:+M', - ('regular', 'W'): '0.5:B|2.0:B|4.0:B|7.0:B|7.0:+B', - ('trillium', 'B'): '0.5:C|1.8:G|2.4:Y|3.5:R|3.5:+M', - ('trillium', 'W'): '0.5:B|1.8:B|2.4:B|3.5:B|3.5:+B', - } - test_names = ( - 'test_regular_B', - 'test_regular_W', - 'test_trillium_B', - 'test_trillium_W', - ) - idx = 0 - for input_val in expected_input_output_pairs: - with self.subTest(test_names[idx]): - self.assertEqual( - get_masspos_value_colors(input_val[0], '', - input_val[1], []), - expected_input_output_pairs[input_val] - ) - idx += 1 - - def test_list_output(self): - """ - Test basic functionality of get_masspos_value_colors - the range option - and color mode are correct, and the output is a list. - """ - expected_input_output_pairs = { - ('regular', 'B'): - [(0.5, 'C'), (2.0, 'G'), (4.0, 'Y'), (7.0, 'R'), (7.0, 'M')], - ('regular', 'W'): - [(0.5, 'B'), (2.0, 'B'), (4.0, 'B'), (7.0, 'B'), (7.0, 'B')], - ('trillium', 'B'): - [(0.5, 'C'), (1.8, 'G'), (2.4, 'Y'), (3.5, 'R'), (3.5, 'M')], - ('trillium', 'W'): - [(0.5, 'B'), (1.8, 'B'), (2.4, 'B'), (3.5, 'B'), (3.5, 'B')], - } - test_names = ( - 'test_regular_B', - 'test_regular_W', - 'test_trillium_B', - 'test_trillium_W', - ) - for i, input_val in enumerate(expected_input_output_pairs): - with self.subTest(test_names[i]): - self.assertListEqual( - get_masspos_value_colors( - input_val[0], '', input_val[1], [], ret_type=''), - expected_input_output_pairs[input_val] - ) - - def test_range_option_not_supported(self): - """ - Test basic functionality of get_masspos_value_colors - the range option - is not supported. - """ - errors = [] - empty_color_option = '' - self.assertIsNone( - get_masspos_value_colors(empty_color_option, '', 'B', errors)) - self.assertGreater(len(errors), 0) - - errors = [] - bad_color_option = 'unsupported' - self.assertIsNone( - get_masspos_value_colors(bad_color_option, '', 'B', errors)) - self.assertGreater(len(errors), 0) - - def test_color_mode_not_supported(self): - """ - Test basic functionality of get_masspos_value_colors - color mode is - not supported. - """ - errors = [] - empty_color_mode = '' - with self.assertRaises(KeyError): - get_masspos_value_colors('regular', '', empty_color_mode, errors) - - errors = [] - bad_color_mode = 'unsupported' - with self.assertRaises(KeyError): - get_masspos_value_colors('regular', '', bad_color_mode, errors) - - class TestGetTimeTicks(TestCase): """Test suite for get_time_ticks.""" diff --git a/tests/controller/test_processing.py b/tests/controller/test_processing.py index f7de7141398081e01524fa8df138bfaba7f4fee1..743eba9d12ce5661787a71e317e2366c0d2ae3cb 100644 --- a/tests/controller/test_processing.py +++ b/tests/controller/test_processing.py @@ -13,7 +13,7 @@ from sohstationviewer.controller.processing import ( get_data_type_from_file ) from sohstationviewer.database.extract_data import get_signature_channels -from PySide6 import QtWidgets +from PySide2 import QtWidgets TEST_DATA_DIR = Path(__file__).resolve().parent.parent.joinpath('test_data') diff --git a/tests/view/plotting/plotting_widget/test_plotting_helper.py b/tests/view/plotting/plotting_widget/test_plotting_helper.py index 66570fd7c44172f02bfca4925ce3b0277e1dedaf..1e898ca260cc9b964a91fbdb4b13f8f9dad03602 100644 --- a/tests/view/plotting/plotting_widget/test_plotting_helper.py +++ b/tests/view/plotting/plotting_widget/test_plotting_helper.py @@ -1,11 +1,151 @@ from unittest import TestCase from sohstationviewer.view.plotting.plotting_widget.plotting_helper import ( + get_masspos_value_colors, + get_categorized_data_from_value_color_equal_on_upper_bound, + get_categorized_data_from_value_color_equal_on_lower_bound, get_colors_sizes_for_abs_y_from_value_colors ) from sohstationviewer.view.util.color import clr +class TestGetMassposValue(TestCase): + """Test suite for getMasspossValue""" + + def test_string_output(self): + """ + Test basic functionality of get_masspos_value_colors - the range option + and color mode are correct, and the output is a string. + """ + expected_input_output_pairs = { + ('regular', 'B'): '0.5:C|2.0:G|4.0:Y|7.0:R|7.0:+M', + ('regular', 'W'): '0.5:B|2.0:B|4.0:B|7.0:B|7.0:+B', + ('trillium', 'B'): '0.5:C|1.8:G|2.4:Y|3.5:R|3.5:+M', + ('trillium', 'W'): '0.5:B|1.8:B|2.4:B|3.5:B|3.5:+B', + } + test_names = ( + 'test_regular_B', + 'test_regular_W', + 'test_trillium_B', + 'test_trillium_W', + ) + idx = 0 + for input_val in expected_input_output_pairs: + with self.subTest(test_names[idx]): + self.assertEqual( + get_masspos_value_colors(input_val[0], '', + input_val[1], []), + expected_input_output_pairs[input_val] + ) + idx += 1 + + def test_list_output(self): + """ + Test basic functionality of get_masspos_value_colors - the range option + and color mode are correct, and the output is a list. + """ + expected_input_output_pairs = { + ('regular', 'B'): + [(0.5, 'C'), (2.0, 'G'), (4.0, 'Y'), (7.0, 'R'), (7.0, 'M')], + ('regular', 'W'): + [(0.5, 'B'), (2.0, 'B'), (4.0, 'B'), (7.0, 'B'), (7.0, 'B')], + ('trillium', 'B'): + [(0.5, 'C'), (1.8, 'G'), (2.4, 'Y'), (3.5, 'R'), (3.5, 'M')], + ('trillium', 'W'): + [(0.5, 'B'), (1.8, 'B'), (2.4, 'B'), (3.5, 'B'), (3.5, 'B')], + } + test_names = ( + 'test_regular_B', + 'test_regular_W', + 'test_trillium_B', + 'test_trillium_W', + ) + for i, input_val in enumerate(expected_input_output_pairs): + with self.subTest(test_names[i]): + self.assertListEqual( + get_masspos_value_colors( + input_val[0], '', input_val[1], [], ret_type=''), + expected_input_output_pairs[input_val] + ) + + def test_range_option_not_supported(self): + """ + Test basic functionality of get_masspos_value_colors - the range option + is not supported. + """ + errors = [] + empty_color_option = '' + self.assertIsNone( + get_masspos_value_colors(empty_color_option, '', 'B', errors)) + self.assertGreater(len(errors), 0) + + errors = [] + bad_color_option = 'unsupported' + self.assertIsNone( + get_masspos_value_colors(bad_color_option, '', 'B', errors)) + self.assertGreater(len(errors), 0) + + def test_color_mode_not_supported(self): + """ + Test basic functionality of get_masspos_value_colors - color mode is + not supported. + """ + errors = [] + empty_color_mode = '' + with self.assertRaises(KeyError): + get_masspos_value_colors('regular', '', empty_color_mode, errors) + + errors = [] + bad_color_mode = 'unsupported' + with self.assertRaises(KeyError): + get_masspos_value_colors('regular', '', bad_color_mode, errors) + + +class TestGetCategorizedDataFromValueColorEqualOnUpperBound(TestCase): + def setUp(self) -> None: + self.c_data = { + 'times': [[0, 1, 2, 3, 4]], + 'data': [[1, 3, 3.2, 3.3, 3.4]]} + + def test_equal_on_upper_bound(self): + chan_db_info = {'valueColors': '3:R|3.3:Y|+3.3:G'} + points_list, colors = \ + get_categorized_data_from_value_color_equal_on_upper_bound( + self.c_data, chan_db_info) + self.assertEqual(points_list, [[0, 1], [2, 3], [4]]) + self.assertEqual(colors, ['R', 'Y', 'G']) + + def test_underscore(self): + chan_db_info = {'valueColors': '3:_|3.3:Y|+3.3:G'} + points_list, colors = \ + get_categorized_data_from_value_color_equal_on_upper_bound( + self.c_data, chan_db_info) + self.assertEqual(points_list, [[2, 3], [4]]) + self.assertEqual(colors, ['Y', 'G']) + + def test_star(self): + chan_db_info = {'valueColors': '*:G'} + points_list, colors = \ + get_categorized_data_from_value_color_equal_on_upper_bound( + self.c_data, chan_db_info) + + self.assertEqual(points_list, self.c_data['times']) + self.assertEqual(colors, ['G']) + + +class TestGetCategorizedDataFromValueColorEqualOnLowerBound(TestCase): + def test_equal_on_lower_bound(self): + c_data = {'times': [[0, 1, 2, 3, 4]], + 'data': [[1, 3, 3.2, 3.3, 3.4]]} + chan_db_info = {'valueColors': '3:R|3.3:Y|=3.3:G'} + points_list, colors = \ + get_categorized_data_from_value_color_equal_on_lower_bound( + c_data, chan_db_info) + + self.assertEqual(points_list, [[0], [1, 2], [3, 4]]) + self.assertEqual(colors, ['R', 'Y', 'G']) + + class TestGetColorsSizesForAbsYFromValueColors(TestCase): def test_get_colors_sizes_for_abs_y_from_value_colors(self): y = [0, 0.5, 1., 2., 3., 4., 5., 7., 7.1,