Skip to content
Snippets Groups Projects
Commit 638508ca authored by JOSSOUD Olivier's avatar JOSSOUD Olivier
Browse files

Explo. Color, offset, mult_factor, timeshift and X/Y original OK.

parent 271f22d0
No related branches found
No related tags found
No related merge requests found
......@@ -61,17 +61,17 @@
</column>
<column>
<property name="text">
<string>Vertical line</string>
<string>Step curve</string>
</property>
</column>
<column>
<property name="text">
<string>Cursor</string>
<string>Vertical line</string>
</property>
</column>
<column>
<property name="text">
<string>Curve</string>
<string>Cursor</string>
</property>
</column>
<column>
......@@ -91,12 +91,22 @@
</column>
<column>
<property name="text">
<string>Time shift</string>
<string>Time shift (s)</string>
</property>
</column>
<column>
<property name="text">
<string>Visible</string>
</property>
</column>
<column>
<property name="text">
<string>X (orig.)</string>
</property>
</column>
<column>
<property name="text">
<string>Delete</string>
<string>Y (orig.)</string>
</property>
</column>
</widget>
......
......@@ -61,7 +61,7 @@ class Dataset:
# Get dataset name
self.dataset_text = directory_name[-9:]
self.last_data_datetime = datetime.datetime(1970, 1, 1)
self.last_data_datetime = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
self.instlogs = {}
......@@ -118,6 +118,7 @@ class InstrumentInstantLog(InstrumentLog):
def __get_df__(self) -> pd.DataFrame:
df = pd.read_csv(self.full_file_name, sep=",", parse_dates=["datetime"])
df["datetime"] = df["datetime"].dt.tz_localize('UTC')
return df
def get_variables(self):
......@@ -125,7 +126,6 @@ class InstrumentInstantLog(InstrumentLog):
def get_timeseries(self, variable: str) -> pd.DataFrame:
timeseries_df = self.df[self.df["name"] == variable]
timeseries_df.rename(columns={'value': variable}, inplace=True)
timeseries_df = timeseries_df.drop(columns=['name'])
return timeseries_df
......@@ -137,6 +137,7 @@ class InstrumentPeriodicLog(InstrumentLog):
def __get_df__(self) -> pd.DataFrame:
df = pd.read_csv(self.full_file_name, sep="\t", parse_dates=["datetime"])
df["datetime"] = df["datetime"].dt.tz_localize('UTC')
return df
def get_variables(self):
......@@ -146,4 +147,5 @@ class InstrumentPeriodicLog(InstrumentLog):
def get_timeseries(self, variable: str) -> pd.DataFrame:
timeseries_df = self.df[["datetime", variable]]
timeseries_df.rename(columns={variable: 'value'}, inplace=True)
return timeseries_df
......@@ -25,7 +25,7 @@ class Ui_MainWindow(object):
self.explo_tablewidget_variables = QtWidgets.QTableWidget(self.tab_explo)
self.explo_tablewidget_variables.setGeometry(QtCore.QRect(20, 50, 1021, 201))
self.explo_tablewidget_variables.setObjectName("explo_tablewidget_variables")
self.explo_tablewidget_variables.setColumnCount(10)
self.explo_tablewidget_variables.setColumnCount(12)
self.explo_tablewidget_variables.setRowCount(0)
item = QtWidgets.QTableWidgetItem()
self.explo_tablewidget_variables.setHorizontalHeaderItem(0, item)
......@@ -47,6 +47,10 @@ class Ui_MainWindow(object):
self.explo_tablewidget_variables.setHorizontalHeaderItem(8, item)
item = QtWidgets.QTableWidgetItem()
self.explo_tablewidget_variables.setHorizontalHeaderItem(9, item)
item = QtWidgets.QTableWidgetItem()
self.explo_tablewidget_variables.setHorizontalHeaderItem(10, item)
item = QtWidgets.QTableWidgetItem()
self.explo_tablewidget_variables.setHorizontalHeaderItem(11, item)
self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.tab_explo)
self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(20, 10, 221, 31))
self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
......@@ -183,11 +187,11 @@ class Ui_MainWindow(object):
item = self.explo_tablewidget_variables.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "Variable"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Vertical line"))
item.setText(_translate("MainWindow", "Step curve"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Cursor"))
item.setText(_translate("MainWindow", "Vertical line"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(4)
item.setText(_translate("MainWindow", "Curve"))
item.setText(_translate("MainWindow", "Cursor"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(5)
item.setText(_translate("MainWindow", "Color"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(6)
......@@ -195,9 +199,13 @@ class Ui_MainWindow(object):
item = self.explo_tablewidget_variables.horizontalHeaderItem(7)
item.setText(_translate("MainWindow", "Mult. factor"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(8)
item.setText(_translate("MainWindow", "Time shift"))
item.setText(_translate("MainWindow", "Time shift (s)"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(9)
item.setText(_translate("MainWindow", "Delete"))
item.setText(_translate("MainWindow", "Visible"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(10)
item.setText(_translate("MainWindow", "X (orig.)"))
item = self.explo_tablewidget_variables.horizontalHeaderItem(11)
item.setText(_translate("MainWindow", "Y (orig.)"))
self.explo_label_dataset.setText(_translate("MainWindow", "Dataset:"))
self.explo_pushbutton_add_row.setText(_translate("MainWindow", "+ Add Row"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_explo), _translate("MainWindow", "Data exploration"))
......
import datetime
import pyqtgraph as pg
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QColor
from PyQt5.QtCore import *
import pandas as pd
import numpy as np
from pyqtgraph.GraphicsScene.mouseEvents import MouseClickEvent
import utils
from config import Config
......@@ -16,11 +18,26 @@ class ExploUim:
# "Variables" tableWidget columns identifiers.
INSTRUMENT_COL = 0
VARIABLE_COL = 1
VLINE_COL = 2
CURSOR_COL = 3
CURVE_COL = 4
COLOR_COL = 5
DELETE_COL = 6
CURVE_COL = 2
VLINE_COL = 3
CURSOR_COL = 4
COLOR_COL = 5
OFFSET_COL = 6
MULT_COL = 7
TIMESHIFT_COL = 8
VISIBLE_COL = 9
XORIG_COL = 10
YORIG_COL = 11
DEFAULT_COLORS = [QColor(255, 255, 255), # White
QColor(228, 26, 28), # Red
QColor(55, 126, 184), # Blue
QColor(77, 175, 74), # Green
QColor(152, 78, 163), # Violet
QColor(255, 127, 0), # Orange
QColor(255, 255, 51), # Yellow
QColor(166, 86, 40), # Brown
QColor(247, 129, 191)] # Pink
def __init__(self, explo_prvd: ExploProvider, main_ui: Ui_MainWindow, config: Config):
self.main_ui = main_ui
......@@ -29,10 +46,17 @@ class ExploUim:
self.current_dataset = None
# The "var_id" is used to identify and match table's lines and plot's items (curves, etc.). Table's row id can
# not be used because it changes when previous rows are deleted.
self.current_variable_id = 0
self.__initialize_variable_table__()
self.__initialize_plot__()
self.__initialize_dataset_combobox__()
self.main_ui.explo_pushbutton_add_row.clicked.connect(self.__add_new_row_in_variable_table__)
self.main_ui.explo_tablewidget_variables.cellClicked.connect(self.__change_color__)
def __initialize_dataset_combobox__(self):
"""Populate the "datasets" combobox with the existing dataset directory names."""
data_root_dir = self.config.read("DATA_SOURCE", "absolute_root_dir")
......@@ -45,7 +69,8 @@ class ExploUim:
def __update_current_dataset__(self, dataset_dir: str):
self.current_dataset = self.explo_prvd.datasets[dataset_dir]
self.__update_instruments_comboboxes__()
for row_id in range(self.main_ui.explo_tablewidget_variables.rowCount()):
self.__update_instruments_combobox__(row_id)
####################################################################################################################
# "Variables" table
......@@ -54,58 +79,100 @@ class ExploUim:
"""Initialize the table containing the to-be-displayed variables"""
# Set column widths
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.INSTRUMENT_COL, 90)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.INSTRUMENT_COL, 110)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.VARIABLE_COL, 90)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.CURVE_COL, 85)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.VLINE_COL, 95)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.CURSOR_COL, 80)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.CURVE_COL, 80)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.COLOR_COL, 80)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.DELETE_COL, 80)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.CURSOR_COL, 60)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.COLOR_COL, 55)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.OFFSET_COL, 90)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.MULT_COL, 90)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.TIMESHIFT_COL, 95)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.VISIBLE_COL, 70)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.XORIG_COL, 80)
self.main_ui.explo_tablewidget_variables.setColumnWidth(self.YORIG_COL, 70)
self.__add_new_row_in_variable_table__()
def __add_new_row_in_variable_table__(self):
"""Add a new row in the calibration table. Initialize the cell's content and properties."""
row_id = self.main_ui.explo_tablewidget_variables.rowCount()
self.main_ui.explo_tablewidget_variables.insertRow(row_id)
table = self.main_ui.explo_tablewidget_variables
row_id = table.rowCount()
table.insertRow(row_id)
# Instruments
instrument_item = QComboBox()
self.main_ui.explo_tablewidget_variables.setCellWidget(row_id, self.INSTRUMENT_COL, instrument_item)
self.main_ui.explo_tablewidget_variables.cellWidget(row_id, self.INSTRUMENT_COL).currentTextChanged.connect(
table.setCellWidget(row_id, self.INSTRUMENT_COL, instrument_item)
table.cellWidget(row_id, self.INSTRUMENT_COL).currentTextChanged.connect(
lambda text, row_id=row_id: self.__update_variables_combobox__(combobox_text=text, row=row_id))
# Variables
variable_item = QComboBox()
self.main_ui.explo_tablewidget_variables.setCellWidget(row_id, self.VARIABLE_COL, variable_item)
self.main_ui.explo_tablewidget_variables.cellWidget(row_id, self.VARIABLE_COL).currentTextChanged.connect(
lambda text, row_id=row_id: self.__apply_new_variable__(variable_name=text, row=row_id))
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))
# Step Curve
curve_item = QCheckBox()
table.setCellWidget(row_id, self.CURVE_COL, curve_item)
# Vertical line
vline_item = QCheckBox()
self.main_ui.explo_tablewidget_variables.setCellWidget(row_id, self.VLINE_COL, vline_item)
table.setCellWidget(row_id, self.VLINE_COL, vline_item)
# Cursor
cursor_item = QCheckBox()
self.main_ui.explo_tablewidget_variables.setCellWidget(row_id, self.CURSOR_COL, cursor_item)
# Curve
curve_item = QCheckBox()
self.main_ui.explo_tablewidget_variables.setCellWidget(row_id, self.CURVE_COL, curve_item)
table.setCellWidget(row_id, self.CURSOR_COL, cursor_item)
# Color
color_item = QTableWidgetItem()
self.main_ui.explo_tablewidget_variables.setItem(row_id, self.COLOR_COL, color_item)
# Delete
delete_item = QPushButton("Delete")
self.main_ui.explo_tablewidget_variables.setCellWidget(row_id, self.DELETE_COL, delete_item)
def __update_instruments_comboboxes__(self):
instrument_combobox = self.main_ui.explo_tablewidget_variables.cellWidget(0, self.INSTRUMENT_COL) # TODO: ALL rows!!
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(-1000.0)
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(-1000.0)
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(-1000.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)
# X original
xorig_item = QTableWidgetItem()
table.setItem(row_id, self.XORIG_COL, xorig_item)
# Y original
yorig_item = QTableWidgetItem()
table.setItem(row_id, self.YORIG_COL, yorig_item)
self.__update_instruments_combobox__(row_id)
def __update_instruments_combobox__(self, row_id: int):
instrument_combobox = self.main_ui.explo_tablewidget_variables.cellWidget(row_id, self.INSTRUMENT_COL)
instrument_combobox.clear()
dataset_dir = self.main_ui.explo_combobox_dataset.currentText()
if dataset_dir == "":
return
dataset = self.explo_prvd.datasets[dataset_dir]
instrument_logs = dataset.instlogs
......@@ -123,23 +190,74 @@ class ExploUim:
for variable_name in variable_names:
variables_combobox.addItem(variable_name)
def __apply_new_variable__(self, variable_name: str, row: int):
# The variable combobox is cleard before variables of the newly-selected instrument are written.
# This function is called on variable-combobox edition, thus it is also called on combobox clear.
# So in this case, do nothing, wait for the call related to the filling of the combobox
if 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)
def __get_row_dataframe__(self, row_id: int) -> pd.DataFrame:
table = self.main_ui.explo_tablewidget_variables
# Get instrument log
instrument_log_name = self.main_ui.explo_tablewidget_variables.cellWidget(row, self.INSTRUMENT_COL).currentText()
instrument_log_name = table.cellWidget(row_id, self.INSTRUMENT_COL).currentText()
instrument_log = self.current_dataset.instlogs[instrument_log_name]
# Get variable name
variable_name = table.cellWidget(row_id, self.VARIABLE_COL).currentText()
timeseries = instrument_log.get_timeseries(variable_name)
return timeseries
def __apply_variable_change__(self, row_id: int, variable_name: str = None) -> None:
"""Read the information related to the row_id and call plot function
Parameters
----------
row_id
variable_name
"""
# The variable combobox is cleared before variables of the newly-selected instrument are written.
# This function is called on variable-combobox edition, thus it is also called on combobox clear.
# So in this case, do nothing, wait for the call related to the filling of the combobox
if variable_name == "":
return
table = self.main_ui.explo_tablewidget_variables
timeseries = self.__get_row_dataframe__(row_id)
# Get color
color = table.item(row_id, self.COLOR_COL).background().color()
# Get position adjustment variables (offset, multiplicative factor and time shift)
offset = table.cellWidget(row_id, self.OFFSET_COL).value()
mult = table.cellWidget(row_id, self.MULT_COL).value()
timeshift = table.cellWidget(row_id, self.TIMESHIFT_COL).value()
try:
self.__update_plot__(timeseries, variable_name)
self.__update_plot__(timeseries, row_id, color, offset, mult, timeshift)
except TypeError:
print("Cannot plot that.")
def __update_xy_original__(self, instant: datetime.datetime):
table = self.main_ui.explo_tablewidget_variables
for row_id in range(table.rowCount()):
# Get X orig (original non-shifted datetime value)
timeshift_sec = table.cellWidget(row_id, self.TIMESHIFT_COL).value()
x_orig = instant - datetime.timedelta(seconds=timeshift_sec)
table.item(row_id, self.XORIG_COL).setText(x_orig.strftime("%H:%M:%S.%f")[:-5])
# Get Y orig (original non-shifted variable value)
df = self.__get_row_dataframe__(row_id).copy()
y_orig = df[df["datetime"] <= instant].iloc[-1]["value"]
if isinstance(y_orig, float):
y_orig = "{:.4f}".format(y_orig)
table.item(row_id, self.YORIG_COL).setText(y_orig)
####################################################################
# Plot
......@@ -147,25 +265,61 @@ class ExploUim:
self.plot_item = pg.PlotItem(axisItems={'bottom': utils.TimeAxisItem(orientation='bottom')})
self.curve = pg.PlotCurveItem()
self.plot_item.addItem(self.curve)
self.step_curves = dict()
self.main_ui.explo_graphicsview_top.setCentralItem(self.plot_item)
self.lines_items = []
def __update_plot__(self, timeseries: pd.DataFrame, variable_name: str) -> None:
# Vertical line following the cursor
self.cursor_vline = pg.InfiniteLine(angle=90, movable=False)
self.plot_item.addItem(self.cursor_vline, ignoreBounds=True)
def __update_plot__(self, timeseries: pd.DataFrame, row_id: int, color: QColor,
offset: float, mult: float, timeshift_sec: float) -> None:
try:
timeseries[variable_name] = timeseries[variable_name].astype(float)
timeseries["value"] = timeseries["value"].astype(float)
except ValueError:
print("Value is not convertible to float")
return
# Get the to-be-modified curve
if row_id in self.step_curves:
step_curve = self.step_curves[row_id]
else:
last_datetime = self.current_dataset.last_data_datetime + datetime.timedelta(seconds=1)
timeseries['datetime'].at[max(timeseries.index)+1] = last_datetime
self.curve.setData(x=utils.pd_time_to_epoch_ms(timeseries['datetime']),
y=list(timeseries[variable_name]),
pen=(200, 200, 200),
stepMode=True,
symbol="o",
symbolBrush=(255, 0, 0),
symbolPen='w')
step_curve = pg.PlotCurveItem()
self.plot_item.addItem(step_curve)
step_curve.scene().sigMouseClicked.connect(lambda event: self.__mouse_clicked__(event))
self.step_curves[row_id] = step_curve
# As it is a _step_ curve, add a last datetime point to determine the end of the last step. This is the datatime
# of the last available data of the dataset, plus one second.
last_datetime = self.current_dataset.last_data_datetime + datetime.timedelta(seconds=1)
x_values = timeseries['datetime'].copy()
x_values = x_values.append(pd.Series([last_datetime]))
# Apply time shift
x_values = x_values + datetime.timedelta(seconds=timeshift_sec)
y_values = list(timeseries["value"])
# Apply multiplicative factor
y_values = [y * mult for y in y_values]
# Apply Y-axis offset
y_values = [y + offset for y in y_values]
# Set data to the plot
step_curve.setData(x=utils.pd_time_to_epoch_ms(x_values),
y=y_values,
pen=color,
stepMode=True)
def __mouse_clicked__(self, event: MouseClickEvent):
pos = event.scenePos()
mousePoint = self.step_curves[0].getViewBox().mapSceneToView(pos)
instant = datetime.datetime.fromtimestamp(mousePoint.x(), tz=datetime.timezone.utc)
# instant = datetime.datetime.fromtimestamp(mousePoint.x())
self.__update_xy_original__(instant)
self.cursor_vline.setPos(mousePoint.x())
# print("x="+instant.strftime("%H:%M:%S"))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment