import datetime import warnings import pyqtgraph as pg import scipy.optimize.optimize from PyQt5.QtWidgets import * from PyQt5.QtGui import QColor from PyQt5.QtCore import * import pandas as pd import pytz from pandas.api.types import is_numeric_dtype from pyqtgraph.GraphicsScene.mouseEvents import MouseClickEvent import utils from config import Config from dataprovider.exploprovider import ExploProvider from uim.explouim import ExploUim from gui.ui_mainwindow import Ui_MainWindow from gui.icbktransition_dialog import IcbktransitionDialog from cfatools.logreader.dataset import DatasetReader class IcbktransitionUim: # "Variables" tableWidget columns identifiers. ID_COL = 0 NAME_COL = 1 START_COL = 2 END_COL = 3 HEIGHT_COL = 4 PROBES_COLORS = {"C1": QColor(228, 26, 28), # Red "C2": QColor(55, 126, 184), # Blue "C3": QColor(77, 175, 74), # Green "C4": QColor(152, 78, 163), # Violet "C5": QColor(255, 127, 0)} # Orange # QColor(255, 255, 51), # Yellow # QColor(166, 86, 40), # Brown # QColor(247, 129, 191)} # Pink PROBES = ["C1", "C2", "C3", "C4", "C5"] def __init__(self, explo_prvd: ExploProvider, explo_uim: ExploUim, main_ui: Ui_MainWindow, config: Config, icbktransition_dialog: IcbktransitionDialog): self.main_ui = main_ui self.explo_prvd = explo_prvd self.explo_uim = explo_uim self.config = config self.icbktransition_dialog = icbktransition_dialog self.icbktransition_ui = self.icbktransition_dialog.ui self.dataset_readers = dict() self.current_dataset = None self.__init_plot__() self.main_ui.explo_pushbutton_icbktransition.clicked.connect(self.__show_icbktransition_dialog__) self.icbktransition_ui.timeedit_reftime.dateTimeChanged.connect(self.__update_plot__) def __show_icbktransition_dialog__(self): self.current_dataset = self.explo_uim.current_dataset self.conduct_timeseries = self.current_dataset.get_timeseries("CONDUCTI_periodic").copy() self.__init_reftime__() self.__update_plot__() self.icbktransition_dialog.show() #################################################################################################################### # "Transition" table def __initialize_transition_table__(self): # Set column widths self.icbktransition_ui.tablewidget_icbk_list.setColumnWidth(self.ID_COL, 150) self.icbktransition_ui.tablewidget_icbk_list.setColumnWidth(self.NAME_COL, 130) self.icbktransition_ui.tablewidget_icbk_list.setColumnWidth(self.START_COL, 55) self.icbktransition_ui.tablewidget_icbk_list.setColumnWidth(self.END_COL, 100) self.icbktransition_ui.tablewidget_icbk_list.setColumnWidth(self.HEIGHT_COL, 120) def __add_new_row_in_variable_table__(self): """Add a new row in the calibration table. Initialize the cell's content and properties.""" table = self.main_ui.explo_tablewidget_variables row_id = table.rowCount() table.insertRow(row_id) # Instruments instrument_item = QComboBox() table.setCellWidget(row_id, self.INSTRUMENT_COL, instrument_item) # Variables variable_item = QComboBox() table.setCellWidget(row_id, self.VARIABLE_COL, variable_item) table.cellWidget(row_id, self.VARIABLE_COL).currentTextChanged.connect( lambda text, row_id=row_id: self.__apply_variable_change__(row_id=row_id, variable_name=text)) # Connect Instrument change to variables display table.cellWidget(row_id, self.INSTRUMENT_COL).currentTextChanged.connect( lambda text, row_id=row_id: self.__update_variables_combobox__(combobox_text=text, variables_combobox=variable_item)) # Color color_item = QTableWidgetItem() color_item.setBackground(self.DEFAULT_COLORS[row_id % len(self.DEFAULT_COLORS)]) table.setItem(row_id, self.COLOR_COL, color_item) # Offset offset_item = QDoubleSpinBox() offset_item.setMinimum(-10000.0) offset_item.setMaximum(10000.0) offset_item.setDecimals(3) table.setCellWidget(row_id, self.OFFSET_COL, offset_item) table.cellWidget(row_id, self.OFFSET_COL).valueChanged.connect( lambda value, row_id=row_id: self.__apply_variable_change__(row_id=row_id)) # Multiplicative factor mult_item = QDoubleSpinBox() mult_item.setValue(1.0) mult_item.setMinimum(-10000.0) mult_item.setMaximum(10000.0) mult_item.setDecimals(5) table.setCellWidget(row_id, self.MULT_COL, mult_item) table.cellWidget(row_id, self.MULT_COL).valueChanged.connect( lambda value, row_id=row_id: self.__apply_variable_change__(row_id=row_id)) # Time shift timeshift_item = QDoubleSpinBox() timeshift_item.setMinimum(-10000.0) timeshift_item.setMaximum(10000.0) table.setCellWidget(row_id, self.TIMESHIFT_COL, timeshift_item) table.cellWidget(row_id, self.TIMESHIFT_COL).valueChanged.connect( lambda value, row_id=row_id: self.__apply_variable_change__(row_id=row_id)) # Visible visible_item = QCheckBox() visible_item.setChecked(True) table.setCellWidget(row_id, self.VISIBLE_COL, visible_item) table.cellWidget(row_id, self.VISIBLE_COL).stateChanged.connect( lambda state, row_id=row_id: self.__apply_variable_change__(row_id=row_id)) # X original xorig_item = QTableWidgetItem() xorig_item.setFlags(Qt.ItemIsEnabled) # Read only table.setItem(row_id, self.XORIG_COL, xorig_item) # Y original yorig_item = QTableWidgetItem() yorig_item.setFlags(Qt.ItemIsEnabled) # Read only table.setItem(row_id, self.YORIG_COL, yorig_item) self.__update_instruments_combobox__(self.main_ui.explo_tablewidget_variables.cellWidget(row_id, self.INSTRUMENT_COL)) if hasattr(self, "plot_item"): self.plot_item.getViewBox().enableAutoRange(enable=True) def __update_instruments_combobox__(self, combobox: QComboBox): combobox.clear() for instrument_name in self.current_dataset.get_instruments_names(): if instrument_name == "manual-event": continue combobox.addItem(instrument_name) def __update_variables_combobox__(self, combobox_text: str, variables_combobox: QComboBox): if combobox_text == "": return variables_combobox.clear() instrument_name = combobox_text variable_names = self.explo_prvd.get_instrument_variables(self.current_dataset, instrument_name) for variable_name in variable_names: variables_combobox.addItem(variable_name) def __change_color__(self, row: int, column: int): if column != self.COLOR_COL: return color = QColorDialog.getColor() self.main_ui.explo_tablewidget_variables.item(row, self.COLOR_COL).setBackground(color) self.__apply_variable_change__(row) def __get_row_dataframe__(self, row_id: int) -> pd.DataFrame: table = self.main_ui.explo_tablewidget_variables # Get instrument log instrument_name = table.cellWidget(row_id, self.INSTRUMENT_COL).currentText() variable_name = table.cellWidget(row_id, self.VARIABLE_COL).currentText() timeseries = self.explo_prvd.get_timeseries(self.current_dataset, instrument_name, variable_name) return timeseries #################################################################### # Plot def __mouse_clicked__(self, event: MouseClickEvent) -> None: """Function triggered when the user clicks on the plot. Display a vertical line under the mouse click and call function ``__update_xy_original__`` Parameters ---------- event: pyqtgraph.MouseClickEvent """ pos = event.scenePos() mouse_point = self.step_curves[0].getViewBox().mapSceneToView(pos) instant = datetime.datetime.fromtimestamp(mouse_point.x(), tz=datetime.timezone.utc) self.measure_vline.setPos(mouse_point.x()) self.__update_xy_original__(instant) def __mouse_moved__(self, pos: QPointF) -> None: """Function triggered when the user's mouse cursor hovers over the plot. Display a vertical line where the cursor is, and update time shift with click-fixed line, if any. Parameters ---------- pos: PyQt5.QtCore.QPointF """ mouse_point = self.step_curves[0].getViewBox().mapSceneToView(pos) self.cursor_vline.setPos(mouse_point.x()) if self.measure_vline.getPos() != [0, 0]: timeshift_s = abs(self.measure_vline.getPos()[0] - self.cursor_vline.getPos()[0]) self.main_ui.explo_label_timeshift.setText("Time shift: " + "{:.2f}".format(timeshift_s) + "s") def __init_plot__(self): self.plot_item = pg.PlotItem(axisItems={'bottom': utils.TimeAxisItem(orientation='bottom')}) self.icbktransition_dialog.ui.graphicsview.setCentralItem(self.plot_item) # Conduct curves self.conduct_curves = dict() for probe in self.PROBES: self.conduct_curves[probe] = pg.PlotCurveItem() self.plot_item.addItem(self.conduct_curves[probe]) # Vertical lines for the original iceblocks transition self.originaltransition_vlines = [] self.originaltransition_texts = [] # Vertical lines for the original iceblocks transition self.newtransition_vlines = [] self.newtransition_texts = [] # Vertical line following the cursor self.cursor_vline = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen(style=Qt.DotLine)) self.plot_item.addItem(self.cursor_vline, ignoreBounds=True) def __update_plot__(self): # Set data to step curve x_values = utils.pd_time_to_epoch_ms(self.conduct_timeseries.index) x_values.append(x_values[-1] + 1) for probe in self.PROBES: y_values = list(self.__shift_to_reftime__(self.conduct_timeseries[probe])) self.conduct_curves[probe].setData(x=x_values, y=y_values, pen=self.PROBES_COLORS[probe], stepMode=True) def __shift_to_reftime__(self, series: pd.Series) -> pd.Series: timezone = pytz.timezone("UTC") reftime = self.icbktransition_ui.timeedit_reftime.dateTime().toPyDateTime() reftime = timezone.localize(reftime) value_at_reftime = series.iloc[series.index.get_loc(reftime, method='nearest')] series = series - value_at_reftime return series def __init_reftime__(self): min_reftime = min(self.conduct_timeseries.index) self.icbktransition_ui.timeedit_reftime.blockSignals(True) self.icbktransition_ui.timeedit_reftime.setMinimumDateTime(min_reftime) self.icbktransition_ui.timeedit_reftime.setMaximumDateTime(max(self.conduct_timeseries.index)) self.icbktransition_ui.timeedit_reftime.setDateTime(min_reftime + datetime.timedelta(minutes=20)) self.icbktransition_ui.timeedit_reftime.blockSignals(False)