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,