Newer
Older
JOSSOUD Olivier
committed
import datetime
import os
import pyqtgraph as pg
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import pandas as pd
import numpy as np
JOSSOUD Olivier
committed
import utils
JOSSOUD Olivier
committed
from uim.picarrocalibuim import PicarroCalibUim
JOSSOUD Olivier
committed
JOSSOUD Olivier
committed
class PicarroInjectionUim(QObject):
JOSSOUD Olivier
committed
"""Class managing the 'Injections identification' sub-tab of the 'Picarro calibration' tab."""
JOSSOUD Olivier
committed
def __init__(self, picarrocalib_uim: PicarroCalibUim):
super(PicarroInjectionUim, self).__init__()
self.main_ui = picarrocalib_uim.main_ui
self.picarro_prvd = picarrocalib_uim.picarro_prvd
JOSSOUD Olivier
committed
self.__init_tablewidget_dimensions__()
# Injection's properties
self.injection_prop_dict = dict()
self.mean_sd_dict = dict()
# Connect picarro_prvd signals
self.picarro_prvd.sig_picarro_df_updated.connect(self.create_plot)
self.picarro_prvd.sig_picarro_df_updated.connect(self.update_stdname_combobox)
self.picarro_prvd.sig_stdtrue_df_updated.connect(self.update_stdname_combobox)
self.picarro_prvd.sig_calibs_df_updated.connect(
self.__update_calib_lines_data__
)
# Connect UI's buttons
self.main_ui.picarro_pushbutton_addtofile.clicked.connect(
self.__append_to_calibration_file
)
self.main_ui.picarro_pushbutton_stdname_suggestion_apply.clicked.connect(
self.apply_std_suggestion
)
JOSSOUD Olivier
committed
def __init_tablewidget_dimensions__(self):
table = self.main_ui.picarro_tablewidget
table.verticalHeader().setStyleSheet("QHeaderView { font-size: 10pt; }")
table.horizontalHeader().setStyleSheet("QHeaderView { font-size: 10pt; }")
table.verticalHeader().setFixedWidth(50) # Width of the Row header
table.setColumnWidth(0, 99)
table.setColumnWidth(1, 98)
table.horizontalHeader().setFixedHeight(24) # Height of the Column header
table.setRowHeight(0, 32)
table.setRowHeight(1, 32)
table.setRowHeight(2, 32)
####################################################################
JOSSOUD Olivier
committed
# Standard "true value" management
JOSSOUD Olivier
committed
def update_stdname_combobox(self):
JOSSOUD Olivier
committed
# Populate the standard's name combobox
std_names = self.picarro_prvd.stdtrue_df["name"]
JOSSOUD Olivier
committed
self.main_ui.picarro_combobox_stdname_user.clear()
self.main_ui.picarro_combobox_stdname_user.addItems(std_names)
def apply_std_suggestion(self):
std_sugg = self.main_ui.picarro_lineedit_stdname_suggestion.text()
index = self.main_ui.picarro_combobox_stdname_user.findText(std_sugg)
if index != -1: # Value not found
self.main_ui.picarro_combobox_stdname_user.setCurrentIndex(index)
self.main_ui.picarro_lineedit_stdname_suggestion.setStyleSheet(
"color: 'black';"
)
JOSSOUD Olivier
committed
####################################################################
# Calibration file management
def __append_to_calibration_file(self):
filename = self.main_ui.picarro_lineedit_calib_filename.text()
if filename == "":
utils.show_popup("Output calibration file path not set!")
return
self.injection_prop_dict[
"standard"
] = self.main_ui.picarro_combobox_stdname_user.currentText()
self.injection_prop_dict[
"dataset"
] = self.main_ui.picarro_lineedit_dataset.text()
JOSSOUD Olivier
committed
# Convert dict to DataFrame
parameters_df = pd.DataFrame([self.injection_prop_dict])
# Add 'keep' column. Injection are valid by default.
parameters_df["keep"] = True
JOSSOUD Olivier
committed
# Reorder dataframe's columns
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
parameters_df = parameters_df[
parameters_df.columns.reindex(
[
"dataset",
"standard",
"plateau_start",
"plateau_end",
"plateau_duration",
"valid_start",
"valid_end",
"valid_duration",
"H2O_mean",
"H2O_std",
"Delta_18_16_mean",
"Delta_18_16_std",
"Delta_D_H_mean",
"Delta_D_H_std",
"keep",
]
)[0]
]
parameters_df.to_csv(
filename,
sep="\t",
mode="a",
index=False,
header=(not os.path.exists(filename) or os.stat(filename).st_size == 0),
)
JOSSOUD Olivier
committed
self.picarro_prvd.update_calib_df(filename)
JOSSOUD Olivier
committed
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setWindowTitle("Injection added")
msg.setText(
"The injection's parameter has successfully been added in calibration file."
)
JOSSOUD Olivier
committed
msg.exec_()
####################################################################
# Plot
def create_plot(self, picarro_df: pd.DataFrame):
JOSSOUD Olivier
committed
# Create a layout
layout = pg.GraphicsLayout()
layout.layout.setSpacing(0.0)
layout.setContentsMargins(0.0, 0.0, 0.0, 0.0)
JOSSOUD Olivier
committed
# Add it to the graphics view
self.main_ui.picarro_graphicsview_injections.setCentralItem(layout)
# Create plot items and add them to the layout
# self.set_plots(layout)
plot_h2o = self.__get_plot_item__(picarro_df, layout, 0, "H2O")
plot_delta_18_16 = self.__get_plot_item__(picarro_df, layout, 1, "Delta_18_16")
JOSSOUD Olivier
committed
plot_delta_18_16.setXLink(plot_h2o)
self.__add_second_curve__(picarro_df, layout, 1, "Delta_D_H")
JOSSOUD Olivier
committed
self.calib_line_item = pg.PlotCurveItem(pen=pg.mkPen("g"))
plot_delta_18_16.addItem(self.calib_line_item)
self.__update_calib_lines_data__()
JOSSOUD Olivier
committed
# Get the main windows view box
main_range = plot_delta_18_16.getViewBox().viewRange()
x_min = main_range[0][0]
x_max = main_range[0][1]
# Add "plateau" region
self.plateau_region = pg.LinearRegionItem(
brush=pg.mkBrush(color=[0, 0, 255, 40])
)
JOSSOUD Olivier
committed
self.plateau_region.setZValue(10)
self.plateau_region.setRegion([x_min, x_min + 15 * 60])
self.plateau_region.sigRegionChanged.connect(
self.__update_plateau_characteristics__
)
JOSSOUD Olivier
committed
self.plateau_region.sigRegionChanged.connect(self.__adjust_regions__)
plot_delta_18_16.addItem(self.plateau_region, ignoreBounds=True)
# Add "valid area" region
self.validarea_region = pg.LinearRegionItem(
brush=pg.mkBrush(color=[0, 255, 0, 40])
)
self.validarea_region.setZValue(11)
JOSSOUD Olivier
committed
self.validarea_region.setRegion([x_min + 10 * 60, x_min + 15 * 60])
self.validarea_region.sigRegionChanged.connect(
self.__update_validarea_characteristics__
)
JOSSOUD Olivier
committed
self.validarea_region.sigRegionChanged.connect(self.__adjust_regions__)
plot_delta_18_16.addItem(self.validarea_region, ignoreBounds=True)
def __get_plot_item__(
self,
picarro_df: pd.DataFrame,
layout: pg.GraphicsLayout,
rank: int,
species: str,
):
plot_item = layout.addPlot(
rank, 0, axisItems={"bottom": utils.TimeAxisItem(orientation="bottom")}
)
JOSSOUD Olivier
committed
step_curve = pg.PlotCurveItem()
species_series = picarro_df[species]
step_curve.setData(
x=utils.pd_time_to_epoch_ms(species_series.index),
y=list(species_series),
stepMode=False,
)
JOSSOUD Olivier
committed
plot_item.addItem(step_curve)
JOSSOUD Olivier
committed
return plot_item
def __add_second_curve__(
self,
picarro_df: pd.DataFrame,
layout: pg.GraphicsLayout,
rank: int,
species: str,
):
JOSSOUD Olivier
committed
# Get the plot item of the main curve in this layout position
plot_item = layout.getItem(rank, 0)
# P2 is the second plot item
p2 = pg.ViewBox()
JOSSOUD Olivier
committed
layout.addItem(p2, 1, 0)
JOSSOUD Olivier
committed
p2.setXLink(plot_item)
plot_item.getAxis("right").setLabel(species, color="blue")
JOSSOUD Olivier
committed
# Create second curve item
step_curve = pg.PlotCurveItem(pen=pg.mkPen("b"))
species_series = picarro_df[species]
step_curve.setData(
x=utils.pd_time_to_epoch_ms(species_series.index),
y=list(species_series),
stepMode=False,
)
JOSSOUD Olivier
committed
# Add it to the plot item.
p2.addItem(step_curve)
def __update_calib_lines_data__(self):
if len(self.picarro_prvd.calibs_df.index) == 0:
return
if len(self.picarro_prvd.picarro_df.index) == 0:
return
calibs_df = self.picarro_prvd.calibs_df.copy()
calibs_df = calibs_df[
(calibs_df["valid_start"] >= self.picarro_prvd.picarro_df.index.min())
& (calibs_df["valid_end"] <= self.picarro_prvd.picarro_df.index.max())
]
x = []
y = []
connect = []
for index, row in calibs_df.iterrows():
x += [row["valid_start"], row["valid_end"]]
y += [row["Delta_18_16_mean"] + 1, row["Delta_18_16_mean"] + 1]
connect += [True, False]
self.calib_line_item.setData(
x=utils.pd_time_to_epoch_ms(x),
y=y,
connect=np.array(connect),
stepMode=False,
)
JOSSOUD Olivier
committed
def __update_plateau_characteristics__(self, region: pg.LinearRegionItem):
xmin = region.getRegion()[0]
xmax = region.getRegion()[1]
duration = QTime(0, 0, 0)
duration = duration.addSecs(xmax - xmin)
self.main_ui.picarro_timeedit_plateau_duration.setTime(duration)
self.injection_prop_dict["plateau_start"] = utils.epoch_to_string(xmin)
self.injection_prop_dict["plateau_end"] = utils.epoch_to_string(xmax)
self.injection_prop_dict["plateau_duration"] = "{:.2f}".format(xmax - xmin)
def __update_validarea_characteristics__(self, region: pg.LinearRegionItem):
JOSSOUD Olivier
committed
# Area's duration
xmin = region.getRegion()[0]
xmax = region.getRegion()[1]
duration = QTime(0, 0, 0)
JOSSOUD Olivier
committed
self.main_ui.picarro_timeedit_validarea_duration.setTime(duration)
mean_sd_dict = self.picarro_prvd.get_mean_and_sd(
JOSSOUD Olivier
committed
first_date=datetime.datetime.fromtimestamp(xmin, tz=datetime.timezone.utc),
last_date=datetime.datetime.fromtimestamp(xmax, tz=datetime.timezone.utc),
)
JOSSOUD Olivier
committed
all_species = ["H2O", "Delta_18_16", "Delta_D_H"]
for species in all_species:
mean_item = QTableWidgetItem("{:.2f}".format(mean_sd_dict[species]["mean"]))
mean_item.setFlags(Qt.ItemIsEnabled) # Read only
self.main_ui.picarro_tablewidget.setItem(
all_species.index(species), 0, mean_item
)
JOSSOUD Olivier
committed
sd_item = QTableWidgetItem("{:.2f}".format(mean_sd_dict[species]["std"]))
sd_item.setFlags(Qt.ItemIsEnabled) # Read only
self.main_ui.picarro_tablewidget.setItem(
all_species.index(species), 1, sd_item
)
JOSSOUD Olivier
committed
# Update standard name's suggestion
if len(self.picarro_prvd.stdtrue_df.index) > 0:
closest_std = self.picarro_prvd.stdtrue_df.iloc[
(
self.picarro_prvd.stdtrue_df["Delta_18_16_mean"]
- mean_sd_dict["Delta_18_16"]["mean"]
)
.abs()
.argsort()[:1]
]
self.main_ui.picarro_lineedit_stdname_suggestion.setText(
list(closest_std["name"])[0]
)
if (
self.main_ui.picarro_lineedit_stdname_suggestion.text()
!= self.main_ui.picarro_combobox_stdname_user.currentText()
):
self.main_ui.picarro_lineedit_stdname_suggestion.setStyleSheet(
"color: 'orange';"
)
JOSSOUD Olivier
committed
else:
self.main_ui.picarro_lineedit_stdname_suggestion.setStyleSheet(
"color: 'black';"
)
JOSSOUD Olivier
committed
# Update Injection's properties dictionnary
self.injection_prop_dict["valid_start"] = utils.epoch_to_string(xmin)
self.injection_prop_dict["valid_end"] = utils.epoch_to_string(xmax)
self.injection_prop_dict["valid_duration"] = "{:.2f}".format(xmax - xmin)
for species in all_species:
for metrics in ["mean", "std"]:
self.injection_prop_dict[species + "_" + metrics] = "{:.2f}".format(
mean_sd_dict[species][metrics]
)
JOSSOUD Olivier
committed
def __adjust_regions__(self, moved_region: pg.LinearRegionItem):
xmin_plateau = self.plateau_region.getRegion()[0]
xmax_plateau = self.plateau_region.getRegion()[1]
plateau_width = xmax_plateau - xmin_plateau
xmin_validarea = self.validarea_region.getRegion()[0]
xmax_validarea = self.validarea_region.getRegion()[1]
validarea_width = xmax_validarea - xmin_validarea
if moved_region == self.validarea_region:
if validarea_width > plateau_width - 10:
self.plateau_region.setRegion([xmin_validarea - 10, xmax_plateau])
elif xmin_validarea < xmin_plateau:
self.plateau_region.setRegion(
[xmin_validarea, xmin_validarea + plateau_width]
)
JOSSOUD Olivier
committed
elif xmax_validarea > xmax_plateau:
self.plateau_region.setRegion(
[xmax_validarea - plateau_width, xmax_validarea]
)
JOSSOUD Olivier
committed
elif moved_region == self.plateau_region:
if xmin_plateau > xmin_validarea:
self.validarea_region.setRegion(
[xmin_plateau, xmin_plateau + validarea_width]
)
JOSSOUD Olivier
committed
elif xmax_plateau < xmax_validarea:
self.validarea_region.setRegion(
[xmax_plateau - validarea_width, xmax_plateau]
)