diff --git a/sohstationviewer/conf/dbSettings.py b/sohstationviewer/conf/dbSettings.py
index 2a913a89d55879d55d25e256f70905e66809b80d..88989c8994f03767628be3ea36c132e2f47a9e6a 100755
--- a/sohstationviewer/conf/dbSettings.py
+++ b/sohstationviewer/conf/dbSettings.py
@@ -19,30 +19,30 @@ dbConf = {
     'plotFunc': {
         'linesDots': (
             ("Lines, one color dots. "),
-            "plotLinesDots"),
+            "plot_lines_dots"),
         'linesSRate': (
             ("Lines, one color dots, bitweight info. "),
-            "plotLinesSRate"),
+            "plot_lines_s_rate"),
         'linesMasspos': (
             ("multi-line mass position, multi-color dots. "),
-            "plotLinesMasspos"),
+            "plot_lines_mass_pos"),
         # 'dotsMasspos': (
         #     ("mass position, multi-color, single line. "),
         #     "plotDotsMasspos"),
         'dotForTime': (
             "Dots according to timestamp. Color defined by valueColors. Ex: G",
-            "plotTimeDots"),
+            "plot_time_dots"),
         'multiColorDots': (
             ("Multicolor dots with colors defined by valueColors. "
              "Value from low to high. "
              "Ex:*:W or -1:_|0:R|2.3:Y|+2.3:G. "
              "With colors: RYGMC: _: not plot"),
-            "plotMultiColorDots"
+            "plot_multi_color_dots"
         ),
         'upDownDots': (
             ("Show data with 2 different values: first down/ second up. "
              "With colors defined by valueColors. Ex: 1:R|0:Y"),
-            'plotUpDownDots'
+            'plot_up_down_dots'
         )
     }
 }
diff --git a/sohstationviewer/view/calendar_dialog.py b/sohstationviewer/view/calendar_dialog.py
new file mode 100644
index 0000000000000000000000000000000000000000..ceb2e9ea16242dcc118b2255bb1c9203b442075a
--- /dev/null
+++ b/sohstationviewer/view/calendar_dialog.py
@@ -0,0 +1,10 @@
+from PySide2 import QtWidgets
+
+from sohstationviewer.view.ui.calendar_ui_qtdesigner import Ui_CalendarDialog
+
+
+class CalendarDialog(QtWidgets.QDialog, Ui_CalendarDialog):
+
+    def __init__(self, parent=None):
+        super().__init__(parent)
+        self.setupUi(self)
diff --git a/sohstationviewer/view/calendardialog.py b/sohstationviewer/view/calendardialog.py
deleted file mode 100644
index afb18249a3ae8f3fe3ffad6941f1913f4c2d5246..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/calendardialog.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from PySide2 import QtWidgets
-
-from sohstationviewer.view.ui.calendar_ui_qtdesigner import Ui_CalendarDialog
-
-
-class CalendarDialog(QtWidgets.QDialog, Ui_CalendarDialog):
-
-    def __init__(self, parent=None):
-        super().__init__(parent)
-        self.setupUi(self)
-
-        # Connect the "selectionChanged" signal from the QCalendar
-        # This lets the user select a date using either the mouse, or
-        # arrow keys on the keyboard.
-        # self.calendarWidget.selectionChanged.connect(self.onDatePicked)
-        # date = self.calendarWidget.selectedDate()
-        # doy = date.dayOfYear()
-        # self.julianDateLineEdit.setText(str(doy))
diff --git a/sohstationviewer/view/channel_dialog.py b/sohstationviewer/view/channel_dialog.py
new file mode 100755
index 0000000000000000000000000000000000000000..0673821e58f93828988a41e06113288d93cb19d0
--- /dev/null
+++ b/sohstationviewer/view/channel_dialog.py
@@ -0,0 +1,88 @@
+"""
+channel_dialog.py
+GUI to add/edit/remove channels
+"""
+
+from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog
+from sohstationviewer.database.proccessDB import executeDB
+
+
+class ChannelDialog(Ui_DBInfoDialog):
+    def __init__(self, parent):
+        super().__init__(
+            parent, ['No.', 'Channel', 'Label', 'Param',
+                     'ConvertFactor', 'Unit', 'FixPoint'],
+            'channel', 'channels', resize_content_columns=[0, 4, 5, 6],
+            need_data_type_choice=True, requested_columns={3: 'Param'},
+            check_fk=False)
+        self.setWindowTitle("Edit/Add/Delete Channels")
+
+    def update_data_table_widget_items(self):
+        param_rows = executeDB("SELECT param from parameters")
+        self.param_choices = [''] + sorted([d[0] for d in param_rows])
+        super(ChannelDialog, self).update_data_table_widget_items()
+
+    def clear_first_row(self):
+        """
+        device with no channels yet, there will be empty channel left
+        """
+        self.data_table_widget.cellWidget(0, 1).setText('')
+        self.data_table_widget.cellWidget(0, 2).setText('')
+        self.data_table_widget.cellWidget(0, 3).setCurrentIndex(-1)
+        self.data_table_widget.cellWidget(0, 4).setText('1')
+        self.data_table_widget.cellWidget(0, 5).setText('')
+        self.data_table_widget.cellWidget(0, 6).setValue(0)
+
+    def add_row(self, row_idx, fk=False):
+        self.addWidget(None, row_idx, 0)  # No.
+        self.addWidget(self.data_list, row_idx, 1, foreign_key=fk)  # chanID
+        self.addWidget(self.data_list, row_idx, 2)  # label
+        self.addWidget(self.data_list, row_idx, 3, choices=self.param_choices)
+        self.addWidget(self.data_list, row_idx, 4,
+                       field_name='convertFactor')
+        self.addWidget(self.data_list, row_idx, 5)  # unit
+        self.addWidget(self.data_list, row_idx, 6,
+                       range=[0, 5])  # fixPoint
+
+    def data_type_changed(self):
+        self.data_type = self.data_type_combo_box.currentText()
+        self.update_data_table_widget_items()
+
+    def get_data_list(self):
+        channel_rows = executeDB(
+            f"SELECT channel, label, param, convertFactor, unit, fixPoint "
+            f"FROM Channels "
+            f"WHERE dataType='{self.data_type}'")
+        return [[d[0], d[1], d[2], d[3],
+                 '' if d[4] is None else d[4],
+                 d[5]]
+                for d in channel_rows]
+
+    def get_row_inputs(self, row_idx):
+        return [
+            self.data_table_widget.cellWidget(row_idx, 1).text().strip(),
+            self.data_table_widget.cellWidget(row_idx, 2).text().strip(),
+            self.data_table_widget.cellWidget(row_idx, 3).currentText(),
+            float(self.data_table_widget.cellWidget(row_idx, 4).text()),
+            self.data_table_widget.cellWidget(row_idx, 5).text(),
+            self.data_table_widget.cellWidget(row_idx, 6).value()
+        ]
+
+    def remove_row(self, remove_row_idx):
+        self.data_table_widget.remove_row(remove_row_idx)
+        for i in range(remove_row_idx, self.data_table_widget.rowCount()):
+            cell_widget = self.data_table_widget.cellWidget(i, 0)
+            cell_widget.setText(str(i))
+
+    def update_data(self, row, widget_idx, list_idx):
+        insertsql = (f"INSERT INTO Channels VALUES"
+                     f"('{row[0]}', '{row[1]}', '{row[2]}',"
+                     f" {row[3]}, '{row[4]}', {row[5]}, '{self.data_type}')")
+        updatesql = (f"UPDATE Channels SET channel='{row[0]}', "
+                     f"label='{row[1]}', param='{row[2]}', "
+                     f"convertFactor={row[3]}, unit='{row[4]}' "
+                     f"fixPoint={row[5]} "
+                     f"WHERE channel='%s'"
+                     f" AND dataType='{self.data_type}'")
+        return super().update_data(
+            row, widget_idx, list_idx, insertsql, updatesql)
diff --git a/sohstationviewer/view/channel_prefer_dialog.py b/sohstationviewer/view/channel_prefer_dialog.py
new file mode 100755
index 0000000000000000000000000000000000000000..fea4be9e0af59d3b19327e8e8379661429eb2800
--- /dev/null
+++ b/sohstationviewer/view/channel_prefer_dialog.py
@@ -0,0 +1,292 @@
+from PySide2 import QtWidgets, QtCore
+
+from sohstationviewer.database.proccessDB import (
+    executeDB, trunc_addDB, executeDB_dict)
+from sohstationviewer.controller.processing import readChannels, detectDataType
+from sohstationviewer.controller.util import displayTrackingInfo
+
+INSTRUCTION = """
+Place lists of channels to be read in the IDs field.\n
+Select the radiobutton for the list to be used in plotting.
+"""
+TOTAL_ROW = 20
+
+COL = {'sel': 0, 'name': 1, 'dataType': 2, 'IDs': 3, 'clr': 4}
+
+
+class ChannelPreferDialog(QtWidgets.QWidget):
+    def __init__(self, parent, dir_names):
+        super(ChannelPreferDialog, self).__init__()
+        self.parent = parent
+        self.dir_names = dir_names
+        self.setWindowTitle("Channel Preferences")
+        self.setGeometry(100, 100, 1100, 800)
+        main_layout = QtWidgets.QVBoxLayout()
+        main_layout.setContentsMargins(7, 7, 7, 7)
+        self.setLayout(main_layout)
+
+        main_layout.addWidget(QtWidgets.QLabel(INSTRUCTION))
+
+        self.create_id_table_widget()
+        main_layout.addWidget(self.id_table_widget, 1)
+
+        button_layout = self.create_buttons_section()
+        main_layout.addLayout(button_layout)
+
+        self.trackingInfoTextBrowser = QtWidgets.QTextBrowser(self)
+        self.trackingInfoTextBrowser.setFixedHeight(60)
+        main_layout.addWidget(self.trackingInfoTextBrowser)
+        self.changed = False
+
+    def create_buttons_section(self):
+        h_layout = QtWidgets.QHBoxLayout()
+        self.add_db_chan_btn = QtWidgets.QPushButton(
+            self, text='Add DB Channels')
+        self.add_db_chan_btn.clicked.connect(self.add_db_channels)
+        h_layout.addWidget(self.add_db_chan_btn)
+
+        self.scan_chan_btn = QtWidgets.QPushButton(
+            self, text='Scan Channels from Data Source')
+        self.scan_chan_btn.clicked.connect(self.scan_channels)
+        h_layout.addWidget(self.scan_chan_btn)
+
+        self.save_btn = QtWidgets.QPushButton(self, text='Save')
+        self.save_btn.clicked.connect(self.save)
+        h_layout.addWidget(self.save_btn)
+
+        self.save_add_main_btn = QtWidgets.QPushButton(
+            self, text='Save - Add to Main')
+        self.save_add_main_btn.clicked.connect(self.save_add_to_main_and_close)
+        h_layout.addWidget(self.save_add_main_btn)
+
+        self.close_btn = QtWidgets.QPushButton(self, text='Cancel')
+        self.close_btn.clicked.connect(self.close)
+        h_layout.addWidget(self.close_btn)
+        return h_layout
+
+    def create_id_table_widget(self):
+        self.id_table_widget = QtWidgets.QTableWidget(self)
+        # self.id_table_widget.verticalHeader().hide()
+        self.id_table_widget.setColumnCount(5)
+        col_headers = ['', 'Name', 'DataType', 'IDs', 'Clear']
+        self.id_table_widget.setHorizontalHeaderLabels(col_headers)
+        header = self.id_table_widget.horizontalHeader()
+        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
+        header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch)
+
+        self.id_table_widget.setRowCount(TOTAL_ROW)
+        self.avail_data_types = self.get_data_types()
+        for row_idx in range(TOTAL_ROW):
+            self.add_row(row_idx)
+
+        self.curr_row = -1
+        self.update_data_table_widget_items()
+
+    @QtCore.Slot()
+    def add_row(self, row_idx):
+        curr_sel_radio_btn = QtWidgets.QRadioButton(self)
+        curr_sel_radio_btn.clicked.connect(
+            lambda checked: self.curr_sel_changed(row_idx))
+        self.id_table_widget.setCellWidget(
+            row_idx, COL['sel'], curr_sel_radio_btn)
+
+        name_line_edit = QtWidgets.QLineEdit(self)
+        name_line_edit.textChanged.connect(self.input_changed)
+        self.id_table_widget.setCellWidget(
+            row_idx, COL['name'], name_line_edit)
+
+        data_type_combo_box = QtWidgets.QComboBox(self)
+        data_type_combo_box.currentIndexChanged.connect(self.input_changed)
+        data_type_combo_box.addItems(['Unknown'] + self.avail_data_types)
+        data_type_combo_box.setCurrentIndex(-1)
+        self.id_table_widget.setCellWidget(
+            row_idx, COL['dataType'], data_type_combo_box)
+
+        id_line_edit = QtWidgets.QLineEdit(self)
+        id_line_edit.textChanged.connect(self.input_changed)
+        self.id_table_widget.setCellWidget(
+            row_idx, COL['IDs'], id_line_edit)
+
+        del_button = QtWidgets.QPushButton(self, text='CLR')
+        del_button.clicked.connect(lambda arg: self.clear_id(row_idx))
+        self.id_table_widget.setCellWidget(
+            row_idx, COL['clr'], del_button)
+
+    @QtCore.Slot()
+    def input_changed(self):
+        self.changed = True
+
+    def update_data_table_widget_items(self):
+        id_rows = self.get_id_rows()
+        # first row
+        self.id_table_widget.cellWidget(0, COL['sel']).setChecked(True)
+        self.curr_sel_changed(0)
+        count = 0
+        for r in id_rows:
+            self.id_table_widget.cellWidget(
+                count, COL['sel']).setChecked(
+                True if ['current'] == 1 else False)
+            self.id_table_widget.cellWidget(
+                count, COL['name']).setText(r['name'])
+            self.id_table_widget.cellWidget(
+                count, COL['dataType']).setCurrentText(r['dataType'])
+            self.id_table_widget.cellWidget(
+                count, COL['IDs']).setText(r['IDs'])
+            self.id_table_widget.cellWidget(
+                count, COL['sel']).setChecked(
+                True if r['current'] == 1 else False)
+            if ['current'] == 1:
+                self.curr_sel_changed(count)
+            count += 1
+        self.update()
+
+    def get_row(self, row_idx):
+        name_widget = self.id_table_widget.cellWidget(row_idx, COL['name'])
+        data_type_widget = self.id_table_widget.cellWidget(row_idx,
+                                                           COL['dataType'])
+        id_widget = self.id_table_widget.cellWidget(row_idx, COL['IDs'])
+        clear_widget = self.id_table_widget.cellWidget(row_idx, COL['clr'])
+        return name_widget, data_type_widget, id_widget, clear_widget
+
+    @QtCore.Slot()
+    def curr_sel_changed(self, row_idx):
+        self.curr_row = row_idx
+        (self.name_widget, self.data_type_widget,
+         self.id_widget, self.clear_widget) = self.get_row(row_idx)
+        self.input_changed()
+
+    @QtCore.Slot()
+    def clear_id(self, row_idx):
+        (name_widget, data_type_widget, id_widget, clear_widget) =\
+            self.get_row(row_idx)
+        if id_widget.text().strip() != "":
+            msg = ("Are you sure you want to delete the channel IDs of  "
+                   "row #%s?" % (row_idx + 1))
+            result = QtWidgets.QMessageBox.question(
+                self, "Confirmation", msg,
+                QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
+            if result == QtWidgets.QMessageBox.No:
+                return
+            self.changed = True
+        if name_widget.text() != '':
+            self.changed = True
+        name_widget.setText('')
+        if data_type_widget.currentText != '':
+            self.changed = True
+        data_type_widget.setCurrentIndex(-1)
+        if id_widget.text() != '':
+            self.changed = True
+        id_widget.setText('')
+
+    def validate_row(self, check_data_type=False):
+        if self.curr_row == -1:
+            msg = "Please select a row."
+            QtWidgets.QMessageBox.information(self, "Select row", msg)
+            return False
+
+        if check_data_type:
+            self.data_type = self.data_type_widget.currentText()
+            if self.data_type not in self.avail_data_types:
+                msg = ("Please select a data type that isn't 'Unknown' for "
+                       "the selected row.")
+                QtWidgets.QMessageBox.information(
+                    self, "Select data type", msg)
+                return False
+        return True
+
+    @QtCore.Slot()
+    def add_db_channels(self):
+        if not self.validate_row(check_data_type=True):
+            return
+
+        db_channels = self.get_db_channels(self.data_type)
+        self.id_widget.setText(','.join(db_channels))
+
+    @QtCore.Slot()
+    def scan_channels(self):
+        if not self.validate_row():
+            return
+
+        data_type = detectDataType(self, self.dir_names)
+        if data_type in self.avail_data_types:
+            self.data_type_widget.setCurrentText(data_type)
+        else:
+            self.data_type_widget.setCurrenText('Unknown')
+        scanned_channels = readChannels(self, self.dir_names)
+        self.id_widget.setText(','.join(scanned_channels))
+
+    @QtCore.Slot()
+    def save(self):
+        if not self.validate_row():
+            return
+        if self.changed:
+            msg = ("All IDs in the database will be overwritten with "
+                   "current IDs in the dialog.\nClick Cancel to stop updating "
+                   "database.")
+            result = QtWidgets.QMessageBox.question(
+                self, "Confirmation", msg,
+                QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
+            if result == QtWidgets.QMessageBox.Cancel:
+                return False
+        sql_list = []
+        for row_idx in range(TOTAL_ROW):
+            sql = self.save_row_sql(row_idx)
+            if sql is not None:
+                sql_list.append(sql)
+        if len(sql_list) == 0:
+            self.parent.IDs = []
+            self.parent.IDsName = ''
+            self.parent.data_type = 'Unknown'
+            return True
+
+        ret = trunc_addDB('ChannelPrefer', sql_list)
+        if ret is not True:
+            displayTrackingInfo(self.parent, ret, "error")
+        self.parent.IDs = [
+            t.strip() for t in self.id_widget.text().split(',')]
+        self.parent.IDsName = self.name_widget.text().strip()
+        self.parent.IDsDataType = self.data_type_widget.currentText()
+        return True
+
+    def save_row_sql(self, row_idx):
+        current = 1 if self.id_table_widget.cellWidget(
+            row_idx, COL['sel']).isChecked() else 0
+        name = self.id_table_widget.cellWidget(
+            row_idx, COL['name']).text()
+        data_type = self.id_table_widget.cellWidget(
+            row_idx, COL['dataType']).currentText()
+        id = self.id_table_widget.cellWidget(
+            row_idx, COL['IDs']).text()
+        if id.strip() == '':
+            return
+        if name.strip() == '' and id.strip() != '':
+            msg = f"Please add Name for row {row_idx}."
+            QtWidgets.QMessageBox.information(self, "Missing info", msg)
+            return
+        return (f"INSERT INTO ChannelPrefer (name, IDs, dataType, current)"
+                f"VALUES ('{name}', '{id}', '{data_type}', {current})")
+
+    @QtCore.Slot()
+    def save_add_to_main_and_close(self):
+        if not self.save():
+            return
+        self.parent.currIDsNameLineEdit.setText(self.parent.IDsName)
+        self.parent.allChanCheckBox.setChecked(False)
+        self.close()
+
+    def get_data_types(self):
+        data_type_rows = executeDB(
+            'SELECT * FROM DataTypes ORDER BY dataType ASC')
+        return [d[0] for d in data_type_rows]
+
+    def get_db_channels(self, data_type):
+        channel_rows = executeDB(
+            f"SELECT channel FROM CHANNELS WHERE dataType='{data_type}' "
+            f" ORDER BY dataType ASC")
+        return [c[0] for c in channel_rows]
+
+    def get_id_rows(self):
+        i_ds_rows = executeDB_dict(
+            "SELECT name, IDs, dataType, current FROM ChannelPrefer "
+            " ORDER BY name ASC")
+        return i_ds_rows
diff --git a/sohstationviewer/view/channeldialog.py b/sohstationviewer/view/channeldialog.py
deleted file mode 100755
index 205f701d65d7738a4c7f4467a024b9c96db360d3..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/channeldialog.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-channeldialog.py
-GUI to add/edit/remove channels
-"""
-
-from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog
-from sohstationviewer.database.proccessDB import executeDB
-
-
-class ChannelDialog(Ui_DBInfoDialog):
-    def __init__(parent, self):
-        super().__init__(
-            parent, ['No.', 'Channel', 'Label', 'Param',
-                     'ConvertFactor', 'Unit', 'FixPoint'],
-            'channel', 'channels', resizeContentColumns=[0, 4, 5, 6],
-            needDataTypeChoice=True, requestedColumns={3: 'Param'},
-            checkFK=False)
-        self.setWindowTitle("Edit/Add/Delete Channels")
-
-    def updateDataTableWidgetItems(self):
-        paraRows = executeDB("SELECT param from parameters")
-        self.paramChoices = [''] + sorted([d[0] for d in paraRows])
-        super(ChannelDialog, self).updateDataTableWidgetItems()
-
-    def clearFirstRow(self):
-        """
-        device with no channels yet, there will be empty channel left
-        """
-        self.dataTableWidget.cellWidget(0, 1).setText('')
-        self.dataTableWidget.cellWidget(0, 2).setText('')
-        self.dataTableWidget.cellWidget(0, 3).setCurrentIndex(-1)
-        self.dataTableWidget.cellWidget(0, 4).setText('1')
-        self.dataTableWidget.cellWidget(0, 5).setText('')
-        self.dataTableWidget.cellWidget(0, 6).setValue(0)
-
-    def addRow(self, rowidx, fk=False):
-        self.addWidget(None, rowidx, 0)       # No.
-        self.addWidget(self.dataList, rowidx, 1, foreignkey=fk)  # chanID
-        self.addWidget(self.dataList, rowidx, 2)                 # label
-        self.addWidget(self.dataList, rowidx, 3, choices=self.paramChoices)
-        self.addWidget(self.dataList, rowidx, 4,
-                       fieldName='convertFactor')
-        self.addWidget(self.dataList, rowidx, 5)                 # unit
-        self.addWidget(self.dataList, rowidx, 6,
-                       range=[0, 5])                     # fixPoint
-
-    def dataTypeChanged(self):
-        self.dataType = self.dataTypeCombobox.currentText()
-        self.updateDataTableWidgetItems()
-
-    def getDataList(self):
-        channelRows = executeDB(
-            f"SELECT channel, label, param, convertFactor, unit, fixPoint "
-            f"FROM Channels "
-            f"WHERE dataType='{self.dataType}'")
-        return [[d[0], d[1], d[2], d[3],
-                 '' if d[4] is None else d[4],
-                 d[5]]
-                for d in channelRows]
-
-    def getRowInputs(self, rowidx):
-        return [
-            self.dataTableWidget.cellWidget(rowidx, 1).text().strip(),
-            self.dataTableWidget.cellWidget(rowidx, 2).text().strip(),
-            self.dataTableWidget.cellWidget(rowidx, 3).currentText().strip(),
-            float(self.dataTableWidget.cellWidget(rowidx, 4).text()),
-            self.dataTableWidget.cellWidget(rowidx, 5).text(),
-            self.dataTableWidget.cellWidget(rowidx, 6).value()
-        ]
-
-    def removeRow(self, removeRowidx):
-        self.dataTableWidget.removeRow(removeRowidx)
-        for i in range(removeRowidx, self.dataTableWidget.rowCount()):
-            cellWget = self.dataTableWidget.cellWidget(i, 0)
-            cellWget.setText(str(i))
-
-    def updateData(self, row, widgetidx, listidx):
-        insertsql = (f"INSERT INTO Channels VALUES"
-                     f"('{row[0]}', '{row[1]}', '{row[2]}',"
-                     f" {row[3]}, '{row[4]}', {row[5]}, '{self.dataType}')")
-        updatesql = (f"UPDATE Channels SET channel='{row[0]}', "
-                     f"label='{row[1]}', param='{row[2]}', "
-                     f"convertFactor={row[3]}, unit='{row[4]}' "
-                     f"fixPoint={row[5]} "
-                     f"WHERE channel='%s'"
-                     f" AND dataType='{self.dataType}'")
-        return super().updateData(
-            row, widgetidx, listidx, insertsql, updatesql)
diff --git a/sohstationviewer/view/channelpreferdialog.py b/sohstationviewer/view/channelpreferdialog.py
deleted file mode 100755
index 25e62dd9a1118feafaaadb6f466de6de3ddff0c9..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/channelpreferdialog.py
+++ /dev/null
@@ -1,299 +0,0 @@
-from PySide2 import QtWidgets, QtCore
-
-from sohstationviewer.database.proccessDB import (
-    executeDB, trunc_addDB, executeDB_dict)
-from sohstationviewer.controller.processing import readChannels, detectDataType
-from sohstationviewer.controller.util import displayTrackingInfo
-
-INSTRUCTION = """
-Place lists of channels to be read in the IDs field.\n
-Select the radiobutton for the list to be used in plotting.
-"""
-TOTAL_ROW = 20
-
-COL = {'sel': 0, 'name': 1, 'dataType': 2, 'IDs': 3, 'clr': 4}
-
-
-class ChannelPreferDialog(QtWidgets.QWidget):
-    def __init__(self, parent, dirnames):
-        super(ChannelPreferDialog, self).__init__()
-        self.parent = parent
-        self.dirnames = dirnames
-        self.setWindowTitle("Channel Preferences")
-        self.setGeometry(100, 100, 1100, 800)
-        mainLayout = QtWidgets.QVBoxLayout()
-        mainLayout.setContentsMargins(7, 7, 7, 7)
-        self.setLayout(mainLayout)
-
-        mainLayout.addWidget(QtWidgets.QLabel(INSTRUCTION))
-
-        self.createIDsTableWidget()
-        mainLayout.addWidget(self.IDsTableWidget, 1)
-
-        buttonLayout = self.createButtonsSection()
-        mainLayout.addLayout(buttonLayout)
-
-        self.trackingInfoTextBrowser = QtWidgets.QTextBrowser(self)
-        self.trackingInfoTextBrowser.setFixedHeight(60)
-        mainLayout.addWidget(self.trackingInfoTextBrowser)
-        self.changed = False
-
-    def createButtonsSection(self):
-        hLayout = QtWidgets.QHBoxLayout()
-        self.addDBChanBtn = QtWidgets.QPushButton(
-            self, text='Add DB Channels')
-        self.addDBChanBtn.clicked.connect(self.addDBChannels)
-        hLayout.addWidget(self.addDBChanBtn)
-
-        self.scanChanBtn = QtWidgets.QPushButton(
-            self, text='Scan Channels from Data Source')
-        self.scanChanBtn.clicked.connect(self.scanChannels)
-        hLayout.addWidget(self.scanChanBtn)
-
-        self.saveBtn = QtWidgets.QPushButton(self, text='Save')
-        self.saveBtn.clicked.connect(self.save)
-        hLayout.addWidget(self.saveBtn)
-
-        self.save_addMainBtn = QtWidgets.QPushButton(
-            self, text='Save - Add to Main')
-        self.save_addMainBtn.clicked.connect(self.save_addToMainNClose)
-        hLayout.addWidget(self.save_addMainBtn)
-
-        self.closeBtn = QtWidgets.QPushButton(self, text='Cancel')
-        self.closeBtn.clicked.connect(self.close)
-        hLayout.addWidget(self.closeBtn)
-        return hLayout
-
-    def createIDsTableWidget(self):
-        self.IDsTableWidget = QtWidgets.QTableWidget(self)
-        # self.IDsTableWidget.verticalHeader().hide()
-        self.IDsTableWidget.setColumnCount(5)
-        colHeaders = ['', 'Name', 'DataType', 'IDs', 'Clear']
-        self.IDsTableWidget.setHorizontalHeaderLabels(colHeaders)
-        header = self.IDsTableWidget.horizontalHeader()
-        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
-        header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch)
-
-        self.IDsTableWidget.setRowCount(TOTAL_ROW)
-        self.availDataTypes = self.getDataTypes()
-        for rowidx in range(TOTAL_ROW):
-            self.addRow(rowidx)
-
-        self.currRow = -1
-        self.updateDataTableWidgetItems()
-
-    @QtCore.Slot()
-    def addRow(self, rowidx):
-        currSelRadioBtn = QtWidgets.QRadioButton(self)
-        currSelRadioBtn.clicked.connect(
-            lambda checked: self.currSelChanged(rowidx))
-        self.IDsTableWidget.setCellWidget(
-            rowidx, COL['sel'], currSelRadioBtn)
-
-        nameLineEdit = QtWidgets.QLineEdit(self)
-        nameLineEdit.textChanged.connect(self.inputChanged)
-        self.IDsTableWidget.setCellWidget(
-            rowidx, COL['name'], nameLineEdit)
-
-        dataTypeCombobox = QtWidgets.QComboBox(self)
-        dataTypeCombobox.currentIndexChanged.connect(self.inputChanged)
-        dataTypeCombobox.addItems(['Unknown'] + self.availDataTypes)
-        dataTypeCombobox.setCurrentIndex(-1)
-        self.IDsTableWidget.setCellWidget(
-            rowidx, COL['dataType'], dataTypeCombobox)
-
-        IDsLineEdit = QtWidgets.QLineEdit(self)
-        IDsLineEdit.textChanged.connect(self.inputChanged)
-        self.IDsTableWidget.setCellWidget(
-            rowidx, COL['IDs'], IDsLineEdit)
-
-        delButton = QtWidgets.QPushButton(self, text='CLR')
-        delButton.clicked.connect(lambda arg: self.clearIDs(rowidx))
-        self.IDsTableWidget.setCellWidget(
-            rowidx, COL['clr'], delButton)
-
-    @QtCore.Slot()
-    def inputChanged(self):
-        self.changed = True
-
-    def updateDataTableWidgetItems(self):
-        IDsRows = self.getIDsRows()
-        # first row
-        self.IDsTableWidget.cellWidget(0, COL['sel']).setChecked(True)
-        self.currSelChanged(0)
-        count = 0
-        for r in IDsRows:
-            self.IDsTableWidget.cellWidget(
-                count, COL['sel']).setChecked(
-                True if ['current'] == 1 else False)
-            self.IDsTableWidget.cellWidget(
-                count, COL['name']).setText(r['name'])
-            self.IDsTableWidget.cellWidget(
-                count, COL['dataType']).setCurrentText(r['dataType'])
-            self.IDsTableWidget.cellWidget(
-                count, COL['IDs']).setText(r['IDs'])
-            self.IDsTableWidget.cellWidget(
-                count, COL['sel']).setChecked(
-                True if r['current'] == 1 else False)
-            if ['current'] == 1:
-                self.currSelChanged(count)
-            count += 1
-        self.update()
-
-    def getRow(self, rowidx):
-        nameWget = self.IDsTableWidget.cellWidget(rowidx, COL['name'])
-        dataTypeWget = self.IDsTableWidget.cellWidget(rowidx, COL['dataType'])
-        IDsWget = self.IDsTableWidget.cellWidget(rowidx, COL['IDs'])
-        clearWget = self.IDsTableWidget.cellWidget(rowidx, COL['clr'])
-        return (nameWget, dataTypeWget, IDsWget, clearWget)
-
-    @QtCore.Slot()
-    def currSelChanged(self, rowidx):
-        self.currRow = rowidx
-        (self.nameWget, self.dataTypeWget,
-         self.IDsWget, self.clearWget) = self.getRow(rowidx)
-        self.inputChanged()
-
-    @QtCore.Slot()
-    def clearIDs(self, rowidx):
-        (nameWget, dataTypeWget, IDsWget, clearWget) = self.getRow(rowidx)
-        if IDsWget.text().strip() != "":
-            msg = ("Are you sure you want to delete the channel IDs of  "
-                   "row #%s?" % (rowidx+1))
-            result = QtWidgets.QMessageBox.question(
-                self, "Confirmation", msg,
-                QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
-            if result == QtWidgets.QMessageBox.No:
-                return
-            self.changed = True
-        if nameWget.text() != '':
-            self.changed = True
-        nameWget.setText('')
-        if dataTypeWget.currentText != '':
-            self.changed = True
-        dataTypeWget.setCurrentIndex(-1)
-        if IDsWget.text() != '':
-            self.changed = True
-        IDsWget.setText('')
-
-    def validateRow(self, checkDataType=False):
-        if self.currRow == -1:
-            msg = ("Please select a row.")
-            QtWidgets.QMessageBox.information(self, "Select row", msg)
-            return False
-
-        if checkDataType:
-            self.dataType = self.dataTypeWget.currentText()
-            if self.dataType not in self.availDataTypes:
-                msg = ("Please select a data type that isn't 'Unknown' for "
-                       "the selected row.")
-                QtWidgets.QMessageBox.information(
-                    self, "Select data type", msg)
-                return False
-        # # check IDs
-        # if self.IDsWget.text().strip() != '':
-        #     msg = ("The selected row's IDs will be overwritten.\n"
-        #            "Do you want to continue?")
-        #     result = QtWidgets.QMessageBox.question(
-        #         self, "Confirmation", msg,
-        #         QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
-        #     if result == QtWidgets.QMessageBox.No:
-        #         return False
-        return True
-
-    @QtCore.Slot()
-    def addDBChannels(self):
-        if not self.validateRow(checkDataType=True):
-            return
-
-        dbChannels = self.getDBChannels(self.dataType)
-        self.IDsWget.setText(','.join(dbChannels))
-
-    @QtCore.Slot()
-    def scanChannels(self):
-        if not self.validateRow():
-            return
-
-        dataType = detectDataType(self, self.dirnames)
-        if dataType in self.availDataTypes:
-            self.dataTypeWget.setCurrentText(dataType)
-        else:
-            self.dataTypeWget.setCurrenText('Unknown')
-        scannedChannels = readChannels(self, self.dirnames)
-        self.IDsWget.setText(','.join(scannedChannels))
-
-    @QtCore.Slot()
-    def save(self):
-        if not self.validateRow():
-            return
-        if self.changed:
-            msg = ("All IDs in the database will be overwritten with "
-                   "current IDs in the dialog.\nClick Cancel to stop updating "
-                   "database.")
-            result = QtWidgets.QMessageBox.question(
-                self, "Confirmation", msg,
-                QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
-            if result == QtWidgets.QMessageBox.Cancel:
-                return False
-        sqlList = []
-        for rowidx in range(TOTAL_ROW):
-            sql = self.saveRowSql(rowidx)
-            if sql is not None:
-                sqlList.append(sql)
-        if len(sqlList) == 0:
-            self.parent.IDs = []
-            self.parent.IDsName = ''
-            self.parent.dataType = 'Unknown'
-            return True
-
-        ret = trunc_addDB('ChannelPrefer', sqlList)
-        if ret is not True:
-            displayTrackingInfo(self.parent, ret, "error")
-        self.parent.IDs = [
-            t.strip() for t in self.IDsWget.text().split(',')]
-        self.parent.IDsName = self.nameWget.text().strip()
-        self.parent.IDsDataType = self.dataTypeWget.currentText()
-        return True
-
-    def saveRowSql(self, rowidx):
-        current = 1 if self.IDsTableWidget.cellWidget(
-            rowidx, COL['sel']).isChecked() else 0
-        name = self.IDsTableWidget.cellWidget(
-            rowidx, COL['name']).text()
-        dataType = self.IDsTableWidget.cellWidget(
-            rowidx, COL['dataType']).currentText()
-        IDs = self.IDsTableWidget.cellWidget(
-            rowidx, COL['IDs']).text()
-        if IDs.strip() == '':
-            return
-        if name.strip() == '' and IDs.strip() != '':
-            msg = f"Please add Name for row {rowidx}."
-            QtWidgets.QMessageBox.information(self, "Missing info", msg)
-            return
-        return (f"INSERT INTO ChannelPrefer (name, IDs, dataType, current)"
-                f"VALUES ('{name}', '{IDs}', '{dataType}', {current})")
-
-    @QtCore.Slot()
-    def save_addToMainNClose(self):
-        if not self.save():
-            return
-        self.parent.currIDsNameLineEdit.setText(self.parent.IDsName)
-        self.parent.allChanCheckBox.setChecked(False)
-        self.close()
-
-    def getDataTypes(self):
-        dataTypeRows = executeDB(
-            'SELECT * FROM DataTypes ORDER BY dataType ASC')
-        return [d[0] for d in dataTypeRows]
-
-    def getDBChannels(self, dataType):
-        channelRows = executeDB(
-            f"SELECT channel FROM CHANNELS WHERE dataType='{dataType}' "
-            f" ORDER BY dataType ASC")
-        return [c[0] for c in channelRows]
-
-    def getIDsRows(self):
-        IDsRows = executeDB_dict(
-            "SELECT name, IDs, dataType, current FROM ChannelPrefer "
-            " ORDER BY name ASC")
-        return IDsRows
diff --git a/sohstationviewer/view/core/calendarwidget.py b/sohstationviewer/view/core/calendar_widget.py
similarity index 52%
rename from sohstationviewer/view/core/calendarwidget.py
rename to sohstationviewer/view/core/calendar_widget.py
index fea881591b0756bfade96981bab6216bdc19ba3b..f476d95207944151872bfa0ea4c5d27d9760d578 100644
--- a/sohstationviewer/view/core/calendarwidget.py
+++ b/sohstationviewer/view/core/calendar_widget.py
@@ -6,36 +6,39 @@ class CalendarWidget(QtWidgets.QCalendarWidget):
     def __init__(self, parent):
         super().__init__(parent)
         self.setupUi()
-        self._showDayOfYear = getattr(self, 'toggleDayOfYear', None) is None
+        self._show_day_of_year = \
+            getattr(self, 'toggle_day_of_year', None) is None
 
     def setupUi(self):
         self.setMinimumWidth(300)
         self.setMinimumHeight(275)
 
-        navbar = self.findChild(QtWidgets.QWidget, 'qt_calendar_navigationbar')
+        nav_bar = self.findChild(
+            QtWidgets.QWidget, 'qt_calendar_navigationbar'
+        )
 
-        if navbar:
-            self.toggleDayOfYear = QtWidgets.QCheckBox('Show DOY')
-            navbar.layout().insertWidget(2, self.toggleDayOfYear)
+        if nav_bar:
+            self.toggle_day_of_year = QtWidgets.QCheckBox('Show DOY')
+            nav_bar.layout().insertWidget(2, self.toggle_day_of_year)
 
-            self.toggleDayOfYear.toggled.connect(self.setShowDayOfYear)
-            self.toggleDayOfYear.setCheckable(True)
+            self.toggle_day_of_year.toggled.connect(self.set_show_day_of_year)
+            self.toggle_day_of_year.setCheckable(True)
 
-            palette = self.toggleDayOfYear.palette()
+            palette = self.toggle_day_of_year.palette()
             palette.setColor(QtGui.QPalette.WindowText, QtGui.QColor('white'))
-            self.toggleDayOfYear.setPalette(palette)
+            self.toggle_day_of_year.setPalette(palette)
 
-            self.toggleDayOfYear.show()
+            self.toggle_day_of_year.show()
 
-    def showDayOfYear(self):
-        return self._showDayOfYear
+    def show_day_of_year(self):
+        return self._show_day_of_year
 
     def paintCell(self, painter, rect, date):
         super().paintCell(painter, rect, date)
 
         painter.save()
 
-        if self.showDayOfYear():
+        if self.show_day_of_year():
             color = (QtGui.QColor('red') if date != self.selectedDate()
                      else QtGui.QColor('white'))
             font = painter.font()
@@ -46,6 +49,6 @@ class CalendarWidget(QtWidgets.QCalendarWidget):
         painter.restore()
 
     @QtCore.Slot(bool)
-    def setShowDayOfYear(self, value):
-        self._showDayOfYear = value
+    def set_show_day_of_year(self, value):
+        self._show_day_of_year = value
         self.updateCells()
diff --git a/sohstationviewer/view/core/db_gui_superclass.py b/sohstationviewer/view/core/db_gui_superclass.py
new file mode 100755
index 0000000000000000000000000000000000000000..a961f17891c6fc8d27047e2e76e385108463e023
--- /dev/null
+++ b/sohstationviewer/view/core/db_gui_superclass.py
@@ -0,0 +1,366 @@
+from PySide2 import QtWidgets, QtGui
+
+from sohstationviewer.database.proccessDB import executeDB
+
+
+def set_widget_color(widget, changed=False, read_only=False):
+    palette = QtGui.QPalette()
+
+    if read_only:
+        # grey text
+        palette.setColor(QtGui.QPalette.Text, QtGui.QColor(100, 100, 100))
+        # light blue background
+        palette.setColor(QtGui.QPalette.Base, QtGui.QColor(210, 240, 255))
+        widget.setReadOnly(True)
+        widget.setPalette(palette)
+        return
+    else:
+        # white background
+        palette.setColor(QtGui.QPalette.Base, QtGui.QColor(255, 255, 255))
+    if changed:
+        # red text
+        palette.setColor(QtGui.QPalette.Text, QtGui.QColor(255, 0, 0))
+    else:
+        try:
+            if widget.isReadOnly():
+                # grey text
+                palette.setColor(
+                    QtGui.QPalette.Text, QtGui.QColor(100, 100, 100))
+            else:
+                # black text
+                palette.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0))
+        except AttributeError:
+            palette.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0))
+    widget.setPalette(palette)
+
+
+class Ui_DBInfoDialog(QtWidgets.QWidget):
+    def __init__(self, parent, column_headers, col_name, table_name,
+                 resize_content_columns=[], requested_columns={},
+                 need_data_type_choice=False, check_fk=True):
+        self.total_col = len(column_headers)
+        self.column_headers = column_headers
+        self.resize_content_columns = resize_content_columns
+        self.requested_columns = requested_columns
+        self.need_data_type_choice = need_data_type_choice
+        self.col_name = col_name
+        self.table_name = table_name
+        self.check_fk = check_fk
+        super(Ui_DBInfoDialog, self).__init__()
+
+        self.setGeometry(100, 100, 900, 900)
+        main_layout = QtWidgets.QVBoxLayout()
+        self.setLayout(main_layout)
+
+        button_layout = self.create_buttons_section()
+        main_layout.addLayout(button_layout)
+
+        self.create_data_table_widget()
+        main_layout.addWidget(self.data_table_widget, 1)
+        if self.table_name != '':
+            instruction = ("Background: LIGHT BLUE - Non Editable due to "
+                           "FK constrain; "
+                           "WHITE - Editable.    "
+                           "Text: BLACK - Saved; RED - Not saved")
+            # TODO: add question mark button to give instruction
+
+            main_layout.addWidget(QtWidgets.QLabel(instruction))
+
+    def addWidget(self, data_list, row_idx, col_idx, foreign_key=False,
+                  choices=None, range=None, field_name=''):
+        if data_list is None:
+            text = str(row_idx)  # row number
+        else:
+            text = (str(data_list[row_idx][col_idx - 1])
+                    if row_idx < len(data_list) else '')
+
+        if data_list is None:
+            widget = QtWidgets.QPushButton(text)
+        elif choices is None and range is None:
+            widget = QtWidgets.QLineEdit(text)
+            if field_name == 'convertFactor':
+                # precision=6
+                validator = QtGui.QDoubleValidator(0.0, 5.0, 6)
+                validator.setNotation(QtGui.QDoubleValidator.StandardNotation)
+                widget.setValidator(validator)
+                if text == '':
+                    widget.setText('1')
+        elif choices:
+            widget = QtWidgets.QComboBox()
+            widget.addItems(choices)
+            widget.setCurrentText(text)
+        elif range:
+            widget = QtWidgets.QSpinBox()
+            widget.setMinimum(range[0])
+            widget.setMaximum(range[1])
+            if text in ["", None, 'None']:
+                widget.setValue(range[0])
+            else:
+                widget.setValue(int(text))
+
+        if data_list is None:
+            set_widget_color(widget)
+            widget.setFixedWidth(40)
+            widget.clicked.connect(
+                lambda: self.row_number_clicked(widget))
+        elif foreign_key:
+            set_widget_color(widget, read_only=True)
+        else:
+            new = False if row_idx < len(data_list) else True
+            set_widget_color(widget, read_only=False, changed=new)
+            if choices is None and range is None:
+                widget.textChanged.connect(
+                    lambda changed_text:
+                    self.cell_input_change(changed_text, row_idx, col_idx))
+            elif choices:
+                widget.currentTextChanged.connect(
+                    lambda changed_text:
+                    self.cell_input_change(changed_text, row_idx, col_idx))
+            elif range:
+                widget.valueChanged.connect(
+                    lambda changed_text:
+                    self.cell_input_change(changed_text, row_idx, col_idx))
+        self.data_table_widget.setCellWidget(row_idx, col_idx, widget)
+
+    def row_number_clicked(self, widget):
+        self.data_table_widget.selectRow(int(widget.text()))
+        self.data_table_widget.repaint()
+
+    def create_buttons_section(self):
+        h_layout = QtWidgets.QHBoxLayout()
+
+        if self.need_data_type_choice:
+            self.data_type_combo_box = QtWidgets.QComboBox(self)
+            data_type_rows = executeDB('SELECT * FROM DataTypes')
+            self.data_type_combo_box.addItems([d[0] for d in data_type_rows])
+            self.data_type_combo_box.currentTextChanged.connect(
+                self.data_type_changed)
+            h_layout.addWidget(self.data_type_combo_box)
+        if self.table_name != '':
+            self.add_row_btn = QtWidgets.QPushButton(self, text='ADD ROW')
+            self.add_row_btn.setFixedWidth(300)
+            self.add_row_btn.clicked.connect(self.add_new_row)
+            h_layout.addWidget(self.add_row_btn)
+
+            self.save_changes_btn = QtWidgets.QPushButton(
+                self, text='SAVE CHANGES')
+            self.save_changes_btn.setFixedWidth(300)
+            self.save_changes_btn.clicked.connect(self.save_changes)
+            h_layout.addWidget(self.save_changes_btn)
+
+        self.close_btn = QtWidgets.QPushButton(self, text='CLOSE')
+        self.close_btn.setFixedWidth(300)
+        self.close_btn.clicked.connect(self.close)
+        h_layout.addWidget(self.close_btn)
+        return h_layout
+
+    def create_data_table_widget(self):
+        self.data_table_widget = QtWidgets.QTableWidget(self)
+        self.data_table_widget.verticalHeader().hide()
+        self.data_table_widget.setColumnCount(self.total_col)
+        self.data_table_widget.setHorizontalHeaderLabels(self.column_headers)
+        header = self.data_table_widget.horizontalHeader()
+        header.setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
+        for i in self.resize_content_columns:
+            header.setSectionResizeMode(
+                i, QtWidgets.QHeaderView.ResizeToContents)
+
+        if self.need_data_type_choice:
+            self.data_type = self.data_type_combo_box.currentText()
+        self.update_data_table_widget_items()
+
+    def update_data_table_widget_items(self):
+        self.data_list = self.get_data_list()
+        row_count = len(self.data_list)
+        row_count = 1 if row_count == 0 else row_count
+        self.data_table_widget.setRowCount(row_count)
+        for i in range(len(self.data_list)):
+            fk = self.check_data_foreign_key(self.data_list[i][0])
+            self.add_row(i, fk)
+        if len(self.data_list) == 0:
+            """
+            No Row, should leave 1 empty row
+            """
+            self.clearFirstRow()
+        self.update()
+
+    def add_new_row(self):
+        row_position = self.data_table_widget.rowCount()
+        self.data_table_widget.insertRow(row_position)
+        self.add_row(row_position)
+        self.data_table_widget.scrollToBottom()
+        self.data_table_widget.repaint()  # to show row's header
+        self.data_table_widget.cellWidget(row_position, 1).setFocus()
+
+    def remove_row(self, remove_row_idx):
+        self.data_table_widget.removeRow(remove_row_idx)
+        for i in range(remove_row_idx, self.data_table_widget.rowCount()):
+            cell_widget = self.data_table_widget.cellWidget(i, 0)
+            cell_widget.setText(str(i))
+
+    def cell_input_change(self, changed_text, rowidx, colidx):
+        """
+        If cell's value is changed, text's color will be red
+        otherwise, text's color will be black
+        """
+        changed = False
+        if rowidx < len(self.data_list):
+            if changed_text != self.data_list[rowidx][colidx - 1]:
+                changed = True
+            cell_widget = self.data_table_widget.cellWidget(rowidx, colidx)
+            set_widget_color(cell_widget, changed=changed)
+
+    def check_data_foreign_key(self, val):
+        if not self.check_fk:
+            return False
+        sql = (f"SELECT {self.col_name} FROM channels "
+               f"WHERE {self.col_name}='{val}'")
+        param_rows = executeDB(sql)
+        if len(param_rows) > 0:
+            return True
+        else:
+            return False
+
+    def reset_row_inputs(self, reset, widget_idx, list_idx):
+        for col_idx in range(1, self.total_col):
+            cell_widget = self.data_table_widget.cellWidget(
+                widget_idx,
+                col_idx
+            )
+            read_only = False
+            if hasattr(cell_widget, 'isReadOnly'):
+                read_only = cell_widget.isReadOnly()
+            if not read_only:
+                if reset == 1:
+                    org_val = self.data_list[list_idx][col_idx - 1]
+                    if isinstance(cell_widget, QtWidgets.QLineEdit):
+                        cell_widget.setText(str(org_val))
+                    elif isinstance(cell_widget, QtWidgets.QComboBox):
+                        cell_widget.setCurrentText(str(org_val))
+                    elif isinstance(cell_widget, QtWidgets.QSpinBox):
+                        cell_widget.setValue(int(org_val))
+                set_widget_color(cell_widget)
+
+    def add_row(self, row_idx, fk=False):
+        pass
+
+    def data_type_changed(self):
+        pass
+
+    def save_changes(self):
+        try:
+            self.data_table_widget.focusWidget().clearFocus()
+        except AttributeError:
+            pass
+        self.remove_count = 0
+        self.insert_count = 0
+        self.skip_count = 0
+        row_count = self.data_table_widget.rowCount()
+        for i in range(row_count):
+            widget_idx = i - (row_count - self.data_table_widget.rowCount())
+            list_idx = (i - self.remove_count
+                        + self.insert_count - self.skip_count)
+            row_inputs = self.get_row_inputs(widget_idx)
+            reset = self.update_data(row_inputs, widget_idx, list_idx)
+            if reset > -1:
+                self.reset_row_inputs(reset, widget_idx, list_idx)
+
+    def update_data(self, row, widget_idx, list_idx, insert_sql, update_sql):
+        """
+        update data_table_widget and data_list according to the action to
+        add, remove, update
+        :param row: values of processed row in data_table_widget
+        :param widget_idx: index of the process rows in data_table_widget
+        :param list_idx: index of the process rows in self.data_list
+        :param col_name: key db column in the table
+        :param table_name: data table name
+        :param insert_sql: query to insert a row to the table
+        :param update_sql: query to update a row in the table
+        :return -1 for doing nothing
+                0 no need to reset input values to org row
+                1 need to reset input values to org row
+        """
+        if list_idx < len(self.data_list):
+            org_row = self.data_list[list_idx]
+            if row[0] == "":
+                msg = (f"The {self.col_name} of '{org_row}' at "
+                       f"row {widget_idx} has been changed to blank. "
+                       f"Are you sure you want to delete this row in "
+                       f"database?")
+                result = QtWidgets.QMessageBox.question(
+                    self, "Delete %s?" % org_row, msg,
+                          QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
+                )
+                if result == QtWidgets.QMessageBox.No:
+                    # reset the first widget value and call the function again
+                    # to check other fields
+                    cell_widget = self.data_table_widget.cellWidget(
+                        widget_idx, 1
+                    )
+                    cell_widget.setText(org_row[0])
+                    row[0] = org_row[0]
+                    self.update_data(row, widget_idx, list_idx)
+                else:
+                    sql = (f"DELETE FROM {self.table_name} "
+                           f"WHERE {self.col_name}='{org_row[0]}'")
+                    executeDB(sql)
+                    self.data_list.remove(org_row)
+                    self.remove_row(widget_idx)
+                    self.remove_count += 1
+                    return -1
+
+            if row == org_row:
+                return -1
+            if (row[0] in [p[0] for p in self.data_list] and
+                    self.data_list[list_idx][0] != row[0]):
+                msg = (f"The {self.col_name} of {org_row} at row"
+                       f" {widget_idx} has been changed to '{row[0]}' "
+                       f"which already is in the database. "
+                       f"It will be changed back to {org_row[0]}.")
+                QtWidgets.QMessageBox.information(self, "Error", msg)
+                # reset the first widget value and call the function again
+                # to check other fields
+                cell_widget = self.data_table_widget.cellWidget(widget_idx, 1)
+                cell_widget.setText(org_row[0])
+                row[0] = org_row[0]
+                self.update_data(row, widget_idx, list_idx)
+            else:
+                msg = (f"{org_row} at row {widget_idx} has "
+                       f"been changed to {row}. Please confirm it.")
+                result = QtWidgets.QMessageBox.question(
+                    self, "Confirmation", msg,
+                    QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel
+                )
+                if result == QtWidgets.QMessageBox.Cancel:
+                    return 1
+                else:
+                    executeDB(update_sql % org_row[0])
+                    self.data_list[list_idx] = row
+                    return 0
+
+        if row[0] == "":
+            msg = (f"Row {widget_idx} has blank {self.col_name}. "
+                   f"It will be removed.")
+            QtWidgets.QMessageBox.information(self, "Error", msg)
+            self.remove_row(widget_idx)
+            return -1
+        blank_requested_columns = [self.requested_columns[i]
+                                   for i in self.requested_columns.keys()
+                                   if row[i - 1] == ""]
+        if blank_requested_columns != []:
+            msg = (f"Row {widget_idx}: blank "
+                   f"{', '.join(blank_requested_columns)} which require some "
+                   f"value(s).")
+            QtWidgets.QMessageBox.information(self, "Error", msg)
+            return -1
+
+        if row[0] in [p[0] for p in self.data_list]:
+            msg = (f"The {self.col_name} '{row[0]}' is already in the "
+                   f"database. Row {widget_idx} will be removed.")
+            QtWidgets.QMessageBox.information(self, "Error", msg)
+            self.remove_row(widget_idx)
+            return -1
+        executeDB(insert_sql)
+        self.data_list.append(row)
+        self.insert_count += 1
+        return 0
diff --git a/sohstationviewer/view/core/dbgui_superclass.py b/sohstationviewer/view/core/dbgui_superclass.py
deleted file mode 100755
index 45dc5189241ad68adceaf157f2bfbb9d1bf452aa..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/core/dbgui_superclass.py
+++ /dev/null
@@ -1,368 +0,0 @@
-from PySide2 import QtWidgets, QtGui
-
-from sohstationviewer.database.proccessDB import executeDB
-
-
-def setWidgetColor(widget, changed=False, readOnly=False):
-    pallete = QtGui.QPalette()
-
-    if readOnly:
-        # grey text
-        pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(100, 100, 100))
-        # light blue background
-        pallete.setColor(QtGui.QPalette.Base, QtGui.QColor(210, 240, 255))
-        widget.setReadOnly(True)
-        widget.setPalette(pallete)
-        return
-    else:
-        # white background
-        pallete.setColor(QtGui.QPalette.Base, QtGui.QColor(255, 255, 255))
-    if changed:
-        # red text
-        pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(255, 0, 0))
-    else:
-        try:
-            if widget.isReadOnly():
-                # grey text
-                pallete.setColor(
-                    QtGui.QPalette.Text, QtGui.QColor(100, 100, 100))
-            else:
-                # black text
-                pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0))
-        except AttributeError:
-            pallete.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0))
-    widget.setPalette(pallete)
-
-
-class Ui_DBInfoDialog(QtWidgets.QWidget):
-    def __init__(self, parent, columnHeaders, colName, tableName,
-                 resizeContentColumns=[], requestedColumns={},
-                 needDataTypeChoice=False, checkFK=True):
-        self.totalCol = len(columnHeaders)
-        self.columnHeaders = columnHeaders
-        self.resizeContentColumns = resizeContentColumns
-        self.requestedColumns = requestedColumns
-        self.needDataTypeChoice = needDataTypeChoice
-        self.colName = colName
-        self.tableName = tableName
-        self.checkFK = checkFK
-        super(Ui_DBInfoDialog, self).__init__()
-
-        self.setGeometry(100, 100, 900, 900)
-        mainLayout = QtWidgets.QVBoxLayout()
-        self.setLayout(mainLayout)
-
-        buttonLayout = self.createButtonsSection()
-        mainLayout.addLayout(buttonLayout)
-
-        self.createDataTableWidget()
-        mainLayout.addWidget(self.dataTableWidget, 1)
-        if self.tableName != '':
-            instruction = ("Background: LIGHT BLUE - Non Editable due to "
-                           "FK constrain; "
-                           "WHITE - Editable.    "
-                           "Text: BLACK - Saved; RED - Not saved")
-            # TODO: add question mark button to give instruction
-            # if self.tableName == 'parameters':
-            #     instruction += (
-            #         "\nValueColors is requested for multiColorDots plotType."
-            #         "Value from low to high"
-            #         "\nThe format for less than or equal value pair is: "
-            #         "value:color"
-            #         "\nThe format for greater than value pair is: "
-            #         "+value:color with "
-            #         "color=R,Y,G,M,C.\n Ex: 2.3:C|+4:M")
-
-            mainLayout.addWidget(QtWidgets.QLabel(instruction))
-
-    def addWidget(self, dataList, rowidx, colidx, foreignkey=False,
-                  choices=None, range=None, fieldName=''):
-        if dataList is None:
-            text = str(rowidx)      # row number
-        else:
-            text = (str(dataList[rowidx][colidx - 1])
-                    if rowidx < len(dataList) else '')
-
-        if dataList is None:
-            widget = QtWidgets.QPushButton(text)
-        elif choices is None and range is None:
-            widget = QtWidgets.QLineEdit(text)
-            if fieldName == 'convertFactor':
-                # precision=6
-                validator = QtGui.QDoubleValidator(0.0, 5.0, 6)
-                validator.setNotation(QtGui.QDoubleValidator.StandardNotation)
-                widget.setValidator(validator)
-                if text == '':
-                    widget.setText('1')
-        elif choices:
-            widget = QtWidgets.QComboBox()
-            widget.addItems(choices)
-            widget.setCurrentText(text)
-        elif range:
-            widget = QtWidgets.QSpinBox()
-            widget.setMinimum(range[0])
-            widget.setMaximum(range[1])
-            if text in ["", None, 'None']:
-                widget.setValue(range[0])
-            else:
-                widget.setValue(int(text))
-
-        if dataList is None:
-            setWidgetColor(widget)
-            widget.setFixedWidth(40)
-            widget.clicked.connect(
-                lambda: self.rowNumberClicked(widget))
-        elif foreignkey:
-            setWidgetColor(widget, readOnly=True)
-        else:
-            new = False if rowidx < len(dataList) else True
-            setWidgetColor(widget, readOnly=False, changed=new)
-            if choices is None and range is None:
-                widget.textChanged.connect(
-                    lambda changedtext:
-                    self.cellInputChange(changedtext, rowidx, colidx))
-            elif choices:
-                widget.currentTextChanged.connect(
-                    lambda changedtext:
-                    self.cellInputChange(changedtext, rowidx, colidx))
-            elif range:
-                widget.valueChanged.connect(
-                    lambda changedtext:
-                    self.cellInputChange(changedtext, rowidx, colidx))
-        self.dataTableWidget.setCellWidget(rowidx, colidx, widget)
-
-    def rowNumberClicked(self, widget):
-        self.dataTableWidget.selectRow(int(widget.text()))
-        self.dataTableWidget.repaint()
-
-    def createButtonsSection(self):
-        hLayout = QtWidgets.QHBoxLayout()
-
-        if self.needDataTypeChoice:
-            self.dataTypeCombobox = QtWidgets.QComboBox(self)
-            dataTypeRows = executeDB('SELECT * FROM DataTypes')
-            self.dataTypeCombobox.addItems([d[0] for d in dataTypeRows])
-            self.dataTypeCombobox.currentTextChanged.connect(
-                self.dataTypeChanged)
-            hLayout.addWidget(self.dataTypeCombobox)
-        if self.tableName != '':
-            self.addRowBtn = QtWidgets.QPushButton(self, text='ADD ROW')
-            self.addRowBtn.setFixedWidth(300)
-            self.addRowBtn.clicked.connect(self.addNewRow)
-            hLayout.addWidget(self.addRowBtn)
-
-            self.saveChangesBtn = QtWidgets.QPushButton(
-                self, text='SAVE CHANGES')
-            self.saveChangesBtn.setFixedWidth(300)
-            self.saveChangesBtn.clicked.connect(self.saveChanges)
-            hLayout.addWidget(self.saveChangesBtn)
-
-        self.closeBtn = QtWidgets.QPushButton(self, text='CLOSE')
-        self.closeBtn.setFixedWidth(300)
-        self.closeBtn.clicked.connect(self.close)
-        hLayout.addWidget(self.closeBtn)
-        return hLayout
-
-    def createDataTableWidget(self):
-        self.dataTableWidget = QtWidgets.QTableWidget(self)
-        self.dataTableWidget.verticalHeader().hide()
-        self.dataTableWidget.setColumnCount(self.totalCol)
-        self.dataTableWidget.setHorizontalHeaderLabels(self.columnHeaders)
-        header = self.dataTableWidget.horizontalHeader()
-        header.setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
-        for i in self.resizeContentColumns:
-            header.setSectionResizeMode(
-                i, QtWidgets.QHeaderView.ResizeToContents)
-
-        if self.needDataTypeChoice:
-            self.dataType = self.dataTypeCombobox.currentText()
-        self.updateDataTableWidgetItems()
-
-    def updateDataTableWidgetItems(self):
-        self.dataList = self.getDataList()
-        rowCount = len(self.dataList)
-        rowCount = 1 if rowCount == 0 else rowCount
-        self.dataTableWidget.setRowCount(rowCount)
-        for i in range(len(self.dataList)):
-            fk = self.checkDataForeignKey(self.dataList[i][0])
-            self.addRow(i, fk)
-        if len(self.dataList) == 0:
-            """
-            No Row, should leave 1 empty row
-            """
-            self.clearFirstRow()
-        self.update()
-
-    def addNewRow(self):
-        rowPosition = self.dataTableWidget.rowCount()
-        self.dataTableWidget.insertRow(rowPosition)
-        self.addRow(rowPosition)
-        self.dataTableWidget.scrollToBottom()
-        self.dataTableWidget.repaint()  # to show row's header
-        self.dataTableWidget.cellWidget(rowPosition, 1).setFocus()
-
-    def removeRow(self, removeRowidx):
-        self.dataTableWidget.removeRow(removeRowidx)
-        for i in range(removeRowidx, self.dataTableWidget.rowCount()):
-            cellWget = self.dataTableWidget.cellWidget(i, 0)
-            cellWget.setText(str(i))
-
-    def cellInputChange(self, changedText, rowidx, colidx):
-        """
-        If cell's value is changed, text's color will be red
-        otherwise, text's color will be black
-        """
-        changed = False
-        if rowidx < len(self.dataList):
-            if changedText != self.dataList[rowidx][colidx - 1]:
-                changed = True
-            cellWget = self.dataTableWidget.cellWidget(rowidx, colidx)
-            setWidgetColor(cellWget, changed=changed)
-
-    def checkDataForeignKey(self, val):
-        if not self.checkFK:
-            return False
-        sql = (f"SELECT {self.colName} FROM channels "
-               f"WHERE {self.colName}='{val}'")
-        paramRows = executeDB(sql)
-        if len(paramRows) > 0:
-            return True
-        else:
-            return False
-
-    def resetRowInputs(self, reset, widgetidx, listidx):
-        for colidx in range(1, self.totalCol):
-            cellWget = self.dataTableWidget.cellWidget(widgetidx, colidx)
-            readOnly = False
-            if hasattr(cellWget, 'isReadOnly'):
-                readOnly = cellWget.isReadOnly()
-            if not readOnly:
-                if reset == 1:
-                    orgVal = self.dataList[listidx][colidx - 1]
-                    if isinstance(cellWget, QtWidgets.QLineEdit):
-                        cellWget.setText(str(orgVal))
-                    elif isinstance(cellWget, QtWidgets.QComboBox):
-                        cellWget.setCurrentText(str(orgVal))
-                    elif isinstance(cellWget, QtWidgets.QSpinBox):
-                        cellWget.setValue(int(orgVal))
-                setWidgetColor(cellWget)
-
-    def addRow(self, rowidx, fk=False):
-        pass
-
-    def dataTypeChanged(self):
-        pass
-
-    def saveChanges(self):
-        try:
-            self.dataTableWidget.focusWidget().clearFocus()
-        except AttributeError:
-            pass
-        self.removeCount = 0
-        self.insertCount = 0
-        self.skipCount = 0
-        rowCount = self.dataTableWidget.rowCount()
-        for i in range(rowCount):
-            widgetidx = i - (rowCount - self.dataTableWidget.rowCount())
-            listidx = i - self.removeCount + self.insertCount - self.skipCount
-            rowInputs = self.getRowInputs(widgetidx)
-            reset = self.updateData(rowInputs, widgetidx, listidx)
-            if reset > -1:
-                self.resetRowInputs(reset, widgetidx, listidx)
-
-    def updateData(self, row, widgetidx, listidx, insertsql, updatesql):
-        """
-        update dataTableWidget and dataList according to the action to
-        add, remove, update
-        :param row: values of processed row in dataTableWidget
-        :param widgetidx: index of the process rows in dataTableWidget
-        :param listidx: index of the process rows in self.dataList
-        :param colName: key db column in the table
-        :param tableName: data table name
-        :param insertsql: query to insert a row to the table
-        :param updatesql: query to update a row in the table
-        :return -1 for doing nothing
-                0 no need to reset input values to org row
-                1 need to reset input values to org row
-        """
-        if listidx < len(self.dataList):
-            orgRow = self.dataList[listidx]
-            if row[0] == "":
-                msg = (f"The {self.colName} of '{orgRow}' at row {widgetidx} "
-                       f"has been changed to blank. Are you sure you want to "
-                       f"delete this row in database?")
-                result = QtWidgets.QMessageBox.question(
-                    self, "Delete %s?" % orgRow, msg,
-                    QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
-                )
-                if result == QtWidgets.QMessageBox.No:
-                    # reset the first widget value and call the function again
-                    # to check other fields
-                    cellWget = self.dataTableWidget.cellWidget(widgetidx, 1)
-                    cellWget.setText(orgRow[0])
-                    row[0] = orgRow[0]
-                    self.updateData(row, widgetidx, listidx)
-                else:
-                    sql = (f"DELETE FROM {self.tableName} "
-                           f"WHERE {self.colName}='{orgRow[0]}'")
-                    executeDB(sql)
-                    self.dataList.remove(orgRow)
-                    self.removeRow(widgetidx)
-                    self.removeCount += 1
-                    return -1
-
-            if row == orgRow:
-                return -1
-            if (row[0] in [p[0] for p in self.dataList] and
-                    self.dataList[listidx][0] != row[0]):
-                msg = (f"The {self.colName} of {orgRow} at row"
-                       f" {widgetidx} has been changed to '{row[0]}' "
-                       f"which already is in the database. "
-                       f"It will be changed back to {orgRow[0]}.")
-                QtWidgets.QMessageBox.information(self, "Error", msg)
-                # reset the first widget value and call the function again
-                # to check other fields
-                cellWget = self.dataTableWidget.cellWidget(widgetidx, 1)
-                cellWget.setText(orgRow[0])
-                row[0] = orgRow[0]
-                self.updateData(row, widgetidx, listidx)
-            else:
-                msg = (f"{orgRow} at row {widgetidx} has "
-                       f"been changed to {row}. Please confirm it.")
-                result = QtWidgets.QMessageBox.question(
-                    self, "Confirmation", msg,
-                    QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel
-                )
-                if result == QtWidgets.QMessageBox.Cancel:
-                    return 1
-                else:
-                    executeDB(updatesql % orgRow[0])
-                    self.dataList[listidx] = row
-                    return 0
-
-        if row[0] == "":
-            msg = (f"Row {widgetidx} has blank {self.colName}. "
-                   f"It will be removed.")
-            QtWidgets.QMessageBox.information(self, "Error", msg)
-            self.removeRow(widgetidx)
-            return -1
-        blankRequestedColumns = [self.requestedColumns[i]
-                                 for i in self.requestedColumns.keys()
-                                 if row[i-1] == ""]
-        if blankRequestedColumns != []:
-            msg = (f"Row {widgetidx}: blank "
-                   f"{', '.join(blankRequestedColumns)} which require some "
-                   f"value(s).")
-            QtWidgets.QMessageBox.information(self, "Error", msg)
-            return -1
-
-        if row[0] in [p[0] for p in self.dataList]:
-            msg = (f"The {self.colName} '{row[0]}' is already in the database."
-                   f" Row {widgetidx} will be removed.")
-            QtWidgets.QMessageBox.information(self, "Error", msg)
-            self.removeRow(widgetidx)
-            return -1
-        executeDB(insertsql)
-        self.dataList.append(row)
-        self.insertCount += 1
-        return 0
diff --git a/sohstationviewer/view/core/plottingWidget.py b/sohstationviewer/view/core/plottingWidget.py
deleted file mode 100755
index 53f735180b138bea851f6d7830d3890366af59b9..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/core/plottingWidget.py
+++ /dev/null
@@ -1,1055 +0,0 @@
-
-from PySide2 import QtCore, QtWidgets
-from matplotlib.backends.backend_qt5agg import (
-    FigureCanvasQTAgg as Canvas)
-from matplotlib import pyplot as pl
-from matplotlib.patches import ConnectionPatch, Rectangle
-from matplotlib.ticker import AutoMinorLocator
-import numpy as np
-
-from sohstationviewer.controller.plottingData import (
-    getTitle, getGaps, getTimeTicks, getUnitBitweight, getMassposValueColors)
-from sohstationviewer.controller.util import displayTrackingInfo, getVal
-
-from sohstationviewer.conf import constants
-from sohstationviewer.conf.colorSettings import Clr, set_colors
-from sohstationviewer.conf.dbSettings import dbConf
-
-from sohstationviewer.database import extractData
-
-from sohstationviewer.model.handling_data import trim_downsample_SOHChan
-
-
-BOTTOM = 0.996
-BOTTOM_PX = 200
-
-
-class PlottingWidget(QtWidgets.QScrollArea):
-    """
-    zorder:
-        axis spines: 0
-        center line: 1
-        lines: 2
-        gap, dots, : 3
-    """
-
-    def __init__(self, parent, trackingBox, name=''):
-        super().__init__()
-        self.name = name
-        self.parent = parent
-        self.trackingBox = trackingBox
-        self.peerPlottingWidgets = [self]
-        self.plotNo = 0
-        self.infoWidget = None
-        self.widgt = QtWidgets.QWidget(parent)
-        self.axes = []
-        self.currplot_title = None
-        self.hidden_plots = {}
-        self.zoomMarker1Shown = False
-        self.axes = []
-
-        self.widthBase = 0.25
-        self.widthBasePx = 1546
-        self.ratioW = 1
-
-        # width of plotting area
-        self.plottingW = self.widthBase
-        # X1: 0.19: Width = 20% of 50in (Figure's width)
-        # X2: 0.19*2: Width = 40% of 50in
-        # X4: 0.19*4: Width = 80% of 50 in
-        # height of plotting area
-        #     + changed when plots are added or removed
-        #     + changed when changing the V-magnify param
-
-        self.plottingBot = BOTTOM
-        # this is the height of a standard plot
-        # plotH = 0.01        # 0.01: Height = 1% of 100in (Figure's height)
-        # left of plotting area: no change
-        self.plottingLBase = 0.04
-        self.plottingLBase = self.plottingLBase
-        self.plottingH = 0.996
-        # this is the height of a standard plot
-        # plotH = 0.01        # 0.01: Height = 1% of 100in (Figure's height)
-        # left of plotting area: no change
-        self.plottingL = 0.03
-        # bottom of plot gap, where we start to draw data
-        # self.plotGapB = 0.990
-        # distance from left axis to start of label
-        self.labelPad = 100
-        self.fontSize = 7
-
-        """
-        Set Figure size 50in width, 100in height.
-        This is the maximum size of plotting container.
-        add_axes will draw proportion to this size.
-        The actual view for user based on size of self.widgt.
-        """
-        self.fig = pl.Figure(facecolor='white', figsize=(50, 100))
-        self.fig.canvas.mpl_connect('pick_event', self.on_pick_on_artist)
-        self.fig.canvas.mpl_connect('button_press_event', self.on_pick)
-
-        self.canvas = Canvas(self.fig)
-        self.canvas.setParent(self.widgt)
-        self.setWidget(self.widgt)
-        self.set_colors('B')
-
-    # ============================= EVENT ==============================
-    def resizeEvent(self, event):
-        # print("resizeEvent")
-        geo = self.maximumViewportSize()
-        # print("resize geo:", geo)
-
-        # set view size fit with the scroll's view port size
-        self.widgt.setFixedWidth(geo.width())
-        self.ratioW = geo.width()/self.widthBasePx
-        self.plottingW = self.ratioW * self.widthBase
-        self.plottingL = self.ratioW * self.plottingLBase
-        if self.plotNo == 0:
-            self.widgt.setFixedHeight(geo.height())
-
-        return super(PlottingWidget, self).resizeEvent(event)
-
-    def contextMenuEvent(self, event):
-        if self.axes == []:
-            return
-        contextMenu = QtWidgets.QMenu(self)
-        removePlotAct = contextMenu.addAction(
-            "Remove %s" % self.currplot_title)
-        removePlotAct.setStatusTip("Remove the current Plot")
-        removePlotAct.triggered.connect(self.hide_currplot)
-
-        if self.hidden_plots != {}:
-            showAllPlotAct = contextMenu.addAction("Show All Plots")
-            showAllPlotAct.triggered.connect(self.show_all_hidden_plots)
-        showPlotActs = []
-        for i in sorted(self.hidden_plots.keys()):
-            showPlotActs.append(
-                contextMenu.addAction('Show hidden Plot %s' % i))
-            showPlotActs[-1].triggered.connect(
-                lambda *arg, index=i: self.show_hidden_plot(index))
-        contextMenu.exec_(self.mapToGlobal(event.pos()))
-
-    def getTimestamp(self, event):
-        x, y = event.x, event.y                 # coordinate data
-        inv = self.axes[0].transData.inverted()
-        # convert to timestamp, bc event.xdata is None in the space bw axes
-        xdata = inv.transform((x, y))[0]
-        return xdata
-
-    def zoomBwMarkers(self, xdata):
-        if self.currMinX == xdata:
-            return
-        self.zoomMarker1Shown = False
-        [self.currMinX, self.currMaxX] = sorted([self.currMinX, xdata])
-        self.set_lim()
-        self.zoomMarker1.set_visible(False)
-        self.zoomMarker2.set_visible(False)
-
-    def on_pick(self, event):
-        """
-        xmouse, ymouse = event.mouseevent.xdata, event.mouseevent.ydata
-        # x, y = artist.get_xdata(), artist.get_ydata()
-        # ind = event.ind
-        print('Artist picked:', artist)
-        # print(self.plots)
-        # print('Index:', self.plots.index(event.artist))
-        # print('{} vertices picked'.format(len(ind)))
-        # print('Pick between vertices {} and {}'.format(
-        min(ind), max(ind) + 1))
-        print('x, y of mouse: {:.2f},{:.2f}'.format(xmouse, ymouse))
-        # print('Data point:', x[ind[0]], y[ind[0]])
-                    print("mouseevent:", dir(event.mouseevent))
-            print("guievent:", dir(event.guiEvent))
-            print("modifiers:", event.guiEvent.modifiers())
-        """
-        # print(dir(event))
-        modifiers = event.guiEvent.modifiers()
-        try:
-            # on_pick() take place after on_pick_on_artist where
-            # tps_t was assigned in TimePowerSquareWidget
-            xdata = self.tps_t
-        except AttributeError:
-            xdata = self.getTimestamp(event)
-        for w in self.peerPlottingWidgets:
-            if modifiers == QtCore.Qt.ShiftModifier:
-                w.on_shift_click(xdata)
-            elif modifiers in [QtCore.Qt.ControlModifier,
-                               QtCore.Qt.MetaModifier]:
-                w.on_ctrl_cmd_click(xdata)
-
-            else:
-                if w.zoomMarker1Shown:
-                    w.zoomBwMarkers(xdata)
-            w.draw()
-
-    def on_ctrl_cmd_click(self, xdata):
-        """
-        On ctrl/cmd + left click
-        + hide zoomMarker1
-        + connect ruler to follow mouse
-        + show ruler
-        """
-        self.zoomMarker1.set_visible(False)
-        self.zoomMarker1Shown = False
-        try:
-            self.fig.canvas.mpl_disconnect(self.follower)
-        except AttributeError:
-            pass
-        self.set_ruler_visibled(self.ruler, xdata)
-
-    def on_shift_click(self, xdata):
-        """
-        On shift + left click
-        if there're no zoom marker
-            + hide ruler
-            + show zoomMarker1, zoomMarker2
-            + connect zoomMarker2 to follow mouse
-        else:
-            + zoom data in between 2 zoomMarkers
-
-        """
-        if not self.zoomMarker1Shown:
-            self.ruler.set_visible(False)
-            self.set_ruler_visibled(self.zoomMarker1, xdata)
-            self.currMinX = xdata
-            self.zoomMarker1Shown = True
-            self.set_ruler_visibled(self.zoomMarker2, xdata)
-        else:
-            self.zoomBwMarkers(xdata)
-
-    def on_pick_on_artist(self, event):
-        artist = event.artist
-        if isinstance(artist, pl.Axes):
-            self.currplot = artist
-            self.currplot_index = self.axes.index(artist)
-            self.currplot_title = "Plot %s" % self.currplot_index
-
-    def set_ruler_visibled(self, ruler, x):
-        ruler.set_visible(True)
-        ruler.xy1 = (x, 0)
-        ruler.xy2 = (x, self.bottom)
-        try:
-            if ruler == self.zoomMarker2:
-                # make zoomMarker2 follow mouse.
-                # need to disconnect when state of rulers change
-                self.follower = self.fig.canvas.mpl_connect(
-                    "motion_notify_event", self.zoomMarker2_follow_mouse)
-        except AttributeError:
-            pass
-
-    def zoomMarker2_follow_mouse(self, mouseevent):
-        xdata = self.getTimestamp(mouseevent)
-        self.zoomMarker2.xy1 = (xdata, 0)
-        self.zoomMarker2.xy2 = (xdata, self.bottom)
-        self.draw()
-
-    def keyPressEvent(self, event):
-        if event.key() == QtCore.Qt.Key_Escape:
-            for w in self.peerPlottingWidgets:
-                w.set_rulers_invisible()
-                w.zoomMarker1Shown = False
-                w.draw()
-        return super(PlottingWidget, self).keyPressEvent(event)
-
-    def set_rulers_invisible(self):
-        self.ruler.set_visible(False)
-        self.zoomMarker1.set_visible(False)
-        self.zoomMarker2.set_visible(False)
-
-    # ============================= END EVENT ==============================
-
-    def add_timestamp_bar(self, usedHeight, top=True, hasLabel=True):
-        """
-        set the axes to display timestampBar on top of the plotting area
-        setting axis off to not display at the begining
-        Color in qpeek is DClr[T0]
-        :return:
-        """
-        self.plottingBot -= usedHeight
-        timestampBar = self.canvas.figure.add_axes(
-            [self.plottingL, self.plottingBot, self.plottingW, 0.00005],
-        )
-        timestampBar.axis('off')
-        timestampBar.xaxis.set_minor_locator(AutoMinorLocator())
-        timestampBar.spines['bottom'].set_color(self.displayColor['basic'])
-        timestampBar.spines['top'].set_color(self.displayColor['basic'])
-
-        if top:
-            labelbottom = False
-        else:
-            labelbottom = True
-        timestampBar.tick_params(which='major', length=7, width=2,
-                                 direction='inout',
-                                 colors=self.displayColor['basic'],
-                                 labelbottom=labelbottom,
-                                 labeltop=not labelbottom)
-        timestampBar.tick_params(which='minor', length=4, width=1,
-                                 direction='inout',
-                                 colors=self.displayColor['basic'])
-        if hasLabel:
-            timestampBar.set_ylabel('Hours',
-                                    fontweight='bold',
-                                    fontsize=self.fontSize,
-                                    rotation=0,
-                                    labelpad=self.labelPad,
-                                    ha='left',
-                                    color=self.displayColor['basic'])
-
-        # self.update_timestamp_bar(timestampBar)
-        return timestampBar
-
-    def update_timestamp_bar(self, timestampBar):
-        times, majorTimes, majorTimeLabels = getTimeTicks(
-            self.currMinX, self.currMaxX, self.dateMode, self.timeTicksTotal)
-        timestampBar.axis('on')
-        timestampBar.set_yticks([])
-        timestampBar.set_xticks(times, minor=True)
-        timestampBar.set_xticks(majorTimes)
-        timestampBar.set_xticklabels(majorTimeLabels,
-                                     fontsize=self.fontSize+2)
-        timestampBar.set_xlim(self.currMinX, self.currMaxX)
-
-    def create_axes(self, plotB, plotH, hasMinMaxLines=True):
-        ax = self.canvas.figure.add_axes(
-            [self.plottingL, plotB, self.plottingW, plotH],
-            picker=True
-        )
-
-        ax.spines['right'].set_visible(False)
-        ax.spines['left'].set_visible(False)
-        if hasMinMaxLines:
-            ax.spines['top'].set_zorder(0)
-            ax.spines['bottom'].set_zorder(0)
-            ax.spines['top'].set_color(self.displayColor['sub_basic'])
-            ax.spines['bottom'].set_color(self.displayColor['sub_basic'])
-
-        ax.set_yticks([])
-        ax.set_xticks([])
-        ax.tick_params(colors=self.displayColor['basic'],
-                       width=0,
-                       pad=-2,
-                       labelsize=self.fontSize)
-        ax.patch.set_alpha(0)
-        return ax
-
-    def setAxesInfo(self, ax, sampleNoList, sampleNoClrList=None,
-                    label=None, info='', y=None, chanDB=None, linkedAx=None):
-        if label is None:
-            label = chanDB['label']
-        titleVerAlignment = 'center'
-        # set info undertitle
-        if linkedAx is not None:
-            info = label
-        if info != '':
-            ax.text(
-                -0.15, 0.2,
-                info,
-                horizontalalignment='left',
-                verticalalignment='top',
-                rotation='horizontal',
-                transform=ax.transAxes,
-                color=self.displayColor['sub_basic'],
-                size=self.fontSize
-            )
-            titleVerAlignment = 'top'
-
-        if linkedAx is None:
-            # set title on left side
-            color = self.displayColor['plot_label']
-            if label.startswith("DEFAULT"):
-                color = self.displayColor["warning"]
-            ax.text(
-                -0.15, 0.6,
-                label,
-                horizontalalignment='left',
-                verticalalignment=titleVerAlignment,
-                rotation='horizontal',
-                transform=ax.transAxes,
-                color=color,
-                size=self.fontSize + 2
-            )
-
-        # set samples' total on right side
-        # if sampleNoClrList is None:
-        #     sampleNoClrList = len(sampleNoList) * ['W']
-        if len(sampleNoList) == 1:
-            ax.sampleLbl = ax.text(
-                1.005, 0.5,
-                sampleNoList[0],
-                horizontalalignment='left',
-                verticalalignment='center',
-                rotation='horizontal',
-                transform=ax.transAxes,
-                # color=Clr[sampleNoClrList[0]],
-                color=self.displayColor['basic'],
-                size=self.fontSize
-            )
-        else:
-            # bottom
-            ax.sampleLbl = ax.text(
-                1.005, 0.25,
-                sampleNoList[0],
-                horizontalalignment='left',
-                verticalalignment='center',
-                rotation='horizontal',
-                transform=ax.transAxes,
-                # color=Clr[sampleNoClrList[0]],
-                color=self.displayColor['basic'],
-                size=self.fontSize
-            )
-            # top
-            ax.sampleLbl = ax.text(
-                1.005, 0.75,
-                sampleNoList[1],
-                horizontalalignment='left',
-                verticalalignment='center',
-                rotation='horizontal',
-                transform=ax.transAxes,
-                # color=Clr[sampleNoClrList[0]],
-                color=self.displayColor['basic'],
-                size=self.fontSize
-            )
-
-        if y is None:
-            # draw center line
-            ax.plot([self.currMinX, self.currMaxX],
-                    [0, 0],
-                    color=self.displayColor['sub_basic'],
-                    linewidth=0.5,
-                    zorder=1
-                    )
-            ax.spines['top'].set_visible(False)
-            ax.spines['bottom'].set_visible(False)
-        else:
-            try:
-                minY = y.min()
-                maxY = y.max()
-                ax.spines['top'].set_visible(True)
-                ax.spines['bottom'].set_visible(True)
-                ax.unit_bw = getUnitBitweight(chanDB, self.parent.bitweightOpt)
-                self.setAxesYlim(ax, minY, maxY)
-            except Exception:
-                pass
-
-    def setAxesYlim(self, ax, minY, maxY):
-        minY = round(minY, 7)
-        maxY = round(maxY, 7)
-        if maxY > minY:
-            ax.set_yticks([minY, maxY])
-            ax.set_yticklabels(
-                [ax.unit_bw.format(minY), ax.unit_bw.format(maxY)])
-        if minY == maxY:
-            maxY += 1
-            ax.set_yticks([minY])
-            ax.set_yticklabels([ax.unit_bw.format(minY)])
-        ax.set_ylim(minY, maxY)
-
-    def addGapBar(self, gaps):
-        """
-        set the axes to display gapBar on top of the plotting area
-        setting axis off to not display at the begining
-        :return:
-        """
-        if self.parent.minGap is None:
-            return
-        self.gaps = getGaps(gaps, float(self.parent.minGap))
-        self.plottingBot -= 0.003
-        self.gapBar = self.create_axes(self.plottingBot,
-                                       0.001,
-                                       hasMinMaxLines=False)
-        self.updateGapBar()
-
-    def updateGapBar(self):
-        gapLabel = "%sm" % self.parent.minGap
-        # TODO: calculate gap limit
-        # self.gaps = []
-        h = 0.001  # height of rectangle represent gap
-        self.setAxesInfo(self.gapBar, [len(self.gaps)],
-                         label=gapLabel)
-        # draw gaps
-        for i in range(len(self.gaps)):
-            x = self.gaps[i][0]
-            w = self.gaps[i][1] - self.gaps[i][
-                0]  # width of rectangle represent gap
-            self.gapBar.add_patch(Rectangle((x, - h / 2), w, h,
-                                            color='r',
-                                            picker=True,
-                                            lw=0.,
-                                            zorder=3))  # on top of center line
-
-    def get_height(self, ratio, bwPlotsDistance=0.0015):
-        plotH = 0.0012 * ratio  # ratio with figure height
-        self.plottingBot -= plotH + bwPlotsDistance
-        self.plottingBotPixel += 19 * ratio
-        return plotH
-
-    # -------------------- Different color dots ----------------------- #
-    def plotNone(self):
-        """
-        plot with nothing needed to show rulers
-        """
-        plotH = 0.00001
-        bwPlotsDistance = 0.0001
-        self.plottingBot -= plotH + bwPlotsDistance
-        ax = self.create_axes(self.plottingBot, plotH, hasMinMaxLines=False)
-        ax.x = None
-        ax.plot([0], [0], linestyle="")
-        return ax
-
-    def plotMultiColorDots(self, cData, chanDB, chan, ax, linkedAx):
-        """
-        plot scattered dots with colors defined by valueColors:
-          *:W or -1:_|0:R|2.3:Y|+2.3:G
-           with colors: RYGMC in dbSettings.py
-           _: not plot
-        :param data: data of the channel which is list of (time, value)
-        :param chanDB: info of channel from DB
-        :return:
-        """
-        if linkedAx is not None:
-            ax = linkedAx
-        if ax is None:
-            plotH = self.get_height(chanDB['height'])
-            ax = self.create_axes(
-                self.plottingBot, plotH, hasMinMaxLines=False)
-
-        x = []
-        prevVal = -constants.HIGHEST_INT
-
-        if chanDB['valueColors'] in [None, 'None', '']:
-            chanDB['valueColors'] = '*:W'
-        valueColors = chanDB['valueColors'].split('|')
-        for vc in valueColors:
-            v, c = vc.split(':')
-            val = getVal(v)
-            if c == '_':
-                prevVal = val
-                continue
-
-            if v.startswith('+'):
-                points = [cData['times'][i]
-                          for i in range(len(cData['data']))
-                          if cData['data'][i] > val]
-            elif v == '*':
-                points = cData['times']
-            else:
-                points = [cData['times'][i]
-                          for i in range(len(cData['data']))
-                          if prevVal < cData['data'][i] <= val]
-            x += points
-
-            ax.plot(points, len(points) * [0], linestyle="",
-                    marker='s', markersize=0.5, zorder=3,
-                    color=Clr[c], picker=True, pickradius=3)
-            prevVal = val
-
-        totalSamples = len(x)
-
-        x = sorted(x)
-        self.setAxesInfo(ax, [totalSamples], chanDB=chanDB, linkedAx=linkedAx)
-        if linkedAx is None:
-            ax.x = x
-        else:
-            ax.linkedX = x
-        return ax
-
-    # def plotDotsMasspos(self, cData, chanDB, chan, linkedAx):
-    #     valueColors = getMassposValueColors(
-    #         self.parent.massPosVoltRangeOpt, chan, self.cMode, self.errors)
-    #     if valueColors is None:
-    #         return
-    #     chanDB['valueColors'] = valueColors
-    #     return self.plotMultiColorDots(cData, chanDB, chan, linkedAx)
-
-    # ---------------------------- up/down dots ---------------------------- #
-    def plotUpDownDots(self, cData, chanDB, chan, ax, linkedAx):
-        """
-        data with 2 different values defined in valueColors
-        """
-        if linkedAx is not None:
-            ax = linkedAx
-        if ax is None:
-            plotH = self.get_height(chanDB['height'])
-            ax = self.create_axes(
-                self.plottingBot, plotH, hasMinMaxLines=False)
-
-        valCols = chanDB['valueColors'].split('|')
-        pointsList = []
-        colors = []
-        for vc in valCols:
-            v, c = vc.split(':')
-            val = getVal(v)
-
-            points = [cData['times'][i]
-                      for i in range(len(cData['data']))
-                      if cData['data'][i] == val]
-            pointsList.append(points)
-            colors.append(c)
-
-        # down dots
-        ax.plot(pointsList[0], len(pointsList[0]) * [-0.5], linestyle="",
-                marker='s', markersize=2, zorder=3,
-                color=Clr[colors[0]], picker=True, pickradius=3)
-        # up dots
-        ax.plot(pointsList[1], len(pointsList[1]) * [0.5], linestyle="",
-                marker='s', markersize=2, zorder=3,
-                color=Clr[colors[1]], picker=True, pickradius=3)
-        x = pointsList[0] + pointsList[1]
-        x = sorted(x)
-        ax.set_ylim(-2, 2)
-        self.setAxesInfo(ax, [len(pointsList[0]), len(pointsList[1])],
-                         sampleNoClrList=colors,
-                         chanDB=chanDB,
-                         linkedAx=linkedAx)
-        if linkedAx is None:
-            ax.x = x
-        else:
-            ax.linkedX = x
-        return ax
-
-    # ----------------------- dots for times, ignore data------------------- #
-    def plotTimeDots(self, cData, chanDB, chan, ax, linkedAx):
-        if linkedAx is not None:
-            ax = linkedAx
-        if ax is None:
-            plotH = self.get_height(chanDB['height'])
-            ax = self.create_axes(self.plottingBot, plotH)
-
-        color = 'W'
-        if chanDB['valueColors'] not in [None, 'None', '']:
-            color = chanDB['valueColors'].strip()
-        x = cData['times']
-        self.setAxesInfo(ax, [len(x)], chanDB=chanDB, linkedAx=linkedAx)
-
-        ax.myPlot = ax.plot(x, [0]*len(x), marker='s', markersize=1.5,
-                            linestyle='', zorder=2,
-                            color=Clr[color], picker=True,
-                            pickradius=3)
-        if linkedAx is None:
-            ax.x = x
-        else:
-            ax.linkedX = x
-        return ax
-
-    # ----------------------- lines - one color dots ----------------------- #
-    def plotLinesDots(self, cData, chanDB, chan, ax, linkedAx, info=''):
-        """ L:G|D:W """
-        if linkedAx is not None:
-            ax = linkedAx
-        if ax is None:
-            plotH = self.get_height(chanDB['height'])
-            ax = self.create_axes(self.plottingBot, plotH)
-
-        x, y = cData['times'], cData['data']
-        self.setAxesInfo(ax, [len(x)], chanDB=chanDB,
-                         info=info, y=y, linkedAx=linkedAx)
-        colors = {}
-        if chanDB['valueColors'] not in [None, 'None', '']:
-            colorParts = chanDB['valueColors'].split('|')
-            for cStr in colorParts:
-                obj, c = cStr.split(':')
-                colors[obj] = c
-
-        lColor = 'G'
-        hasDot = False
-        if 'L' in colors:
-            lColor = colors['L']
-        if 'D' in colors:
-            dColor = colors['D']
-            hasDot = True
-
-        if not hasDot:
-            ax.myPlot = ax.plot(x, y,
-                                linestyle='-', linewidth=0.7,
-                                color=Clr[lColor])
-        else:
-            ax.myPlot = ax.plot(x, y, marker='s', markersize=1.5,
-                                linestyle='-', linewidth=0.7, zorder=2,
-                                color=Clr[lColor],
-                                markerfacecolor=Clr[dColor],
-                                picker=True, pickradius=3)
-        if linkedAx is None:
-            ax.x = x
-            ax.y = y
-        else:
-            ax.linkedX = x
-            ax.linkedY = y
-        return ax
-
-    def plotLinesSRate(self, cData, chanDB, chan, ax, linkedAx):
-        """
-        multi-line line seismic, one color, line only,
-            can apply bit weights in (get_unit_bitweight())
-        """
-        if cData['samplerate'] >= 1.0:
-            info = "%dsps" % cData['samplerate']
-        else:
-            info = "%gsps" % cData['samplerate']
-        return self.plotLinesDots(cData, chanDB, chan, ax, linkedAx, info=info)
-
-    # ----------------------- lines - multi-color dots --------------------- #
-    def plotLinesMasspos(self, cData, chanDB, chan, ax, linkedAx):
-        valueColors = getMassposValueColors(
-            self.parent.massPosVoltRangeOpt, chan,
-            self.cMode, self.errors, retType='tupleList')
-
-        if valueColors is None:
-            return
-
-        if ax is None:
-            plotH = self.get_height(chanDB['height'])
-            ax = self.create_axes(self.plottingBot, plotH)
-
-        ax.x, ax.y = cData['times'], cData['data']
-        self.setAxesInfo(ax, [len(ax.x)], chanDB=chanDB, y=ax.y)
-        ax.myPlot = ax.plot(ax.x, ax.y,
-                            linestyle='-', linewidth=0.7,
-                            color=self.displayColor['sub_basic'],
-                            zorder=2)[0]
-        colors = [None] * len(ax.y)
-        sizes = [0.5] * len(ax.y)
-        for i in range(len(ax.y)):
-            count = 0
-            prevV = 0
-            for v, c in valueColors:
-                if count < (len(valueColors) - 1):
-                    if prevV < abs(ax.y[i]) <= v:
-                        colors[i] = Clr[c]
-                        break
-                else:
-                    # if abs(ax.y[i]) > v:
-                    colors[i] = Clr[c]
-                    break
-                prevV = v
-                count += 1
-        ax.scatter(ax.x, ax.y, marker='s', c=colors, s=sizes, zorder=3)
-        return ax
-    # ---------------------------------------------------------#
-
-    def add_ruler(self, color):
-        ruler = ConnectionPatch(
-            xyA=(0, 0),
-            xyB=(0, self.bottom),
-            coordsA="data",
-            coordsB="data",
-            axesA=self.timestampBarTop,
-            axesB=self.timestampBarBottom,
-            color=color,
-        )
-        ruler.set_visible(False)
-        self.timestampBarBottom.add_artist(ruler)
-        return ruler
-
-    def set_lim(self, orgSize=False):
-        if not orgSize:
-            for chanID in self.plottingData1:
-                cData = self.plottingData1[chanID]
-                self.getZoomData(cData, chanID)
-            for chanID in self.plottingData2:
-                cData = self.plottingData2[chanID]
-                self.getZoomData(cData, chanID)
-        self.update_timestamp_bar(self.timestampBarTop)
-        self.update_timestamp_bar(self.timestampBarBottom)
-        if hasattr(self, 'gapBar'):
-            self.gapBar.set_xlim(self.currMinX, self.currMaxX)
-            if not orgSize:
-                newGaps = [g for g in self.gaps
-                           if (self.currMinX <= g[0] <= self.currMaxX
-                               or self.currMinX <= g[1] <= self.currMaxX)]
-
-                # reset total of samples on the right
-                self.gapBar.sampleLbl.set_text(len(newGaps))
-        for ax in self.axes:
-            ax.set_xlim(self.currMinX, self.currMaxX)
-            if ax.x is None:
-                # the plotNone bar is at the end, no need to process
-                break
-            if not orgSize:
-                # x, y
-                newXIndexes = np.where((ax.x >= self.currMinX) &
-                                       (ax.x <= self.currMaxX))
-
-                # reset total of samples on the right
-                ax.sampleLbl.set_text(len(newXIndexes))
-
-                if len(newXIndexes) == 0:
-                    continue
-                if hasattr(ax, 'y'):
-                    # don't need to reset y range if ax.y not exist
-                    newX = ax.x[newXIndexes]
-                    newMinX = min(newX)
-                    newMaxX = max(newX)
-                    try:
-                        newMinXIndex = ax.x.index(newMinX)
-                        newMaxXIndex = ax.x.index(newMaxX)
-                    except AttributeError:
-                        newMinXIndex = np.where(ax.x == newMinX)[0][0]
-                        newMaxXIndex = np.where(ax.x == newMaxX)[0][0]
-                    newY = ax.y[newMinXIndex:newMaxXIndex + 1]
-                    newMinY = min(newY)
-                    newMaxY = max(newY)
-                    self.setAxesYlim(ax, newMinY, newMaxY)
-
-    def set_title(self, title, y=100, valight='top'):
-        self.fig.text(-0.15, y, title,
-                      verticalalignment=valight,
-                      horizontalalignment='left',
-                      transform=self.timestampBarTop.transAxes,
-                      color=self.displayColor['basic'],
-                      size=self.fontSize)
-
-    def draw(self):
-        try:
-            self.canvas.draw()
-            # a bug on mac:
-            # not showing updated info until clicking on another window
-            # fix by calling repaint()
-            self.widgt.repaint()
-        except TypeError:
-            pass
-
-    # ######## Functions for outside world #####
-    def init_size(self):
-        geo = self.maximumViewportSize()
-        if self.plotNo == 0:
-            # set view size fit with the scroll's view port size
-            self.widgt.setFixedWidth(geo.width())
-            self.widgt.setFixedHeight(geo.height())
-
-    def set_msg_widget(self, msgWidget):
-        self.msgWidget = msgWidget
-
-    def set_background_color(self, color='black'):
-        self.fig.patch.set_facecolor(color)
-        self.draw()
-
-    def resetView(self):
-        """
-        reset all zooms back to the first plotting
-        """
-        if self.axes == []:
-            return
-        self.currMinX = self.minX
-        self.currMaxX = self.maxX
-        self.set_lim()
-        self.draw()
-
-    def clear(self):
-        if self.zoomMarker1.get_visible():
-            self.zoomMarker1.set_visible(False)
-        else:
-            self.zoomMarker1.set_visible(True)
-        # self.fig.clear()
-        # self.axes = []
-        self.draw()
-
-    def applyConvertFactor(self, cData, convertFactor):
-        """
-        convertFactor = 150mV/count = 150V/1000count
-        => unit data * convertFactor= data *150/1000 V
-        """
-        cData['data'] = np.multiply(cData['data'], [convertFactor])
-
-    # =============================== axes ================================
-    def plot_channels(self, startTm, endTm, key, dataTime,
-                      gaps, channelList, timeTicksTotal,
-                      plottingData1, plottingData2):
-        """
-        :param key: staID for mseed, (device, expNo) for reftek
-        :param plottingData: a ditionary including:
-            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
-              channels:{cha: {netID, statID, locID, chanID, times, data,
-                              samplerate, startTmEpoch, endTmEpoch}       #
-              earliestUTC: the earliest time of all channels
-              latestUTC: the latest time of all channels
-        :timeTicksTotal: max number of tick to show on time bar
-
-        Data set: {channelname: [(x,y), (x,y)...]
-        """
-        self.plottingData1 = plottingData1
-        self.plottingData2 = plottingData2
-        self.processingLog = []     # [(message, type)]
-        self.errors = []
-        if self.axes != []:
-            self.fig.clear()
-        self.dateMode = self.parent.dateFormat.upper()
-        self.timeTicksTotal = timeTicksTotal
-
-        self.minX = self.currMinX = max(dataTime[0], startTm)
-        self.maxX = self.currMaxX = min(dataTime[1], endTm)
-        self.plotNo = len(self.plottingData1) + len(self.plottingData2)
-        title = getTitle(key, self.minX, self.maxX, self.dateMode)
-        self.plottingBot = BOTTOM
-        self.plottingBotPixel = BOTTOM_PX
-        self.axes = []
-
-        self.timestampBarTop = self.add_timestamp_bar(0.003)
-        self.set_title(title)
-        self.addGapBar(gaps)
-        notFoundChan = [c for c in channelList
-                        if c not in self.plottingData1.keys()]
-        if len(notFoundChan) > 0:
-            msg = (f"The following channels is in Channel Preferences but "
-                   f"not in the given data: {notFoundChan}")
-            self.processingLog.append((msg, 'warning'))
-
-        for chanID in self.plottingData1:
-            chanDB = extractData.getChanPlotInfo(chanID, self.parent.dataType)
-            if chanDB['height'] == 0:
-                # not draw
-                continue
-            if chanDB['channel'] == 'DEFAULT':
-                msg = (f"Channel {chanID}'s "
-                       f"definition can't be found database.")
-                displayTrackingInfo(self.trackingBox, msg, 'warning')
-
-            if chanDB['plotType'] == '':
-                continue
-            self.plottingData1[chanID]['chanDB'] = chanDB
-            self.getZoomData(self.plottingData1[chanID], chanID,
-                             self.plottingData1, True)
-
-        for chanID in self.plottingData2:
-            chanDB = extractData.getChanPlotInfo(chanID, self.parent.dataType)
-            self.plottingData2[chanID]['chanDB'] = chanDB
-            self.getZoomData(self.plottingData2[chanID], chanID, True)
-
-        self.axes.append(self.plotNone())
-        self.timestampBarBottom = self.add_timestamp_bar(0.003, top=False)
-        self.set_lim(orgSize=True)
-        self.bottom = self.axes[-1].get_ybound()[0]
-        self.ruler = self.add_ruler(self.displayColor['time_ruler'])
-        self.zoomMarker1 = self.add_ruler(self.displayColor['zoom_marker'])
-        self.zoomMarker2 = self.add_ruler(self.displayColor['zoom_marker'])
-        # Set view size fit with the given data
-        if self.widgt.geometry().height() < self.plottingBotPixel:
-            self.widgt.setFixedHeight(self.plottingBotPixel)
-
-        self.draw()
-
-    def getZoomData(self, cData, chanID, plottingData=None, firsttime=False):
-        """
-        :param setID: (netID, statID, locID)
-        :param plottingData: a ditionary including:
-            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
-              channels:{cha: {netID, statID, locID, chanID, times, data,
-                              samplerate, startTmEpoch, endTmEpoch}       #
-              earliestUTC: the earliest time of all channels
-              latestUTC: the latest time of all channels
-        :timeTicksTotal: max number of tick to show on time bar
-
-        Data set: {channelname: [(x,y), (x,y)...]
-        """
-        chanDB = cData['chanDB']
-        plotType = chanDB['plotType']
-        trim_downsample_SOHChan(cData, self.currMinX, self.currMaxX, firsttime)
-        self.applyConvertFactor(cData, 1)
-        if 'ax' not in cData:
-            linkedAx = None
-            if chanDB['linkedChan'] not in [None, 'None', '']:
-                try:
-                    linkedAx = plottingData['channels'][
-                        chanDB['linkedChan']]['ax']
-                except KeyError:
-                    pass
-            ax = getattr(self, dbConf['plotFunc'][plotType][1])(
-                cData, chanDB, chanID, None, linkedAx)
-            if ax is None:
-                return
-            cData['ax'] = ax
-            ax.chan = chanID
-            self.axes.append(ax)
-        else:
-            getattr(self, dbConf['plotFunc'][plotType][1])(
-                cData, chanDB, chanID, cData['ax'], None)
-
-    def hide_plots(self, plot_indexes):
-        if self.axes == []:
-            return
-        plot_indexes = sorted(plot_indexes)
-        idx = 0
-        total_h = 0
-        for i in range(plot_indexes[0], len(self.axes)):
-            pos = self.axes[i].get_position()
-            pos.y0 += total_h
-            pos.y1 += total_h
-            if idx < len(plot_indexes) and i == plot_indexes[idx]:
-                h = pos.y1 - pos.y0
-                total_h += h
-                pos.y0 = pos.y1
-                idx += 1
-                self.hidden_plots[i] = h
-            self.axes[i].set_position(pos)
-
-        # currently consider every plot height are all 100px height
-        height = self.widgt.geometry().height() - 100 * len(plot_indexes)
-        self.widgt.setFixedHeight(height)
-        self.draw()
-
-    def hide_currplot(self):
-        pos = self.currplot.get_position()
-        h = pos.y1 - pos.y0
-        pos.y0 = pos.y1
-        self.currplot.set_position(pos)
-        for i in range(self.currplot_index + 1, len(self.axes)):
-            pos = self.axes[i].get_position()
-            pos.y0 += h
-            pos.y1 += h
-            self.axes[i].set_position(pos)
-        # currently consider every plot height are all 100px height
-        height = self.widgt.geometry().height() - 100
-        self.widgt.setFixedHeight(height)
-        self.hidden_plots[self.currplot_index] = h
-        self.draw()
-
-    def show_hidden_plot(self, index):
-        h = self.hidden_plots[index]
-        workplot = self.axes[index]
-        pos = workplot.get_position()
-        pos.y1 = pos.y0 + h
-        workplot.set_position(pos)
-        for i in range(index, len(self.axes)):
-            pos = self.axes[i].get_position()
-            pos.y0 -= h
-            pos.y1 -= h
-            self.axes[i].set_position(pos)
-        # currently consider every plot height are all 100px height
-        height = self.widgt.geometry().height() + 100
-        self.widgt.setFixedHeight(height)
-        del self.hidden_plots[index]
-        pos = workplot.get_position()
-        self.draw()
-
-    def show_all_hidden_plots(self):
-        plot_indexes = sorted(self.hidden_plots.keys())
-        idx = 0
-        total_h = 0
-        for i in range(plot_indexes[0], len(self.axes)):
-            pos = self.axes[i].get_position()
-            if idx < len(plot_indexes) and i == plot_indexes[idx]:
-                h = self.hidden_plots[i]
-                total_h += h
-                pos.y1 = pos.y0 + h
-                idx += 1
-                del self.hidden_plots[i]
-            pos.y0 -= total_h
-            pos.y1 -= total_h
-            self.axes[i].set_position(pos)
-
-        # currently consider every plot height are all 100px height
-        height = self.widgt.geometry().height() + 100 * len(plot_indexes)
-        self.widgt.setFixedHeight(height)
-        self.draw()
-
-    def rePlot(self):
-        for ax in self.axes:
-            print("pos", self.ax.get_position())
-
-    # ============================= END AXES ==============================
-    def set_colors(self, mode):
-        self.cMode = mode
-        self.displayColor = set_colors(mode)
-        self.fig.patch.set_facecolor(self.displayColor['background'])
-
-    def setPeerPlottingWidgets(self, widgets):
-        self.peerPlottingWidgets = widgets
diff --git a/sohstationviewer/view/core/plotting_widget.py b/sohstationviewer/view/core/plotting_widget.py
new file mode 100755
index 0000000000000000000000000000000000000000..e4452faf812061a47153083b923353827d6a7748
--- /dev/null
+++ b/sohstationviewer/view/core/plotting_widget.py
@@ -0,0 +1,1066 @@
+from PySide2 import QtCore, QtWidgets
+from matplotlib.backends.backend_qt5agg import (
+    FigureCanvasQTAgg as Canvas)
+from matplotlib import pyplot as pl
+from matplotlib.patches import ConnectionPatch, Rectangle
+from matplotlib.ticker import AutoMinorLocator
+import numpy as np
+
+from sohstationviewer.controller.plottingData import (
+    getTitle, getGaps, getTimeTicks, getUnitBitweight, getMassposValueColors)
+from sohstationviewer.controller.util import displayTrackingInfo, getVal
+
+from sohstationviewer.conf import constants
+from sohstationviewer.conf.colorSettings import Clr, set_colors
+from sohstationviewer.conf.dbSettings import dbConf
+
+from sohstationviewer.database import extractData
+
+from sohstationviewer.model.handling_data import trim_downsample_SOHChan
+
+BOTTOM = 0.996
+BOTTOM_PX = 200
+
+
+class PlottingWidget(QtWidgets.QScrollArea):
+    """
+    zorder:
+        axis spines: 0
+        center line: 1
+        lines: 2
+        gap, dots, : 3
+    """
+
+    def __init__(self, parent, tracking_box, name=''):
+        super().__init__()
+        self.name = name
+        self.parent = parent
+        self.tracking_box = tracking_box
+        self.peer_plotting_widgets = [self]
+        self.plot_no = 0
+        self.info_widget = None
+        self.widgt = QtWidgets.QWidget(parent)
+        self.axes = []
+        self.curr_plot_title = None
+        self.hidden_plots = {}
+        self.zoom_marker1_shown = False
+        self.axes = []
+
+        self.width_base = 0.25
+        self.width_base_px = 1546
+        self.ratio_w = 1
+
+        # width of plotting area
+        self.plotting_w = self.width_base
+        # X1: 0.19: Width = 20% of 50in (Figure's width)
+        # X2: 0.19*2: Width = 40% of 50in
+        # X4: 0.19*4: Width = 80% of 50 in
+        # height of plotting area
+        #     + changed when plots are added or removed
+        #     + changed when changing the V-magnify param
+
+        self.plotting_bot = BOTTOM
+        # this is the height of a standard plot
+        # plot_h = 0.01        # 0.01: Height = 1% of 100in (Figure's height)
+        # left of plotting area: no change
+        self.plotting_l_base = 0.04
+        self.plotting_l_base = self.plotting_l_base
+        self.plotting_h = 0.996
+        # this is the height of a standard plot
+        # plot_h = 0.01        # 0.01: Height = 1% of 100in (Figure's height)
+        # left of plotting area: no change
+        self.plotting_l = 0.03
+        # bottom of plot gap, where we start to draw data
+        # self.plotGapB = 0.990
+        # distance from left axis to start of label
+        self.label_pad = 100
+        self.font_size = 7
+
+        """
+        Set Figure size 50in width, 100in height.
+        This is the maximum size of plotting container.
+        add_axes will draw proportion to this size.
+        The actual view for user based on size of self.widgt.
+        """
+        self.fig = pl.Figure(facecolor='white', figsize=(50, 100))
+        self.fig.canvas.mpl_connect('pick_event', self.on_pick_on_artist)
+        self.fig.canvas.mpl_connect('button_press_event', self.on_pick)
+
+        self.canvas = Canvas(self.fig)
+        self.canvas.setParent(self.widgt)
+        self.setWidget(self.widgt)
+        self.set_colors('B')
+
+    # ============================= EVENT ==============================
+    def resizeEvent(self, event):
+        # print("resizeEvent")
+        geo = self.maximumViewportSize()
+        # print("resize geo:", geo)
+
+        # set view size fit with the scroll's view port size
+        self.widgt.setFixedWidth(geo.width())
+        self.ratio_w = geo.width() / self.width_base_px
+        self.plotting_w = self.ratio_w * self.width_base
+        self.plotting_l = self.ratio_w * self.plotting_l_base
+        if self.plot_no == 0:
+            self.widgt.setFixedHeight(geo.height())
+
+        return super(PlottingWidget, self).resizeEvent(event)
+
+    def contextMenuEvent(self, event):
+        if self.axes == []:
+            return
+        context_menu = QtWidgets.QMenu(self)
+        remove_plot_act = context_menu.addAction(
+            "Remove %s" % self.curr_plot_title)
+        remove_plot_act.setStatusTip("Remove the current Plot")
+        remove_plot_act.triggered.connect(self.hide_curr_plot)
+
+        if self.hidden_plots != {}:
+            show_all_plot_act = context_menu.addAction("Show All Plots")
+            show_all_plot_act.triggered.connect(self.show_all_hidden_plots)
+        show_plot_acts = []
+        for i in sorted(self.hidden_plots.keys()):
+            show_plot_acts.append(
+                context_menu.addAction('Show hidden Plot %s' % i))
+            show_plot_acts[-1].triggered.connect(
+                lambda *arg, index=i: self.show_hidden_plot(index))
+        context_menu.exec_(self.mapToGlobal(event.pos()))
+
+    def get_timestamp(self, event):
+        x, y = event.x, event.y  # coordinate data
+        inv = self.axes[0].transData.inverted()
+        # convert to timestamp, bc event.xdata is None in the space bw axes
+        xdata = inv.transform((x, y))[0]
+        return xdata
+
+    def zoom_bw_markers(self, xdata):
+        if self.curr_min_x == xdata:
+            return
+        self.zoom_marker1_shown = False
+        [self.curr_min_x, self.curr_max_x] = sorted([self.curr_min_x, xdata])
+        self.set_lim()
+        self.zoom_marker1.set_visible(False)
+        self.zoom_marker2.set_visible(False)
+
+    def on_pick(self, event):
+        """
+        xmouse, ymouse = event.mouseevent.xdata, event.mouseevent.ydata
+        # x, y = artist.get_xdata(), artist.get_ydata()
+        # ind = event.ind
+        print('Artist picked:', artist)
+        # print(self.plots)
+        # print('Index:', self.plots.index(event.artist))
+        # print('{} vertices picked'.format(len(ind)))
+        # print('Pick between vertices {} and {}'.format(
+        min(ind), max(ind) + 1))
+        print('x, y of mouse: {:.2f},{:.2f}'.format(xmouse, ymouse))
+        # print('Data point:', x[ind[0]], y[ind[0]])
+                    print("mouseevent:", dir(event.mouseevent))
+            print("guievent:", dir(event.guiEvent))
+            print("modifiers:", event.guiEvent.modifiers())
+        """
+        # print(dir(event))
+        modifiers = event.guiEvent.modifiers()
+        try:
+            # on_pick() take place after on_pick_on_artist where
+            # tps_t was assigned in TimePowerSquareWidget
+            xdata = self.tps_t
+        except AttributeError:
+            xdata = self.get_timestamp(event)
+        for w in self.peer_plotting_widgets:
+            if modifiers == QtCore.Qt.ShiftModifier:
+                w.on_shift_click(xdata)
+            elif modifiers in [QtCore.Qt.ControlModifier,
+                               QtCore.Qt.MetaModifier]:
+                w.on_ctrl_cmd_click(xdata)
+
+            else:
+                if w.zoom_marker1_shown:
+                    w.zoom_bw_markers(xdata)
+            w.draw()
+
+    def on_ctrl_cmd_click(self, xdata):
+        """
+        On ctrl/cmd + left click
+        + hide zoom_marker1
+        + connect ruler to follow mouse
+        + show ruler
+        """
+        self.zoom_marker1.set_visible(False)
+        self.zoom_marker1_shown = False
+        try:
+            self.fig.canvas.mpl_disconnect(self.follower)
+        except AttributeError:
+            pass
+        self.set_ruler_visibled(self.ruler, xdata)
+
+    def on_shift_click(self, xdata):
+        """
+        On shift + left click
+        if there're no zoom marker
+            + hide ruler
+            + show zoom_marker1, zoom_marker2
+            + connect zoom_marker2 to follow mouse
+        else:
+            + zoom data in between 2 zoomMarkers
+
+        """
+        if not self.zoom_marker1_shown:
+            self.ruler.set_visible(False)
+            self.set_ruler_visibled(self.zoom_marker1, xdata)
+            self.curr_min_x = xdata
+            self.zoom_marker1_shown = True
+            self.set_ruler_visibled(self.zoom_marker2, xdata)
+        else:
+            self.zoom_bw_markers(xdata)
+
+    def on_pick_on_artist(self, event):
+        artist = event.artist
+        if isinstance(artist, pl.Axes):
+            self.curr_plot = artist
+            self.curr_plot_index = self.axes.index(artist)
+            self.curr_plot_title = "Plot %s" % self.curr_plot_index
+
+    def set_ruler_visibled(self, ruler, x):
+        ruler.set_visible(True)
+        ruler.xy1 = (x, 0)
+        ruler.xy2 = (x, self.bottom)
+        try:
+            if ruler == self.zoom_marker2:
+                # make zoom_marker2 follow mouse.
+                # need to disconnect when state of rulers change
+                self.follower = self.fig.canvas.mpl_connect(
+                    "motion_notify_event", self.zoom_marker2_follow_mouse)
+        except AttributeError:
+            pass
+
+    def zoom_marker2_follow_mouse(self, mouseevent):
+        xdata = self.get_timestamp(mouseevent)
+        self.zoom_marker2.xy1 = (xdata, 0)
+        self.zoom_marker2.xy2 = (xdata, self.bottom)
+        self.draw()
+
+    def keyPressEvent(self, event):
+        if event.key() == QtCore.Qt.Key_Escape:
+            for w in self.peer_plotting_widgets:
+                w.set_rulers_invisible()
+                w.zoom_marker1_shown = False
+                w.draw()
+        return super(PlottingWidget, self).keyPressEvent(event)
+
+    def set_rulers_invisible(self):
+        self.ruler.set_visible(False)
+        self.zoom_marker1.set_visible(False)
+        self.zoom_marker2.set_visible(False)
+
+    # ============================= END EVENT ==============================
+
+    def add_timestamp_bar(self, used_height, top=True, has_label=True):
+        """
+        set the axes to display timestamp_bar on top of the plotting area
+        setting axis off to not display at the begining
+        Color in qpeek is DClr[T0]
+        :return:
+        """
+        self.plotting_bot -= used_height
+        timestamp_bar = self.canvas.figure.add_axes(
+            [self.plotting_l, self.plotting_bot, self.plotting_w, 0.00005],
+        )
+        timestamp_bar.axis('off')
+        timestamp_bar.xaxis.set_minor_locator(AutoMinorLocator())
+        timestamp_bar.spines['bottom'].set_color(self.display_color['basic'])
+        timestamp_bar.spines['top'].set_color(self.display_color['basic'])
+
+        if top:
+            labelbottom = False
+        else:
+            labelbottom = True
+        timestamp_bar.tick_params(which='major', length=7, width=2,
+                                  direction='inout',
+                                  colors=self.display_color['basic'],
+                                  labelbottom=labelbottom,
+                                  labeltop=not labelbottom)
+        timestamp_bar.tick_params(which='minor', length=4, width=1,
+                                  direction='inout',
+                                  colors=self.display_color['basic'])
+        if has_label:
+            timestamp_bar.set_ylabel('Hours',
+                                     fontweight='bold',
+                                     fontsize=self.font_size,
+                                     rotation=0,
+                                     labelpad=self.label_pad,
+                                     ha='left',
+                                     color=self.display_color['basic'])
+
+        # self.update_timestamp_bar(timestamp_bar)
+        return timestamp_bar
+
+    def update_timestamp_bar(self, timestamp_bar):
+        times, major_times, major_time_labels = getTimeTicks(
+            self.curr_min_x, self.curr_max_x, self.date_mode,
+            self.time_ticks_total
+        )
+        timestamp_bar.axis('on')
+        timestamp_bar.set_yticks([])
+        timestamp_bar.set_xticks(times, minor=True)
+        timestamp_bar.set_xticks(major_times)
+        timestamp_bar.set_xticklabels(major_time_labels,
+                                      fontsize=self.font_size + 2)
+        timestamp_bar.set_xlim(self.curr_min_x, self.curr_max_x)
+
+    def create_axes(self, plot_b, plot_h, has_min_max_lines=True):
+        ax = self.canvas.figure.add_axes(
+            [self.plotting_l, plot_b, self.plotting_w, plot_h],
+            picker=True
+        )
+
+        ax.spines['right'].set_visible(False)
+        ax.spines['left'].set_visible(False)
+        if has_min_max_lines:
+            ax.spines['top'].set_zorder(0)
+            ax.spines['bottom'].set_zorder(0)
+            ax.spines['top'].set_color(self.display_color['sub_basic'])
+            ax.spines['bottom'].set_color(self.display_color['sub_basic'])
+
+        ax.set_yticks([])
+        ax.set_xticks([])
+        ax.tick_params(colors=self.display_color['basic'],
+                       width=0,
+                       pad=-2,
+                       labelsize=self.font_size)
+        ax.patch.set_alpha(0)
+        return ax
+
+    def set_axes_info(self, ax, sample_no_list, sample_no_clr_list=None,
+                      label=None, info='', y=None, chan_db=None,
+                      linked_ax=None):
+        if label is None:
+            label = chan_db['label']
+        title_ver_alignment = 'center'
+        # set info undertitle
+        if linked_ax is not None:
+            info = label
+        if info != '':
+            ax.text(
+                -0.15, 0.2,
+                info,
+                horizontalalignment='left',
+                verticalalignment='top',
+                rotation='horizontal',
+                transform=ax.transAxes,
+                color=self.display_color['sub_basic'],
+                size=self.font_size
+            )
+            title_ver_alignment = 'top'
+
+        if linked_ax is None:
+            # set title on left side
+            color = self.display_color['plot_label']
+            if label.startswith("DEFAULT"):
+                color = self.display_color["warning"]
+            ax.text(
+                -0.15, 0.6,
+                label,
+                horizontalalignment='left',
+                verticalalignment=title_ver_alignment,
+                rotation='horizontal',
+                transform=ax.transAxes,
+                color=color,
+                size=self.font_size + 2
+            )
+
+        # set samples' total on right side
+        # if sample_no_clr_list is None:
+        #     sample_no_clr_list = len(sample_no_list) * ['W']
+        if len(sample_no_list) == 1:
+            ax.sampleLbl = ax.text(
+                1.005, 0.5,
+                sample_no_list[0],
+                horizontalalignment='left',
+                verticalalignment='center',
+                rotation='horizontal',
+                transform=ax.transAxes,
+                # color=Clr[sample_no_clr_list[0]],
+                color=self.display_color['basic'],
+                size=self.font_size
+            )
+        else:
+            # bottom
+            ax.sampleLbl = ax.text(
+                1.005, 0.25,
+                sample_no_list[0],
+                horizontalalignment='left',
+                verticalalignment='center',
+                rotation='horizontal',
+                transform=ax.transAxes,
+                # color=Clr[sample_no_clr_list[0]],
+                color=self.display_color['basic'],
+                size=self.font_size
+            )
+            # top
+            ax.sampleLbl = ax.text(
+                1.005, 0.75,
+                sample_no_list[1],
+                horizontalalignment='left',
+                verticalalignment='center',
+                rotation='horizontal',
+                transform=ax.transAxes,
+                # color=Clr[sample_no_clr_list[0]],
+                color=self.display_color['basic'],
+                size=self.font_size
+            )
+
+        if y is None:
+            # draw center line
+            ax.plot([self.curr_min_x, self.curr_max_x],
+                    [0, 0],
+                    color=self.display_color['sub_basic'],
+                    linewidth=0.5,
+                    zorder=1
+                    )
+            ax.spines['top'].set_visible(False)
+            ax.spines['bottom'].set_visible(False)
+        else:
+            try:
+                min_y = y.min()
+                max_y = y.max()
+                ax.spines['top'].set_visible(True)
+                ax.spines['bottom'].set_visible(True)
+                ax.unit_bw = getUnitBitweight(
+                    chan_db, self.parent.bitweightOpt
+                )
+                self.set_axes_ylim(ax, min_y, max_y)
+            except Exception:
+                pass
+
+    def set_axes_ylim(self, ax, min_y, max_y):
+        min_y = round(min_y, 7)
+        max_y = round(max_y, 7)
+        if max_y > min_y:
+            ax.set_yticks([min_y, max_y])
+            ax.set_yticklabels(
+                [ax.unit_bw.format(min_y), ax.unit_bw.format(max_y)])
+        if min_y == max_y:
+            max_y += 1
+            ax.set_yticks([min_y])
+            ax.set_yticklabels([ax.unit_bw.format(min_y)])
+        ax.set_ylim(min_y, max_y)
+
+    def add_gap_bar(self, gaps):
+        """
+        set the axes to display gap_bar on top of the plotting area
+        setting axis off to not display at the beginning
+        :return:
+        """
+        if self.parent.minGap is None:
+            return
+        self.gaps = getGaps(gaps, float(self.parent.minGap))
+        self.plotting_bot -= 0.003
+        self.gap_bar = self.create_axes(self.plotting_bot,
+                                        0.001,
+                                        has_min_max_lines=False)
+        self.update_gap_bar()
+
+    def update_gap_bar(self):
+        gap_label = "%sm" % self.parent.minGap
+        # TODO: calculate gap limit
+        # self.gaps = []
+        h = 0.001  # height of rectangle represent gap
+        self.set_axes_info(self.gap_bar, [len(self.gaps)],
+                           label=gap_label)
+        # draw gaps
+        for i in range(len(self.gaps)):
+            x = self.gaps[i][0]
+            w = self.gaps[i][1] - self.gaps[i][
+                0]  # width of rectangle represent gap
+            self.gap_bar.add_patch(
+                Rectangle(
+                    (x, - h / 2), w, h, color='r', picker=True, lw=0., zorder=3
+                )
+            )  # on top of center line
+
+    def get_height(self, ratio, bw_plots_distance=0.0015):
+        plot_h = 0.0012 * ratio  # ratio with figure height
+        self.plotting_bot -= plot_h + bw_plots_distance
+        self.plotting_bot_pixel += 19 * ratio
+        return plot_h
+
+    # -------------------- Different color dots ----------------------- #
+    def plot_none(self):
+        """
+        plot with nothing needed to show rulers
+        """
+        plot_h = 0.00001
+        bw_plots_distance = 0.0001
+        self.plotting_bot -= plot_h + bw_plots_distance
+        ax = self.create_axes(self.plotting_bot, plot_h,
+                              has_min_max_lines=False)
+        ax.x = None
+        ax.plot([0], [0], linestyle="")
+        return ax
+
+    def plot_multi_color_dots(self, c_data, chan_db, chan, ax, linked_ax):
+        """
+        plot scattered dots with colors defined by valueColors:
+          *:W or -1:_|0:R|2.3:Y|+2.3:G
+           with colors: RYGMC in dbSettings.py
+           _: not plot
+        :param data: data of the channel which is list of (time, value)
+        :param chan_db: info of channel from DB
+        :return:
+        """
+        if linked_ax is not None:
+            ax = linked_ax
+        if ax is None:
+            plot_h = self.get_height(chan_db['height'])
+            ax = self.create_axes(
+                self.plotting_bot, plot_h, has_min_max_lines=False)
+
+        x = []
+        prev_val = -constants.HIGHEST_INT
+
+        if chan_db['valueColors'] in [None, 'None', '']:
+            chan_db['valueColors'] = '*:W'
+        value_colors = chan_db['valueColors'].split('|')
+        for vc in value_colors:
+            v, c = vc.split(':')
+            val = getVal(v)
+            if c == '_':
+                prev_val = val
+                continue
+
+            if v.startswith('+'):
+                points = [c_data['times'][i]
+                          for i in range(len(c_data['data']))
+                          if c_data['data'][i] > val]
+            elif v == '*':
+                points = c_data['times']
+            else:
+                points = [c_data['times'][i]
+                          for i in range(len(c_data['data']))
+                          if prev_val < c_data['data'][i] <= val]
+            x += points
+
+            ax.plot(points, len(points) * [0], linestyle="",
+                    marker='s', markersize=0.5, zorder=3,
+                    color=Clr[c], picker=True, pickradius=3)
+            prev_val = val
+
+        total_samples = len(x)
+
+        x = sorted(x)
+        self.set_axes_info(ax, [total_samples], chan_db=chan_db,
+                           linked_ax=linked_ax)
+        if linked_ax is None:
+            ax.x = x
+        else:
+            ax.linkedX = x
+        return ax
+
+    # def plotDotsMasspos(self, c_data, chan_db, chan, linked_ax):
+    #     valueColors = getMassposValueColors(
+    #         self.parent.massPosVoltRangeOpt, chan, self.cMode, self.errors)
+    #     if valueColors is None:
+    #         return
+    #     chan_db['valueColors'] = valueColors
+    #     return self.plot_multi_color_dots(c_data, chan_db, chan, linked_ax)
+
+    # ---------------------------- up/down dots ---------------------------- #
+    def plot_up_down_dots(self, c_data, chan_db, chan, ax, linked_ax):
+        """
+        data with 2 different values defined in valueColors
+        """
+        if linked_ax is not None:
+            ax = linked_ax
+        if ax is None:
+            plot_h = self.get_height(chan_db['height'])
+            ax = self.create_axes(
+                self.plotting_bot, plot_h, has_min_max_lines=False)
+
+        val_cols = chan_db['valueColors'].split('|')
+        points_list = []
+        colors = []
+        for vc in val_cols:
+            v, c = vc.split(':')
+            val = getVal(v)
+
+            points = [c_data['times'][i]
+                      for i in range(len(c_data['data']))
+                      if c_data['data'][i] == val]
+            points_list.append(points)
+            colors.append(c)
+
+        # down dots
+        ax.plot(points_list[0], len(points_list[0]) * [-0.5], linestyle="",
+                marker='s', markersize=2, zorder=3,
+                color=Clr[colors[0]], picker=True, pickradius=3)
+        # up dots
+        ax.plot(points_list[1], len(points_list[1]) * [0.5], linestyle="",
+                marker='s', markersize=2, zorder=3,
+                color=Clr[colors[1]], picker=True, pickradius=3)
+        x = points_list[0] + points_list[1]
+        x = sorted(x)
+        ax.set_ylim(-2, 2)
+        self.set_axes_info(ax, [len(points_list[0]), len(points_list[1])],
+                           sample_no_clr_list=colors,
+                           chan_db=chan_db,
+                           linked_ax=linked_ax)
+        if linked_ax is None:
+            ax.x = x
+        else:
+            ax.linkedX = x
+        return ax
+
+    # ----------------------- dots for times, ignore data------------------- #
+    def plot_time_dots(self, c_data, chan_db, chan, ax, linked_ax):
+        if linked_ax is not None:
+            ax = linked_ax
+        if ax is None:
+            plot_h = self.get_height(chan_db['height'])
+            ax = self.create_axes(self.plotting_bot, plot_h)
+
+        color = 'W'
+        if chan_db['valueColors'] not in [None, 'None', '']:
+            color = chan_db['valueColors'].strip()
+        x = c_data['times']
+        self.set_axes_info(ax, [len(x)], chan_db=chan_db, linked_ax=linked_ax)
+
+        ax.myPlot = ax.plot(x, [0] * len(x), marker='s', markersize=1.5,
+                            linestyle='', zorder=2,
+                            color=Clr[color], picker=True,
+                            pickradius=3)
+        if linked_ax is None:
+            ax.x = x
+        else:
+            ax.linkedX = x
+        return ax
+
+    # ----------------------- lines - one color dots ----------------------- #
+    def plot_lines_dots(self, c_data, chan_db, chan, ax, linked_ax, info=''):
+        """ L:G|D:W """
+        if linked_ax is not None:
+            ax = linked_ax
+        if ax is None:
+            plot_h = self.get_height(chan_db['height'])
+            ax = self.create_axes(self.plotting_bot, plot_h)
+
+        x, y = c_data['times'], c_data['data']
+        self.set_axes_info(ax, [len(x)], chan_db=chan_db,
+                           info=info, y=y, linked_ax=linked_ax)
+        colors = {}
+        if chan_db['valueColors'] not in [None, 'None', '']:
+            color_parts = chan_db['valueColors'].split('|')
+            for cStr in color_parts:
+                obj, c = cStr.split(':')
+                colors[obj] = c
+
+        l_color = 'G'
+        has_dot = False
+        if 'L' in colors:
+            l_color = colors['L']
+        if 'D' in colors:
+            d_color = colors['D']
+            has_dot = True
+
+        if not has_dot:
+            ax.myPlot = ax.plot(x, y,
+                                linestyle='-', linewidth=0.7,
+                                color=Clr[l_color])
+        else:
+            ax.myPlot = ax.plot(x, y, marker='s', markersize=1.5,
+                                linestyle='-', linewidth=0.7, zorder=2,
+                                color=Clr[l_color],
+                                markerfacecolor=Clr[d_color],
+                                picker=True, pickradius=3)
+        if linked_ax is None:
+            ax.x = x
+            ax.y = y
+        else:
+            ax.linkedX = x
+            ax.linkedY = y
+        return ax
+
+    def plot_lines_s_rate(self, c_data, chan_db, chan, ax, linked_ax):
+        """
+        multi-line line seismic, one color, line only,
+            can apply bit weights in (get_unit_bitweight())
+        """
+        if c_data['samplerate'] >= 1.0:
+            info = "%dsps" % c_data['samplerate']
+        else:
+            info = "%gsps" % c_data['samplerate']
+        return self.plot_lines_dots(c_data, chan_db, chan, ax, linked_ax,
+                                    info=info)
+
+    # ----------------------- lines - multi-color dots --------------------- #
+    def plot_lines_mass_pos(self, c_data, chan_db, chan, ax, linked_ax):
+        value_colors = getMassposValueColors(
+            self.parent.massPosVoltRangeOpt, chan,
+            self.c_mode, self.errors, retType='tupleList')
+
+        if value_colors is None:
+            return
+
+        if ax is None:
+            plot_h = self.get_height(chan_db['height'])
+            ax = self.create_axes(self.plotting_bot, plot_h)
+
+        ax.x, ax.y = c_data['times'], c_data['data']
+        self.set_axes_info(ax, [len(ax.x)], chan_db=chan_db, y=ax.y)
+        ax.myPlot = ax.plot(ax.x, ax.y,
+                            linestyle='-', linewidth=0.7,
+                            color=self.display_color['sub_basic'],
+                            zorder=2)[0]
+        colors = [None] * len(ax.y)
+        sizes = [0.5] * len(ax.y)
+        for i in range(len(ax.y)):
+            count = 0
+            prev_v = 0
+            for v, c in value_colors:
+                if count < (len(value_colors) - 1):
+                    if prev_v < abs(ax.y[i]) <= v:
+                        colors[i] = Clr[c]
+                        break
+                else:
+                    # if abs(ax.y[i]) > v:
+                    colors[i] = Clr[c]
+                    break
+                prev_v = v
+                count += 1
+        ax.scatter(ax.x, ax.y, marker='s', c=colors, s=sizes, zorder=3)
+        return ax
+
+    # ---------------------------------------------------------#
+
+    def add_ruler(self, color):
+        ruler = ConnectionPatch(
+            xyA=(0, 0),
+            xyB=(0, self.bottom),
+            coordsA="data",
+            coordsB="data",
+            axesA=self.timestamp_bar_top,
+            axesB=self.timestamp_bar_bottom,
+            color=color,
+        )
+        ruler.set_visible(False)
+        self.timestamp_bar_bottom.add_artist(ruler)
+        return ruler
+
+    def set_lim(self, org_size=False):
+        if not org_size:
+            for chanID in self.plotting_data1:
+                c_data = self.plotting_data1[chanID]
+                self.get_zoom_data(c_data, chanID)
+            for chanID in self.plotting_data2:
+                c_data = self.plotting_data2[chanID]
+                self.get_zoom_data(c_data, chanID)
+        self.update_timestamp_bar(self.timestamp_bar_top)
+        self.update_timestamp_bar(self.timestamp_bar_bottom)
+        if hasattr(self, 'gap_bar'):
+            self.gap_bar.set_xlim(self.curr_min_x, self.curr_max_x)
+            if not org_size:
+                new_gaps = [g for g in self.gaps
+                            if (self.curr_min_x <= g[0] <= self.curr_max_x
+                                or self.curr_min_x <= g[1] <= self.curr_max_x)]
+
+                # reset total of samples on the right
+                self.gap_bar.sampleLbl.set_text(len(new_gaps))
+        for ax in self.axes:
+            ax.set_xlim(self.curr_min_x, self.curr_max_x)
+            if ax.x is None:
+                # the plot_none bar is at the end, no need to process
+                break
+            if not org_size:
+                # x, y
+                new_x_indexes = np.where((ax.x >= self.curr_min_x) &
+                                         (ax.x <= self.curr_max_x))
+
+                # reset total of samples on the right
+                ax.sampleLbl.set_text(len(new_x_indexes))
+
+                if len(new_x_indexes) == 0:
+                    continue
+                if hasattr(ax, 'y'):
+                    # don't need to reset y range if ax.y not exist
+                    new_x = ax.x[new_x_indexes]
+                    new_min_x = min(new_x)
+                    new_max_x = max(new_x)
+                    try:
+                        new_min_x_index = ax.x.index(new_min_x)
+                        new_max_x_index = ax.x.index(new_max_x)
+                    except AttributeError:
+                        new_min_x_index = np.where(ax.x == new_min_x)[0][0]
+                        new_max_x_index = np.where(ax.x == new_max_x)[0][0]
+                    new_y = ax.y[new_min_x_index:new_max_x_index + 1]
+                    new_min_y = min(new_y)
+                    new_max_y = max(new_y)
+                    self.set_axes_ylim(ax, new_min_y, new_max_y)
+
+    def set_title(self, title, y=100, v_align='top'):
+        self.fig.text(-0.15, y, title,
+                      verticalalignment=v_align,
+                      horizontalalignment='left',
+                      transform=self.timestamp_bar_top.transAxes,
+                      color=self.display_color['basic'],
+                      size=self.font_size)
+
+    def draw(self):
+        try:
+            self.canvas.draw()
+            # a bug on mac:
+            # not showing updated info until clicking on another window
+            # fix by calling repaint()
+            self.widgt.repaint()
+        except TypeError:
+            pass
+
+    # ######## Functions for outside world #####
+    def init_size(self):
+        geo = self.maximumViewportSize()
+        if self.plot_no == 0:
+            # set view size fit with the scroll's view port size
+            self.widgt.setFixedWidth(geo.width())
+            self.widgt.setFixedHeight(geo.height())
+
+    def set_msg_widget(self, msg_widget):
+        self.msg_widget = msg_widget
+
+    def set_background_color(self, color='black'):
+        self.fig.patch.set_facecolor(color)
+        self.draw()
+
+    def reset_view(self):
+        """
+        reset all zooms back to the first plotting
+        """
+        if self.axes == []:
+            return
+        self.curr_min_x = self.min_x
+        self.curr_max_x = self.max_x
+        self.set_lim()
+        self.draw()
+
+    def clear(self):
+        if self.zoom_marker1.get_visible():
+            self.zoom_marker1.set_visible(False)
+        else:
+            self.zoom_marker1.set_visible(True)
+        # self.fig.clear()
+        # self.axes = []
+        self.draw()
+
+    def apply_convert_factor(self, c_data, convert_factor):
+        """
+        convertFactor = 150mV/count = 150V/1000count
+        => unit data * convertFactor= data *150/1000 V
+        """
+        c_data['data'] = np.multiply(c_data['data'], [convert_factor])
+
+    # =============================== axes ================================
+    def plot_channels(self, start_tm, end_tm, key, data_time,
+                      gaps, channel_list, time_ticks_total,
+                      plotting_data1, plotting_data2):
+        """
+        :param key: staID for mseed, (device, expNo) for reftek
+        :param plottingData: a ditionary including:
+            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
+              channels:{cha: {netID, statID, locID, chanID, times, data,
+                              samplerate, startTmEpoch, endTmEpoch}       #
+              earliestUTC: the earliest time of all channels
+              latestUTC: the latest time of all channels
+        :timeTicksTotal: max number of tick to show on time bar
+
+        Data set: {channelname: [(x,y), (x,y)...]
+        """
+        self.plotting_data1 = plotting_data1
+        self.plotting_data2 = plotting_data2
+        self.processing_log = []  # [(message, type)]
+        self.errors = []
+        if self.axes != []:
+            self.fig.clear()
+        self.date_mode = self.parent.dateFormat.upper()
+        self.time_ticks_total = time_ticks_total
+
+        self.min_x = self.curr_min_x = max(data_time[0], start_tm)
+        self.max_x = self.curr_max_x = min(data_time[1], end_tm)
+        self.plot_no = len(self.plotting_data1) + len(self.plotting_data2)
+        title = getTitle(key, self.min_x, self.max_x, self.date_mode)
+        self.plotting_bot = BOTTOM
+        self.plotting_bot_pixel = BOTTOM_PX
+        self.axes = []
+
+        self.timestamp_bar_top = self.add_timestamp_bar(0.003)
+        self.set_title(title)
+        self.add_gap_bar(gaps)
+        not_found_chan = [c for c in channel_list
+                          if c not in self.plotting_data1.keys()]
+        if len(not_found_chan) > 0:
+            msg = (f"The following channels is in Channel Preferences but "
+                   f"not in the given data: {not_found_chan}")
+            self.processing_log.append((msg, 'warning'))
+
+        for chan_id in self.plotting_data1:
+            chan_db = extractData.getChanPlotInfo(chan_id,
+                                                  self.parent.dataType)
+            if chan_db['height'] == 0:
+                # not draw
+                continue
+            if chan_db['channel'] == 'DEFAULT':
+                msg = (f"Channel {chan_id}'s "
+                       f"definition can't be found database.")
+                displayTrackingInfo(self.tracking_box, msg, 'warning')
+
+            if chan_db['plotType'] == '':
+                continue
+            self.plotting_data1[chan_id]['chan_db'] = chan_db
+            self.get_zoom_data(self.plotting_data1[chan_id], chan_id,
+                               self.plotting_data1, True)
+
+        for chan_id in self.plotting_data2:
+            chan_db = extractData.getChanPlotInfo(chan_id,
+                                                  self.parent.dataType)
+            self.plotting_data2[chan_id]['chan_db'] = chan_db
+            self.get_zoom_data(self.plotting_data2[chan_id], chan_id, True)
+
+        self.axes.append(self.plot_none())
+        self.timestamp_bar_bottom = self.add_timestamp_bar(0.003, top=False)
+        self.set_lim(org_size=True)
+        self.bottom = self.axes[-1].get_ybound()[0]
+        self.ruler = self.add_ruler(self.display_color['time_ruler'])
+        self.zoom_marker1 = self.add_ruler(self.display_color['zoom_marker'])
+        self.zoom_marker2 = self.add_ruler(self.display_color['zoom_marker'])
+        # Set view size fit with the given data
+        if self.widgt.geometry().height() < self.plotting_bot_pixel:
+            self.widgt.setFixedHeight(self.plotting_bot_pixel)
+
+        self.draw()
+
+    def get_zoom_data(self, c_data, chan_id, plotting_data=None,
+                      first_time=False):
+        """
+        :param setID: (netID, statID, locID)
+        :param plotting_data: a ditionary including:
+            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
+              channels:{cha: {netID, statID, locID, chanID, times, data,
+                              samplerate, startTmEpoch, endTmEpoch}       #
+              earliestUTC: the earliest time of all channels
+              latestUTC: the latest time of all channels
+        :timeTicksTotal: max number of tick to show on time bar
+
+        Data set: {channelname: [(x,y), (x,y)...]
+        """
+        chan_db = c_data['chan_db']
+        plot_type = chan_db['plotType']
+        trim_downsample_SOHChan(c_data, self.curr_min_x, self.curr_max_x,
+                                first_time)
+        self.apply_convert_factor(c_data, 1)
+        if 'ax' not in c_data:
+            linked_ax = None
+            if chan_db['linkedChan'] not in [None, 'None', '']:
+                try:
+                    linked_ax = plotting_data['channels'][
+                        chan_db['linkedChan']]['ax']
+                except KeyError:
+                    pass
+            ax = getattr(self, dbConf['plotFunc'][plot_type][1])(
+                c_data, chan_db, chan_id, None, linked_ax)
+            if ax is None:
+                return
+            c_data['ax'] = ax
+            ax.chan = chan_id
+            self.axes.append(ax)
+        else:
+            getattr(self, dbConf['plotFunc'][plot_type][1])(
+                c_data, chan_db, chan_id, c_data['ax'], None)
+
+    def hide_plots(self, plot_indexes):
+        if self.axes == []:
+            return
+        plot_indexes = sorted(plot_indexes)
+        idx = 0
+        total_h = 0
+        for i in range(plot_indexes[0], len(self.axes)):
+            pos = self.axes[i].get_position()
+            pos.y0 += total_h
+            pos.y1 += total_h
+            if idx < len(plot_indexes) and i == plot_indexes[idx]:
+                h = pos.y1 - pos.y0
+                total_h += h
+                pos.y0 = pos.y1
+                idx += 1
+                self.hidden_plots[i] = h
+            self.axes[i].set_position(pos)
+
+        # currently consider every plot height are all 100px height
+        height = self.widgt.geometry().height() - 100 * len(plot_indexes)
+        self.widgt.setFixedHeight(height)
+        self.draw()
+
+    def hide_curr_plot(self):
+        pos = self.curr_plot.get_position()
+        h = pos.y1 - pos.y0
+        pos.y0 = pos.y1
+        self.curr_plot.set_position(pos)
+        for i in range(self.curr_plot_index + 1, len(self.axes)):
+            pos = self.axes[i].get_position()
+            pos.y0 += h
+            pos.y1 += h
+            self.axes[i].set_position(pos)
+        # currently consider every plot height are all 100px height
+        height = self.widgt.geometry().height() - 100
+        self.widgt.setFixedHeight(height)
+        self.hidden_plots[self.curr_plot_index] = h
+        self.draw()
+
+    def show_hidden_plot(self, index):
+        h = self.hidden_plots[index]
+        work_plot = self.axes[index]
+        pos = work_plot.get_position()
+        pos.y1 = pos.y0 + h
+        work_plot.set_position(pos)
+        for i in range(index, len(self.axes)):
+            pos = self.axes[i].get_position()
+            pos.y0 -= h
+            pos.y1 -= h
+            self.axes[i].set_position(pos)
+        # currently consider every plot height are all 100px height
+        height = self.widgt.geometry().height() + 100
+        self.widgt.setFixedHeight(height)
+        del self.hidden_plots[index]
+        pos = work_plot.get_position()
+        self.draw()
+
+    def show_all_hidden_plots(self):
+        plot_indexes = sorted(self.hidden_plots.keys())
+        idx = 0
+        total_h = 0
+        for i in range(plot_indexes[0], len(self.axes)):
+            pos = self.axes[i].get_position()
+            if idx < len(plot_indexes) and i == plot_indexes[idx]:
+                h = self.hidden_plots[i]
+                total_h += h
+                pos.y1 = pos.y0 + h
+                idx += 1
+                del self.hidden_plots[i]
+            pos.y0 -= total_h
+            pos.y1 -= total_h
+            self.axes[i].set_position(pos)
+
+        # currently consider every plot height are all 100px height
+        height = self.widgt.geometry().height() + 100 * len(plot_indexes)
+        self.widgt.setFixedHeight(height)
+        self.draw()
+
+    def replot(self):
+        for ax in self.axes:
+            print("pos", self.ax.get_position())
+
+    # ============================= END AXES ==============================
+    def set_colors(self, mode):
+        self.c_mode = mode
+        self.display_color = set_colors(mode)
+        self.fig.patch.set_facecolor(self.display_color['background'])
+
+    def set_peer_plotting_widgets(self, widgets):
+        self.peer_plotting_widgets = widgets
diff --git a/sohstationviewer/view/dataTypedialog.py b/sohstationviewer/view/dataTypedialog.py
deleted file mode 100755
index a546a2ddfb15fa2c90a001cbe229bb4e8d118d4a..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/dataTypedialog.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""
-datatypedialog.py
-GUI to add/edit/remove dataTypes
-NOTE: Cannot remove or change dataTypes that already have channels.
-"""
-
-from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog
-from sohstationviewer.database.proccessDB import executeDB
-
-
-class DataTypeDialog(Ui_DBInfoDialog):
-    def __init__(self, parent):
-        super().__init__(parent, ['No.', 'DataType'], 'dataType', 'dataTypes',
-                         resizeContentColumns=[0])
-        self.setWindowTitle("Edit/Add/Delete DataTypes")
-
-    def addRow(self, rowidx, fk=False):
-        self.addWidget(None, rowidx, 0)       # No.
-        self.addWidget(self.dataList, rowidx, 1, foreignkey=fk)
-
-    def getDataList(self):
-        dataTypeRows = executeDB('SELECT * FROM DataTypes')
-        return [[d[0]] for d in dataTypeRows]
-
-    def getRowInputs(self, rowidx):
-        return [self.dataTableWidget.cellWidget(rowidx, 1).text().strip()]
-
-    def updateData(self, row, widgetidx, listidx):
-        insertsql = (f"INSERT INTO DataTypes VALUES('{row[0]}')")
-        updatesql = (f"UPDATE DataTypes SET dataType='{row[0]}' "
-                     f"WHERE dataType='%s'")
-
-        return super().updateData(
-            row, widgetidx, listidx, insertsql, updatesql)
diff --git a/sohstationviewer/view/data_type_dialog.py b/sohstationviewer/view/data_type_dialog.py
new file mode 100755
index 0000000000000000000000000000000000000000..726dbb4c5f33f672a0b83119938333326d9eff73
--- /dev/null
+++ b/sohstationviewer/view/data_type_dialog.py
@@ -0,0 +1,34 @@
+"""
+datatypedialog.py
+GUI to add/edit/remove dataTypes
+NOTE: Cannot remove or change dataTypes that already have channels.
+"""
+
+from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog
+from sohstationviewer.database.proccessDB import executeDB
+
+
+class DataTypeDialog(Ui_DBInfoDialog):
+    def __init__(self, parent):
+        super().__init__(parent, ['No.', 'DataType'], 'dataType', 'dataTypes',
+                         resize_content_columns=[0])
+        self.setWindowTitle("Edit/Add/Delete DataTypes")
+
+    def add_row(self, row_idx, fk=False):
+        self.addWidget(None, row_idx, 0)  # No.
+        self.addWidget(self.data_list, row_idx, 1, foreign_key=fk)
+
+    def get_data_list(self):
+        data_type_rows = executeDB('SELECT * FROM DataTypes')
+        return [[d[0]] for d in data_type_rows]
+
+    def get_row_inputs(self, row_idx):
+        return [self.data_table_widget.cellWidget(row_idx, 1).text().strip()]
+
+    def update_data(self, row, widget_idx, list_idx):
+        insert_sql = f"INSERT INTO DataTypes VALUES('{row[0]}')"
+        update_sql = (f"UPDATE DataTypes SET dataType='{row[0]}' "
+                      f"WHERE dataType='%s'")
+
+        return super().update_data(
+            row, widget_idx, list_idx, insert_sql, update_sql)
diff --git a/sohstationviewer/view/mainwindow.py b/sohstationviewer/view/mainwindow.py
index b6936d4b0945387170d9c137d7bc9178cef5e5b0..323182d75e2c4b895c7f5a4c0fbd2bf3cd2d8af8 100755
--- a/sohstationviewer/view/mainwindow.py
+++ b/sohstationviewer/view/mainwindow.py
@@ -6,16 +6,16 @@ from copy import deepcopy
 from PySide2 import QtCore, QtWidgets
 
 from sohstationviewer.view.ui.main_ui import Ui_MainWindow
-from sohstationviewer.view.calendardialog import CalendarDialog
+from sohstationviewer.view.calendar_dialog import CalendarDialog
 from sohstationviewer.view.core.filelistwidget import FileListItem
 from sohstationviewer.controller.processing import loadData, detectDataType
-from sohstationviewer.view.dataTypedialog import DataTypeDialog
-from sohstationviewer.view.paramdialog import ParamDialog
-from sohstationviewer.view.channeldialog import ChannelDialog
-from sohstationviewer.view.plottypedialog import PlotTypeDialog
-from sohstationviewer.view.channelpreferdialog import ChannelPreferDialog
+from sohstationviewer.view.data_type_dialog import DataTypeDialog
+from sohstationviewer.view.param_dialog import ParamDialog
+from sohstationviewer.view.channel_dialog import ChannelDialog
+from sohstationviewer.view.plot_type_dialog import PlotTypeDialog
+from sohstationviewer.view.channel_prefer_dialog import ChannelPreferDialog
 from sohstationviewer.database.proccessDB import executeDB_dict
-from sohstationviewer.view.waveformdialog import WaveformDialog
+from sohstationviewer.view.waveform_dialog import WaveformDialog
 from sohstationviewer.view.time_power_squareddialog import (
     TimePowerSquaredDialog)
 from sohstationviewer.controller.util import displayTrackingInfo
@@ -39,9 +39,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
         self.waveformDlg = WaveformDialog(self)
         self.TPSDlg = TimePowerSquaredDialog(self)
 
-    # def resizeEvent(self, event):
-    #     self.plottingWidget.init_size()
-
     @QtCore.Slot()
     def openDataType(self):
         win = DataTypeDialog(self)
@@ -90,7 +87,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 
     @QtCore.Slot()
     def resetView(self):
-        self.plottingWidget.resetView()
+        self.plottingWidget.reset_view()
 
     @QtCore.Slot()
     def setDateFormat(self, displayFormat):
@@ -116,9 +113,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
         """
         print(f'Opening {item.text()}')
 
-        # Do something with the Path object,
+        # TODO: Do something with the Path object,
         # i.e., path.open(), or path.iterdir() ...
-        # path = item.filePath
 
     @QtCore.Slot()
     def changeCurrentDirectory(self):
@@ -218,7 +214,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 
         selKey = do.selectedKey
 
-        # do.createPlottingData(startTm, endTm)
         # mainPlot
 
         soh_data = deepcopy(do.SOHData[selKey])
@@ -236,11 +231,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
         peerPlottingWidgets = [self.plottingWidget]
 
         if self.tpsCheckBox.isChecked():
-            peerPlottingWidgets.append(self.TPSDlg.plottingWidget)
-            self.TPSDlg.setData(self.dataType, ','.join(self.dirnames))
+            peerPlottingWidgets.append(self.TPSDlg.plotting_widget)
+            self.TPSDlg.set_data(self.dataType, ','.join(self.dirnames))
             self.TPSDlg.show()
             wfChans = list(do.waveformData[do.selectedKey].keys())
-            self.TPSDlg.plottingWidget.plot_channels(
+            self.TPSDlg.plotting_widget.plot_channels(
                 self.startTm, self.endTm, selKey,
                 do.dataTime[selKey],
                 wf_data)
@@ -249,21 +244,21 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
 
         if self.reqWFChans != []:
             # waveformPlot
-            peerPlottingWidgets.append(self.waveformDlg.plottingWidget)
-            self.waveformDlg.setData(self.dataType, ','.join(self.dirnames))
+            peerPlottingWidgets.append(self.waveformDlg.plotting_widget)
+            self.waveformDlg.set_data(self.dataType, ','.join(self.dirnames))
             self.waveformDlg.show()
             wfChans = list(do.waveformData[do.selectedKey].keys())
-            self.waveformDlg.plottingWidget.plot_channels(
+            self.waveformDlg.plotting_widget.plot_channels(
                 self.startTm, self.endTm, selKey,
                 do.dataTime[selKey], wfChans, timeTickTotal,
                 wf_data, mp_data)
         else:
             self.waveformDlg.hide()
 
-        self.plottingWidget.setPeerPlottingWidgets(peerPlottingWidgets)
-        self.waveformDlg.plottingWidget.setPeerPlottingWidgets(
+        self.plottingWidget.set_peer_plotting_widgets(peerPlottingWidgets)
+        self.waveformDlg.plotting_widget.set_peer_plotting_widgets(
             peerPlottingWidgets)
-        self.TPSDlg.plottingWidget.setPeerPlottingWidgets(
+        self.TPSDlg.plotting_widget.set_peer_plotting_widgets(
             peerPlottingWidgets)
 
     def setCurrentDirectory(self, path=''):
diff --git a/sohstationviewer/view/param_dialog.py b/sohstationviewer/view/param_dialog.py
new file mode 100755
index 0000000000000000000000000000000000000000..1b181bbc8e135520acb728fac4b73801b8e7e980
--- /dev/null
+++ b/sohstationviewer/view/param_dialog.py
@@ -0,0 +1,68 @@
+"""
+param_dialog.py
+GUI to add/dit/remove params
+NOTE: Cannot remove or change params that are already used for channels.
+"""
+
+from PySide2 import QtWidgets
+
+
+from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog
+from sohstationviewer.view.core import plotting_widget
+from sohstationviewer.database.proccessDB import executeDB
+
+from sohstationviewer.conf.dbSettings import dbConf
+
+
+class ParamDialog(Ui_DBInfoDialog):
+    def __init__(self, parent):
+        super().__init__(
+            parent,
+            ['No.', 'Param', 'Plot Type', 'ValueColors', 'Height    '],
+            'param', 'parameters',
+            resize_content_columns=[0, 3])
+        self.setWindowTitle("Edit/Add/Delete Parameters")
+
+    def add_row(self, row_idx, fk=False):
+        self.addWidget(None, row_idx, 0)       # No.
+        self.addWidget(self.data_list, row_idx, 1, foreign_key=fk)
+        self.addWidget(self.data_list, row_idx, 2,
+                       choices=[''] + sorted(plotting_widget.plotFunc.keys()))
+
+        self.addWidget(self.data_list, row_idx, 3)
+        self.addWidget(self.data_list, row_idx, 4, range=[0, 10])
+
+    def get_data_list(self):
+        param_rows = executeDB('SELECT * FROM Parameters')
+        return [[d[0],
+                 '' if d[1] is None else d[1],
+                 d[2]]
+                for d in param_rows]
+
+    def get_row_inputs(self, row_idx):
+        # check value_colors string
+        value_colors_string = self.data_table_widget.cellWidget(
+            row_idx, 3).text().strip()
+        value_colors = value_colors_string.split("|")
+        for vc in value_colors:
+            if not dbConf['valColRE'].match(vc):
+                msg = (f"The valueColor is requested for  '{vc}' at line "
+                       f"{row_idx}does not match the required format:"
+                       f"[+]value:color with color=R,Y,G,M,C."
+                       f"\n Ex: 2.3:C|+4:M")
+                QtWidgets.QMessageBox.information(self, "Error", msg)
+        return [
+            self.data_table_widget.cellWidget(row_idx, 1).text().strip(),
+            self.data_table_widget.cellWidget(row_idx, 2).currentText(),
+            self.data_table_widget.cellWidget(row_idx, 3).text().strip(),
+            int(self.data_table_widget.cellWidget(row_idx, 4).text())
+        ]
+
+    def update_data(self, row, widget_idx, list_idx):
+        insertsql = (f"INSERT INTO Parameters VALUES"
+                     f"('{row[0]}', '{row[1]}', {row[2]})")
+        updatesql = (f"UPDATE Parameters SET param='{row[0]}', "
+                     f"plotType='{row[1]}', height={row[2]} "
+                     f"WHERE param='%s'")
+        return super().update_data(
+            row, widget_idx, list_idx, insertsql, updatesql)
diff --git a/sohstationviewer/view/paramdialog.py b/sohstationviewer/view/paramdialog.py
deleted file mode 100755
index 0315073a7ff1589cd15faaeaf81e1256d071c433..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/paramdialog.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""
-paramdialog.py
-GUI to add/dit/remove params
-NOTE: Cannot remove or change params that are already used for channels.
-"""
-
-from PySide2 import QtWidgets
-
-
-from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog
-from sohstationviewer.view.core import plottingWidget
-from sohstationviewer.database.proccessDB import executeDB
-
-from sohstationviewer.conf.dbSettings import dbConf
-
-
-class ParamDialog(Ui_DBInfoDialog):
-    def __init__(self, parent):
-        super().__init__(
-            parent,
-            ['No.', 'Param', 'Plot Type', 'ValueColors', 'Height    '],
-            'param', 'parameters',
-            resizeContentColumns=[0, 3])
-        self.setWindowTitle("Edit/Add/Delete Parameters")
-
-    def addRow(self, rowidx, fk=False):
-        self.addWidget(None, rowidx, 0)       # No.
-        self.addWidget(self.dataList, rowidx, 1, foreignkey=fk)
-        self.addWidget(self.dataList, rowidx, 2,
-                       choices=[''] + sorted(plottingWidget.plotFunc.keys()))
-
-        self.addWidget(self.dataList, rowidx, 3)
-        self.addWidget(self.dataList, rowidx, 4, range=[0, 10])
-
-    def getDataList(self):
-        paramRows = executeDB('SELECT * FROM Parameters')
-        return [[d[0],
-                 '' if d[1] is None else d[1],
-                 d[2]]
-                for d in paramRows]
-
-    def getRowInputs(self, rowidx):
-        # check vallueColors string
-        valueColorsString = self.dataTableWidget.cellWidget(
-            rowidx, 3).currentText().strip()
-        valueColors = valueColorsString.split("|")
-        for vc in valueColors:
-            if not dbConf['valColRE'].match(vc):
-                msg = (f"The valueColor is requested for  '{vc}' at line "
-                       f"{rowidx}does not match the required format:"
-                       f"[+]value:color with color=R,Y,G,M,C."
-                       f"\n Ex: 2.3:C|+4:M")
-                QtWidgets.QMessageBox.information(self, "Error", msg)
-        return [
-            self.dataTableWidget.cellWidget(rowidx, 1).text().strip(),
-            self.dataTableWidget.cellWidget(rowidx, 2).currentText().strip(),
-            self.dataTableWidget.cellWidget(rowidx, 3).currentText().strip(),
-            int(self.dataTableWidget.cellWidget(rowidx, 4).text())
-        ]
-
-    def updateData(self, row, widgetidx, listidx):
-        insertsql = (f"INSERT INTO Parameters VALUES"
-                     f"('{row[0]}', '{row[1]}', {row[2]})")
-        updatesql = (f"UPDATE Parameters SET param='{row[0]}', "
-                     f"plotType='{row[1]}', height={row[2]} "
-                     f"WHERE param='%s'")
-        return super().updateData(
-            row, widgetidx, listidx, insertsql, updatesql)
diff --git a/sohstationviewer/view/plot_type_dialog.py b/sohstationviewer/view/plot_type_dialog.py
new file mode 100755
index 0000000000000000000000000000000000000000..7e965fb99536a0359267146c8258f79f797811f9
--- /dev/null
+++ b/sohstationviewer/view/plot_type_dialog.py
@@ -0,0 +1,24 @@
+"""
+plottypedialog
+GUI to view the types of plotting and their descriptions
+NOTE: plottypes are defined in plottingWidget
+"""
+
+from sohstationviewer.view.core.db_gui_superclass import Ui_DBInfoDialog
+from sohstationviewer.view.core import plotting_widget
+
+
+class PlotTypeDialog(Ui_DBInfoDialog):
+    def __init__(self, parent):
+        super().__init__(
+            parent, ['No.', '       Plot Type        ', 'Description'],
+            '', '', resize_content_columns=[0, 1], check_fk=False)
+        self.setWindowTitle("Plotting Types")
+
+    def add_row(self, row_idx, fk=False):
+        self.addWidget(None, row_idx, 0)       # No.
+        self.addWidget(self.data_list, row_idx, 1, foreign_key=True)
+        self.addWidget(self.data_list, row_idx, 2, foreign_key=True)
+
+    def get_data_list(self):
+        return [[key, val[0]] for key, val in plotting_widget.plotFunc.items()]
diff --git a/sohstationviewer/view/plottypedialog.py b/sohstationviewer/view/plottypedialog.py
deleted file mode 100755
index 4611d3eef0d7e87c0050a61f33f7dfecafd84709..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/plottypedialog.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-plottypedialog
-GUI to view the types of plotting and their descriptions
-NOTE: plottypes are defined in plottingWidget
-"""
-
-from sohstationviewer.view.core.dbgui_superclass import Ui_DBInfoDialog
-from sohstationviewer.view.core import plottingWidget
-
-
-class PlotTypeDialog(Ui_DBInfoDialog):
-    def __init__(self, parent):
-        super().__init__(
-            parent, ['No.', '       Plot Type        ', 'Description'],
-            '', '', resizeContentColumns=[0, 1], checkFK=False)
-        self.setWindowTitle("Plotting Types")
-
-    def addRow(self, rowidx, fk=False):
-        self.addWidget(None, rowidx, 0)       # No.
-        self.addWidget(self.dataList, rowidx, 1, foreignkey=True)
-        self.addWidget(self.dataList, rowidx, 2, foreignkey=True)
-
-    def getDataList(self):
-        return [[key, val[0]] for key, val in plottingWidget.plotFunc.items()]
diff --git a/sohstationviewer/view/time_power_squareddialog.py b/sohstationviewer/view/time_power_squareddialog.py
index 225f7121321a5898c36e5a8db03dd5135f19368c..c0aa7dca1076b19a2c88f6b269a1fbb76ec858c3 100755
--- a/sohstationviewer/view/time_power_squareddialog.py
+++ b/sohstationviewer/view/time_power_squareddialog.py
@@ -4,7 +4,7 @@ import numpy as np
 
 from PySide2 import QtWidgets, QtCore
 
-from sohstationviewer.view.core import plottingWidget as plottingWidget
+from sohstationviewer.view.core import plotting_widget as plottingWidget
 from sohstationviewer.controller.plottingData import (
     getTitle, getDayTicks, formatTime)
 from sohstationviewer.model.handling_data import (
@@ -18,113 +18,113 @@ from sohstationviewer.controller.util import displayTrackingInfo, fmti
 
 class TimePowerSquaredWidget(plottingWidget.PlottingWidget):
 
-    def plot_channels(self, startTm=None, endTm=None, key=None,
-                      dataTime=None, plottingData=None):
-        self.processingLog = []  # [(message, type)]
+    def plot_channels(self, start_tm=None, end_tm=None, key=None,
+                      data_time=None, plotting_data=None):
+        self.processing_log = []  # [(message, type)]
 
-        self.minX = self.currMinX = max(dataTime[0], startTm)
-        self.maxX = self.currMaxX = min(dataTime[1], endTm)
+        self.min_x = self.curr_min_x = max(data_time[0], start_tm)
+        self.max_x = self.curr_max_x = min(data_time[1], end_tm)
         self.errors = []
         if self.axes != []:
             self.fig.clear()
-        self.dateMode = self.parent.dateFormat.upper()
-        if plottingData is not None:
-            self.plottingData = plottingData
-            self.minX = max(dataTime[0], startTm)
-            self.maxX = min(dataTime[1], endTm)
-            self.plotNo = len(plottingData)
-        title = getTitle(key, self.minX, self.maxX, self.dateMode)
-        self.plottingBot = plottingWidget.BOTTOM
-        self.plottingBotPixel = plottingWidget.BOTTOM_PX
+        self.date_mode = self.parent.date_format.upper()
+        if plotting_data is not None:
+            self.plotting_data = plotting_data
+            self.min_x = max(data_time[0], start_tm)
+            self.max_x = min(data_time[1], end_tm)
+            self.plot_no = len(plotting_data)
+        title = getTitle(key, self.min_x, self.max_x, self.date_mode)
+        self.plotting_bot = plottingWidget.BOTTOM
+        self.plotting_bot_pixel = plottingWidget.BOTTOM_PX
         self.axes = []
         self.rulers = []
         self.zoom_marker1s = []
         self.zoom_marker2s = []
 
-        self.timestampBarTop = self.add_timestamp_bar(0.)
-        self.set_title(title, y=0, valight='bottom')
-        self.eachDay5MinList = get_eachDay5MinList(self.minX, self.maxX)
-        for chanID in self.plottingData:
-            ax = self.getZoomData(self.plottingData[chanID], chanID)
-            # ax.chan = chanID
+        self.timestamp_bar_top = self.add_timestamp_bar(0.)
+        self.set_title(title, y=0, v_align='bottom')
+        self.each_day5_min_list = get_eachDay5MinList(self.min_x, self.max_x)
+        for chanID in self.plotting_data:
+            ax = self.get_zoom_data(self.plotting_data[chanID], chanID)
             self.axes.append(ax)
-        self.setLegend()
+        self.set_legend()
         # Set view size fit with the given data
-        if self.widgt.geometry().height() < self.plottingBotPixel:
-            self.widgt.setFixedHeight(self.plottingBotPixel)
+        if self.widgt.geometry().height() < self.plotting_bot_pixel:
+            self.widgt.setFixedHeight(self.plotting_bot_pixel)
         self.set_lim_markers()
         self.draw()
 
-    def getZoomData(self, cData, chanID):
-        if 'tps_data' not in cData:
+    def get_zoom_data(self, c_data, chan_id):
+        if 'tps_data' not in c_data:
             # get new minX, maxX according to exact start time  of days
-            get_trimTPSData(cData, self.minX, self.maxX, self.eachDay5MinList)
-        totalDays = cData['tps_data'].shape[0]
-        plotH = self.get_height(1.5 * totalDays, bwPlotsDistance=0.003)
-        ax = self.create_axes(self.plottingBot, plotH)
+            get_trimTPSData(c_data, self.min_x, self.max_x,
+                            self.each_day5_min_list)
+        total_days = c_data['tps_data'].shape[0]
+        plot_h = self.get_height(1.5 * total_days, bw_plots_distance=0.003)
+        ax = self.create_axes(self.plotting_bot, plot_h)
         ax.text(
             -0.1, 1.2,
-            f"{getChanLabel(chanID)} {cData['samplerate']}",
+            f"{getChanLabel(chan_id)} {c_data['samplerate']}",
             horizontalalignment='left',
             verticalalignment='top',
             rotation='horizontal',
             transform=ax.transAxes,
-            color=self.displayColor['plot_label'],
-            size=self.fontSize + 2
+            color=self.display_color['plot_label'],
+            size=self.font_size + 2
         )
 
         zm1 = ax.plot(
             [], [], marker='|', markersize=10,
-            markeredgecolor=self.displayColor['zoom_marker'])[0]
+            markeredgecolor=self.display_color['zoom_marker'])[0]
         self.zoom_marker1s.append(zm1)
 
         zm2 = ax.plot(
             [], [], marker='|', markersize=10,
-            markeredgecolor=self.displayColor['zoom_marker'])[0]
+            markeredgecolor=self.display_color['zoom_marker'])[0]
         self.zoom_marker2s.append(zm2)
 
         rl = ax.plot(
             [], [], marker='s', markersize=5,
-            markeredgecolor=self.displayColor['time_ruler'],
+            markeredgecolor=self.display_color['time_ruler'],
             markerfacecolor='None')[0]
         self.rulers.append(rl)
 
         x = np.array([i for i in range(const.NO_5M_DAY)])
-        sCnts = self.parent.selSquareCounts      # square counts range
-        clrs = self.parent.colorDef               # colordef
+        s_cnts = self.parent.sel_square_counts  # square counts range
+        clrs = self.parent.color_def  # colordef
 
-        for dayIdx, y in enumerate(cData['tps_data']):
+        for dayIdx, y in enumerate(c_data['tps_data']):
             # not draw data out of day range
-            colorSet = self.getColorSet(y, sCnts, clrs)
+            color_set = self.get_color_set(y, s_cnts, clrs)
             # (- dayIdx): each day is a line, increase from top to bottom
-            ax.scatter(x, [- dayIdx]*len(x), marker='|',
-                       c=colorSet, s=7, alpha=0.8)
+            ax.scatter(x, [- dayIdx] * len(x), marker='|',
+                       c=color_set, s=7, alpha=0.8)
         # extra to show highlight square
-        ax.set_ylim(-(cData['tps_data'].shape[0] + 1), 1)
+        ax.set_ylim(-(c_data['tps_data'].shape[0] + 1), 1)
 
         return ax
 
-    def setLegend(self):
+    def set_legend(self):
         """
         plot one dot for each color and assign label to it
         legend will be one color for each label
         """
         # set height of legend and distance bw legend and upper ax
-        plotH = self.get_height(7, bwPlotsDistance=0.003)
+        plot_h = self.get_height(7, bw_plots_distance=0.003)
         ax = self.canvas.figure.add_axes(
-            [self.plottingL, self.plottingBot, self.plottingW, plotH],
+            [self.plotting_l, self.plotting_bot, self.plotting_w, plot_h],
             picker=True
         )
         ax.patch.set_alpha(0)
-        cLabels = self.parent.selColLabels
-        clrs = self.parent.colorDef  # colordef
-        for idx in range(len(cLabels)):
+        c_labels = self.parent.sel_col_labels
+        clrs = self.parent.color_def  # colordef
+        for idx in range(len(c_labels)):
             # draw a dot out of xlim so it isn't displayed in plotting area
             ax.scatter([300], [1],
                        c=Clr[clrs[idx]],
                        s=0.1,
-                       label=cLabels[idx],
-                       edgecolor=self.displayColor['basic'],
+                       label=c_labels[idx],
+                       edgecolor=self.display_color['basic'],
                        alpha=0.8,
                        zorder=1,
                        picker=True)
@@ -132,9 +132,9 @@ class TimePowerSquaredWidget(plottingWidget.PlottingWidget):
         ax.set_xlim(-2, const.NO_5M_DAY + 1)
         ax.legend(loc="upper left", framealpha=0.2,
                   markerscale=25,
-                  labelcolor=self.displayColor['basic'])
+                  labelcolor=self.display_color['basic'])
 
-    def getColorSet(self, y, sCnts, col):
+    def get_color_set(self, y, s_cnts, col):
         """
         create array of color (col) according to value of y compare with
          sCnts (square count range)
@@ -145,38 +145,37 @@ class TimePowerSquaredWidget(plottingWidget.PlottingWidget):
         """
         return (
             np.where(
-                y == sCnts[0], Clr[col[0]], np.where(
-                    y < sCnts[1], Clr[col[1]], np.where(
-                        y < sCnts[2], Clr[col[2]], np.where(
-                            y < sCnts[3], Clr[col[3]], np.where(
-                                y < sCnts[4], Clr[col[4]], np.where(
-                                    y < sCnts[5], Clr[col[5]], np.where(
-                                        y < sCnts[6], Clr[col[6]], Clr[col[7]])
-                                )))))))
-
-    def create_axes(self, plotB, plotH, hasMinMaxLines=False):
+                y == s_cnts[0], Clr[col[0]], np.where(
+                    y < s_cnts[1], Clr[col[1]], np.where(
+                        y < s_cnts[2], Clr[col[2]], np.where(
+                            y < s_cnts[3], Clr[col[3]], np.where(
+                                y < s_cnts[4], Clr[col[4]], np.where(
+                                    y < s_cnts[5], Clr[col[5]], np.where(
+                                        y < s_cnts[6], Clr[col[6]], Clr[col[7]]
+                                    ))))))))
+
+    def create_axes(self, plot_b, plot_h, has_min_max_lines=False):
         """
         create axes for 288 of 5m in a day in which minor tick for every hour,
         major tick for every 4 hour
         """
         ax = self.canvas.figure.add_axes(
-            [self.plottingL, plotB, self.plottingW, plotH],
+            [self.plotting_l, plot_b, self.plotting_w, plot_h],
             picker=True
         )
-        # ax.axis('off')
         ax.spines['right'].set_visible(False)
         ax.spines['left'].set_visible(False)
         ax.xaxis.grid(True, which='major',
-                      color=self.displayColor['basic'], linestyle='-')
+                      color=self.display_color['basic'], linestyle='-')
         ax.xaxis.grid(True, which='minor',
-                      color=self.displayColor['sub_basic'], linestyle='-')
+                      color=self.display_color['sub_basic'], linestyle='-')
         ax.set_yticks([])
 
-        times, majorTimes, majorTimeLabels = getDayTicks()
+        times, major_times, major_time_labels = getDayTicks()
         ax.set_xticks(times, minor=True)
-        ax.set_xticks(majorTimes)
-        ax.set_xticklabels(majorTimeLabels, fontsize=self.fontSize,
-                           color=self.displayColor['basic'])
+        ax.set_xticks(major_times)
+        ax.set_xticklabels(major_time_labels, fontsize=self.font_size,
+                           color=self.display_color['basic'])
         # extra to show highlight square
         ax.set_xlim(-2, const.NO_5M_DAY + 1)
         ax.patch.set_alpha(0)
@@ -187,7 +186,7 @@ class TimePowerSquaredWidget(plottingWidget.PlottingWidget):
         Click on each point will highlight time position for each channel
         and display time and counts for each channel
         """
-        infoStr = ""
+        info_str = ""
         if event.artist in self.axes:
             xdata = round(event.mouseevent.xdata)
             # when click on outside xrange that close to edge, adjust to edge
@@ -197,18 +196,18 @@ class TimePowerSquaredWidget(plottingWidget.PlottingWidget):
                 xdata = 287
             ydata = round(event.mouseevent.ydata)
             if xdata is not None:
-                yIdx = - ydata
-                xIdx = xdata
+                y_idx = - ydata
+                x_idx = xdata
                 # identify time for other plots' rulers displaying
-                self.tps_t = self.eachDay5MinList[yIdx, xIdx]
-                format_t = formatTime(self.tps_t, self.dateMode, 'HH:MM:SS')
-                infoStr += f"{format_t}:"
-                for chanID in self.plottingData:
-                    cData = self.plottingData[chanID]
-                    data = cData['tps_data'][yIdx, xIdx]
-                    infoStr += f"  {chanID}:{fmti(sqrt(data))}"
-                infoStr += "  (counts)"
-                displayTrackingInfo(self.trackingBox, infoStr)
+                self.tps_t = self.each_day5_min_list[y_idx, x_idx]
+                format_t = formatTime(self.tps_t, self.date_mode, 'HH:MM:SS')
+                info_str += f"{format_t}:"
+                for chanID in self.plotting_data:
+                    c_data = self.plotting_data[chanID]
+                    data = c_data['tps_data'][y_idx, x_idx]
+                    info_str += f"  {chanID}:{fmti(sqrt(data))}"
+                info_str += "  (counts)"
+                displayTrackingInfo(self.tracking_box, info_str)
                 self.draw()
 
     def on_ctrl_cmd_click(self, xdata):
@@ -216,111 +215,111 @@ class TimePowerSquaredWidget(plottingWidget.PlottingWidget):
         Ctrl + cmd: base on xdata to find indexes of x an y
         in self.each_day_5_min_list to display ruler for each channel
         """
-        self.zoomMarker1Shown = False
-        xIdx, yIdx = findTPSTm(xdata, self.eachDay5MinList)
+        self.zoom_marker1_shown = False
+        x_idx, y_idx = findTPSTm(xdata, self.each_day5_min_list)
         for rl in self.rulers:
-            rl.set_data(xIdx, yIdx)
+            rl.set_data(x_idx, y_idx)
 
     def on_shift_click(self, xdata):
         """
         Shift + right click in TPS
         on the second of zoommaker, call set_lim_markers to mark the new limit
         """
-        if not self.zoomMarker1Shown:
+        if not self.zoom_marker1_shown:
             self.set_rulers_invisible()
-            self.currMinX = xdata
-            self.zoomMarker1Shown = True
+            self.curr_min_x = xdata
+            self.zoom_marker1_shown = True
         else:
-            [self.currMinX, self.currMaxX] = sorted(
-                [self.currMinX, xdata])
+            [self.curr_min_x, self.curr_max_x] = sorted(
+                [self.curr_min_x, xdata])
             self.set_lim_markers()
-            self.zoomMarker1Shown = False
+            self.zoom_marker1_shown = False
 
     def set_rulers_invisible(self):
         for rl in self.rulers:
             rl.set_data([], [])
 
     def set_lim_markers(self):
-        xIdx, yIdx = findTPSTm(self.currMinX, self.eachDay5MinList)
+        x_idx, y_idx = findTPSTm(self.curr_min_x, self.each_day5_min_list)
         for zm1 in self.zoom_marker1s:
-            zm1.set_data(xIdx, yIdx)
-            xIdx, yIdx = findTPSTm(self.currMaxX, self.eachDay5MinList)
+            zm1.set_data(x_idx, y_idx)
+            x_idx, y_idx = findTPSTm(self.curr_max_x, self.each_day5_min_list)
         for zm2 in self.zoom_marker2s:
-            zm2.set_data(xIdx, yIdx)
+            zm2.set_data(x_idx, y_idx)
 
 
 class TimePowerSquaredDialog(QtWidgets.QWidget):
     def __init__(self, parent):
         super().__init__()
         self.parent = parent
-        self.dateFormat = self.parent.dateFormat
-        self.bitweightOpt = self.parent.bitweightOpt
+        self.date_format = self.parent.dateFormat
+        self.bitweight_opt = self.parent.bitweightOpt
         self.setGeometry(50, 50, 1200, 700)
         self.setWindowTitle("TPS Plot")
 
-        mainLayout = QtWidgets.QVBoxLayout()
-        self.setLayout(mainLayout)
-        mainLayout.setContentsMargins(5, 5, 5, 5)
-        mainLayout.setSpacing(0)
+        main_layout = QtWidgets.QVBoxLayout()
+        self.setLayout(main_layout)
+        main_layout.setContentsMargins(5, 5, 5, 5)
+        main_layout.setSpacing(0)
 
-        self.trackingInfoTextBrowser = QtWidgets.QTextBrowser(self)
+        self.tracking_info_text_browser = QtWidgets.QTextBrowser(self)
 
-        self.plottingWidget = TimePowerSquaredWidget(
-            self, self.trackingInfoTextBrowser, "timepowersquaredwidget")
-        mainLayout.addWidget(self.plottingWidget, 2)
+        self.plotting_widget = TimePowerSquaredWidget(
+            self, self.tracking_info_text_browser, "timepowersquaredwidget")
+        main_layout.addWidget(self.plotting_widget, 2)
 
-        bottomLayout = QtWidgets.QHBoxLayout()
-        bottomLayout.addSpacing(20)
-        mainLayout.addLayout(bottomLayout)
+        bottom_layout = QtWidgets.QHBoxLayout()
+        bottom_layout.addSpacing(20)
+        main_layout.addLayout(bottom_layout)
 
         # ################ Color range #################
-        bottomLayout.addWidget(QtWidgets.QLabel("Color Range"))
-
-        self.colorDef = getColorDef()
-        (self.colorRanges,
-         self.allSquareCounts,
-         self.colorLabel) = getColorRanges()
-        self.colorRangeChoice = QtWidgets.QComboBox(self)
-        self.colorRangeChoice.addItems(self.colorRanges)
-        self.colorRangeChoice.setCurrentText('high')
-        bottomLayout.addWidget(self.colorRangeChoice)
+        bottom_layout.addWidget(QtWidgets.QLabel("Color Range"))
+
+        self.color_def = getColorDef()
+        (self.color_ranges,
+         self.all_square_counts,
+         self.color_label) = getColorRanges()
+        self.color_range_choice = QtWidgets.QComboBox(self)
+        self.color_range_choice.addItems(self.color_ranges)
+        self.color_range_choice.setCurrentText('high')
+        bottom_layout.addWidget(self.color_range_choice)
         ################################################
-        self.replotButton = QtWidgets.QPushButton("RePlot", self)
-        bottomLayout.addWidget(self.replotButton)
+        self.replot_button = QtWidgets.QPushButton("RePlot", self)
+        bottom_layout.addWidget(self.replot_button)
 
-        self.writeFileButton = QtWidgets.QPushButton('Write file', self)
-        bottomLayout.addWidget(self.writeFileButton)
+        self.write_file_button = QtWidgets.QPushButton('Write file', self)
+        bottom_layout.addWidget(self.write_file_button)
 
-        self.trackingInfoTextBrowser.setFixedHeight(60)
-        bottomLayout.addWidget(self.trackingInfoTextBrowser)
+        self.tracking_info_text_browser.setFixedHeight(60)
+        bottom_layout.addWidget(self.tracking_info_text_browser)
 
-        self.connectSignals()
-        self.colorRangeChanged()
+        self.connect_signals()
+        self.color_range_changed()
 
-    def setData(self, dataType, fileName):
-        self.dataType = dataType
-        self.setWindowTitle("TPS Plot %s - %s" % (dataType, fileName))
+    def set_data(self, data_type, file_name):
+        self.data_type = data_type
+        self.setWindowTitle("TPS Plot %s - %s" % (data_type, file_name))
 
     def resizeEvent(self, event):
-        self.plottingWidget.init_size()
+        self.plotting_widget.init_size()
 
-    def connectSignals(self):
-        self.writeFileButton.clicked.connect(self.writeFile)
-        self.replotButton.clicked.connect(self.replot)
-        self.colorRangeChoice.currentTextChanged.connect(
-            self.colorRangeChanged)
+    def connect_signals(self):
+        self.write_file_button.clicked.connect(self.write_file)
+        self.replot_button.clicked.connect(self.replot)
+        self.color_range_choice.currentTextChanged.connect(
+            self.color_range_changed)
 
     @QtCore.Slot()
-    def colorRangeChanged(self):
-        colorRange = self.colorRangeChoice.currentText()
-        crIndex = self.colorRanges.index(colorRange)
-        self.selSquareCounts = self.allSquareCounts[crIndex]
-        self.selColLabels = self.colorLabel[crIndex]
+    def color_range_changed(self):
+        color_range = self.color_range_choice.currentText()
+        cr_index = self.color_ranges.index(color_range)
+        self.sel_square_counts = self.all_square_counts[cr_index]
+        self.sel_col_labels = self.color_label[cr_index]
 
     @QtCore.Slot()
-    def writeFile(self):
+    def write_file(self):
         print("writeFile")
 
     @QtCore.Slot()
     def replot(self):
-        self.plottingWidget.plot_channels()
+        self.plotting_widget.plot_channels()
diff --git a/sohstationviewer/view/ui/calendar_ui_qtdesigner.py b/sohstationviewer/view/ui/calendar_ui_qtdesigner.py
index 34e8bfe7329fc58cfc6afa4bd9c9ef2b93d007ff..6db266827f8561da36409fb314163c68c43fb1ea 100644
--- a/sohstationviewer/view/ui/calendar_ui_qtdesigner.py
+++ b/sohstationviewer/view/ui/calendar_ui_qtdesigner.py
@@ -10,7 +10,7 @@
 
 from PySide2 import QtCore, QtWidgets
 
-from sohstationviewer.view.core.calendarwidget import CalendarWidget
+from sohstationviewer.view.core.calendar_widget import CalendarWidget
 
 
 class Ui_CalendarDialog(object):
diff --git a/sohstationviewer/view/ui/main_ui.py b/sohstationviewer/view/ui/main_ui.py
index 8a6e102c08421d4056a46d27956cf9982158741c..ce350d4a11bf975e59714ec40f4079e203d5b6c8 100755
--- a/sohstationviewer/view/ui/main_ui.py
+++ b/sohstationviewer/view/ui/main_ui.py
@@ -2,9 +2,9 @@
 
 from PySide2 import QtCore, QtGui, QtWidgets
 
-from sohstationviewer.view.core.calendarwidget import CalendarWidget
+from sohstationviewer.view.core.calendar_widget import CalendarWidget
 
-from sohstationviewer.view.core.plottingWidget import PlottingWidget
+from sohstationviewer.view.core.plotting_widget import PlottingWidget
 from sohstationviewer.conf import constants
 
 
diff --git a/sohstationviewer/view/waveform_dialog.py b/sohstationviewer/view/waveform_dialog.py
new file mode 100755
index 0000000000000000000000000000000000000000..6a90104615c78f9275b9e1ffa77312b88a162faa
--- /dev/null
+++ b/sohstationviewer/view/waveform_dialog.py
@@ -0,0 +1,149 @@
+# UI and connectSignals for MainWindow
+
+from PySide2 import QtWidgets
+
+from sohstationviewer.view.core import plotting_widget as plottingWidget
+from sohstationviewer.model.handling_data import trim_downsample_WFChan
+
+from sohstationviewer.controller.plottingData import getTitle
+
+from sohstationviewer.database import extractData
+
+from sohstationviewer.conf.dbSettings import dbConf
+
+
+class WaveformWidget(plottingWidget.PlottingWidget):
+
+    def plot_channels(self, start_tm, end_tm, key,
+                      data_time, channel_list, time_ticks_total,
+                      plotting_data1, plotting_data2):
+        """
+        :param setID: (netID, statID, locID)
+        :param plottingData: a ditionary including:
+            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
+              channels:[cha: {netID, statID, locID, chanID, times, data,
+                              samplerate, startTmEpoch, endTmEpoch}       #
+              earliestUTC: the earliest time of all channels
+              latestUTC: the latest time of all channels
+        :timeTicksTotal: max number of tick to show on time bar
+
+        """
+        self.plotting_data1 = plotting_data1
+        self.plotting_data2 = plotting_data2
+        self.processing_log = []     # [(message, type)]
+        self.errors = []
+        if self.axes != []:
+            self.fig.clear()
+        self.date_mode = self.parent.date_format.upper()
+        self.time_ticks_total = time_ticks_total
+        self.min_x = self.curr_min_x = max(data_time[0], start_tm)
+        self.max_x = self.curr_max_x = min(data_time[1], end_tm)
+        self.plot_no = len(self.plotting_data1) + len(self.plotting_data2)
+        title = getTitle(key, self.min_x, self.max_x, self.date_mode)
+        self.plotting_bot = plottingWidget.BOTTOM
+        self.plotting_bot_pixel = plottingWidget.BOTTOM_PX
+        self.axes = []
+
+        self.timestamp_bar_top = self.add_timestamp_bar(0.003)
+        self.set_title(title)
+
+        for chanID in self.plotting_data1:
+            chan_db = extractData.getWFPlotInfo(chanID)
+            if chan_db['plotType'] == '':
+                continue
+            self.plotting_data1[chanID]['chan_db'] = chan_db
+            self.get_zoom_data(self.plotting_data1[chanID], chanID, True)
+        for chanID in self.plotting_data2:
+            chan_db = extractData.getChanPlotInfo(chanID,
+                                                  self.parent.data_type)
+            self.plotting_data2[chanID]['chan_db'] = chan_db
+            self.get_zoom_data(self.plotting_data2[chanID], chanID, True)
+
+        self.axes.append(self.plot_none())
+        self.timestamp_bar_bottom = self.add_timestamp_bar(0.003, top=False)
+        self.set_lim(org_size=True)
+        self.bottom = self.axes[-1].get_ybound()[0]
+        self.ruler = self.add_ruler(self.display_color['time_ruler'])
+        self.zoom_marker1 = self.add_ruler(self.display_color['zoom_marker'])
+        self.zoom_marker2 = self.add_ruler(self.display_color['zoom_marker'])
+        # Set view size fit with the given data
+        if self.widgt.geometry().height() < self.plotting_bot_pixel:
+            self.widgt.setFixedHeight(self.plotting_bot_pixel)
+
+        self.draw()
+
+    def get_zoom_data(self, c_data, chan_id, first_time=False):
+        """
+        :param setID: (netID, statID, locID)
+        :param plottingData: a ditionary including:
+            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
+              channels:{cha: {netID, statID, locID, chanID, times, data,
+                              samplerate, startTmEpoch, endTmEpoch}       #
+              earliestUTC: the earliest time of all channels
+              latestUTC: the latest time of all channels
+        :timeTicksTotal: max number of tick to show on time bar
+
+        Data set: {channelname: [(x,y), (x,y)...]
+        """
+        chan_db = c_data['chan_db']
+        plot_type = chan_db['plotType']
+        # data already processed for massposition in plottingWidget
+        if not (chan_id.startswith('VM') or chan_id.startswith('MP')):
+            trim_downsample_WFChan(
+                c_data, self.curr_min_x, self.curr_max_x, first_time)
+            self.apply_convert_factor(c_data, 1)
+        # use ax_wf because with massposition, ax has been used
+        # in plottingWidget
+        if 'ax_wf' not in c_data:
+            ax = getattr(self, dbConf['plotFunc'][plot_type][1])(
+                c_data, chan_db, chan_id, None, None)
+            if ax is None:
+                return
+            c_data['ax_wf'] = ax
+            ax.chan = chan_id
+            self.axes.append(ax)
+        else:
+            getattr(self, dbConf['plotFunc'][plot_type][1])(
+                c_data, chan_db, chan_id, c_data['ax_wf'], None)
+
+
+class WaveformDialog(QtWidgets.QWidget):
+    def __init__(self, parent):
+        super().__init__()
+        self.parent = parent
+        self.date_format = self.parent.dateFormat
+        self.bitweight_opt = self.parent.bitweightOpt
+        self.massPosVoltRangeOpt = self.parent.massPosVoltRangeOpt
+        self.setGeometry(300, 300, 1200, 700)
+        self.setWindowTitle("Raw Data Plot")
+
+        main_layout = QtWidgets.QVBoxLayout()
+        self.setLayout(main_layout)
+        main_layout.setContentsMargins(5, 5, 5, 5)
+        main_layout.setSpacing(0)
+
+        self.tracking_info_text_browser = QtWidgets.QTextBrowser(self)
+
+        self.plotting_widget = WaveformWidget(
+            self, self.tracking_info_text_browser, "waveformWidget")
+        main_layout.addWidget(self.plotting_widget, 2)
+
+        bottom_layout = QtWidgets.QHBoxLayout()
+        main_layout.addLayout(bottom_layout)
+
+        self.write_ps_button = QtWidgets.QPushButton('Write .ps', self)
+        bottom_layout.addWidget(self.write_ps_button)
+        self.tracking_info_text_browser.setFixedHeight(60)
+        bottom_layout.addWidget(self.tracking_info_text_browser)
+
+        self.connect_signals()
+
+    def set_data(self, data_type, file_name):
+        self.data_type = data_type
+        self.setWindowTitle("Raw Data Plot %s - %s" % (data_type, file_name))
+
+    def resizeEvent(self, event):
+        self.plotting_widget.init_size()
+
+    def connect_signals(self):
+        print("connectSignals")
diff --git a/sohstationviewer/view/waveformdialog.py b/sohstationviewer/view/waveformdialog.py
deleted file mode 100755
index be9b2d1761066539a782dd3c9f2478b56d6b3a8c..0000000000000000000000000000000000000000
--- a/sohstationviewer/view/waveformdialog.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# UI and connectSignals for MainWindow
-
-from PySide2 import QtWidgets
-
-from sohstationviewer.view.core import plottingWidget as plottingWidget
-from sohstationviewer.model.handling_data import trim_downsample_WFChan
-
-from sohstationviewer.controller.plottingData import getTitle
-
-from sohstationviewer.database import extractData
-
-from sohstationviewer.conf.dbSettings import dbConf
-
-
-class WaveformWidget(plottingWidget.PlottingWidget):
-
-    def plot_channels(self, startTm, endTm, key,
-                      dataTime, channelList, timeTicksTotal,
-                      plottingData1, plottingData2):
-        """
-        :param setID: (netID, statID, locID)
-        :param plottingData: a ditionary including:
-            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
-              channels:[cha: {netID, statID, locID, chanID, times, data,
-                              samplerate, startTmEpoch, endTmEpoch}       #
-              earliestUTC: the earliest time of all channels
-              latestUTC: the latest time of all channels
-        :timeTicksTotal: max number of tick to show on time bar
-
-        """
-        self.plottingData1 = plottingData1
-        self.plottingData2 = plottingData2
-        self.processingLog = []     # [(message, type)]
-        self.errors = []
-        if self.axes != []:
-            self.fig.clear()
-        self.dateMode = self.parent.dateFormat.upper()
-        self.timeTicksTotal = timeTicksTotal
-        self.minX = self.currMinX = max(dataTime[0], startTm)
-        self.maxX = self.currMaxX = min(dataTime[1], endTm)
-        self.plotNo = len(self.plottingData1) + len(self.plottingData2)
-        title = getTitle(key, self.minX, self.maxX, self.dateMode)
-        self.plottingBot = plottingWidget.BOTTOM
-        self.plottingBotPixel = plottingWidget.BOTTOM_PX
-        self.axes = []
-
-        self.timestampBarTop = self.add_timestamp_bar(0.003)
-        self.set_title(title)
-
-        for chanID in self.plottingData1:
-            chanDB = extractData.getWFPlotInfo(chanID)
-            if chanDB['plotType'] == '':
-                continue
-            self.plottingData1[chanID]['chanDB'] = chanDB
-            self.getZoomData(self.plottingData1[chanID], chanID, True)
-        for chanID in self.plottingData2:
-            chanDB = extractData.getChanPlotInfo(chanID, self.parent.dataType)
-            self.plottingData2[chanID]['chanDB'] = chanDB
-            self.getZoomData(self.plottingData2[chanID], chanID, True)
-
-        self.axes.append(self.plotNone())
-        self.timestampBarBottom = self.add_timestamp_bar(0.003, top=False)
-        self.set_lim(orgSize=True)
-        self.bottom = self.axes[-1].get_ybound()[0]
-        self.ruler = self.add_ruler(self.displayColor['time_ruler'])
-        self.zoomMarker1 = self.add_ruler(self.displayColor['zoom_marker'])
-        self.zoomMarker2 = self.add_ruler(self.displayColor['zoom_marker'])
-        # Set view size fit with the given data
-        if self.widgt.geometry().height() < self.plottingBotPixel:
-            self.widgt.setFixedHeight(self.plottingBotPixel)
-
-        self.draw()
-
-    def getZoomData(self, cData, chanID, firsttime=False):
-        """
-        :param setID: (netID, statID, locID)
-        :param plottingData: a ditionary including:
-            { gaps: [(t1,t2),(t1,t2),...]   (in epoch time)
-              channels:{cha: {netID, statID, locID, chanID, times, data,
-                              samplerate, startTmEpoch, endTmEpoch}       #
-              earliestUTC: the earliest time of all channels
-              latestUTC: the latest time of all channels
-        :timeTicksTotal: max number of tick to show on time bar
-
-        Data set: {channelname: [(x,y), (x,y)...]
-        """
-        chanDB = cData['chanDB']
-        plotType = chanDB['plotType']
-        # data already processed for massposition in plottingWidget
-        if not (chanID.startswith('VM') or chanID.startswith('MP')):
-            trim_downsample_WFChan(
-                cData, self.currMinX, self.currMaxX, firsttime)
-            self.applyConvertFactor(cData, 1)
-        # use ax_wf because with massposition, ax has been used
-        # in plottingWidget
-        if 'ax_wf' not in cData:
-            ax = getattr(self, dbConf['plotFunc'][plotType][1])(
-                cData, chanDB, chanID, None, None)
-            if ax is None:
-                return
-            cData['ax_wf'] = ax
-            ax.chan = chanID
-            self.axes.append(ax)
-        else:
-            getattr(self, dbConf['plotFunc'][plotType][1])(
-                cData, chanDB, chanID, cData['ax_wf'], None)
-
-
-class WaveformDialog(QtWidgets.QWidget):
-    def __init__(self, parent):
-        super().__init__()
-        self.parent = parent
-        self.dateFormat = self.parent.dateFormat
-        self.bitweightOpt = self.parent.bitweightOpt
-        self.massPosVoltRangeOpt = self.parent.massPosVoltRangeOpt
-        self.setGeometry(300, 300, 1200, 700)
-        self.setWindowTitle("Raw Data Plot")
-
-        mainLayout = QtWidgets.QVBoxLayout()
-        self.setLayout(mainLayout)
-        mainLayout.setContentsMargins(5, 5, 5, 5)
-        mainLayout.setSpacing(0)
-
-        self.trackingInfoTextBrowser = QtWidgets.QTextBrowser(self)
-
-        self.plottingWidget = WaveformWidget(
-            self, self.trackingInfoTextBrowser, "waveformWidget")
-        mainLayout.addWidget(self.plottingWidget, 2)
-
-        bottomLayout = QtWidgets.QHBoxLayout()
-        mainLayout.addLayout(bottomLayout)
-
-        self.writePSButton = QtWidgets.QPushButton('Write .ps', self)
-        bottomLayout.addWidget(self.writePSButton)
-        self.trackingInfoTextBrowser.setFixedHeight(60)
-        bottomLayout.addWidget(self.trackingInfoTextBrowser)
-
-        self.connectSignals()
-
-    def setData(self, dataType, fileName):
-        self.dataType = dataType
-        self.setWindowTitle("Raw Data Plot %s - %s" % (dataType, fileName))
-
-    def resizeEvent(self, event):
-        self.plottingWidget.init_size()
-
-    def connectSignals(self):
-        print("connectSignals")