diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index 3b24795..d916548 100644 --- a/src/gui_qt/_py/basewindow.py +++ b/src/gui_qt/_py/basewindow.py @@ -365,6 +365,9 @@ class Ui_BaseWindow(object): self.actionBinning.setObjectName("actionBinning") self.actionTNMH = QtWidgets.QAction(BaseWindow) self.actionTNMH.setObjectName("actionTNMH") + self.actionExclude_region = QtWidgets.QAction(BaseWindow) + self.actionExclude_region.setCheckable(True) + self.actionExclude_region.setObjectName("actionExclude_region") self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.action_save_fit_parameter) @@ -419,6 +422,7 @@ class Ui_BaseWindow(object): self.menuLimits.addAction(self.action_no_range) self.menuLimits.addAction(self.action_x_range) self.menuLimits.addAction(self.action_custom_range) + self.menuLimits.addAction(self.actionExclude_region) self.menuFit.addAction(self.action_FitWidget) self.menuFit.addSeparator() self.menuFit.addAction(self.action_create_fit_function) @@ -631,6 +635,7 @@ class Ui_BaseWindow(object): self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,")) self.actionBinning.setText(_translate("BaseWindow", "Binning...")) self.actionTNMH.setText(_translate("BaseWindow", "TNMH...")) + self.actionExclude_region.setText(_translate("BaseWindow", "Exclude region")) from ..data.datawidget.datawidget import DataWidget from ..data.integral_widget import IntegralWidget from ..data.point_select import PointSelectWidget diff --git a/src/gui_qt/dsc/glass_dialog.py b/src/gui_qt/dsc/glass_dialog.py index f815b7a..b7afe09 100644 --- a/src/gui_qt/dsc/glass_dialog.py +++ b/src/gui_qt/dsc/glass_dialog.py @@ -136,8 +136,8 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): max_x = max(max_x, data.x.max()) item = QtWidgets.QListWidgetItem(name) - item.setCheckState(QtCore.Qt.Checked) - item.setData(QtCore.Qt.UserRole, key) + item.setCheckState(QtCore.Qt.CheckState.Checked) + item.setData(QtCore.Qt.ItemDataRole.UserRole, key) item.setForeground(mkBrush(c.rgb())) self.listWidget.addItem(item) @@ -191,10 +191,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): for idx in range(self.listWidget.count()): item = self.listWidget.item(idx) - if item.checkState() == QtCore.Qt.Unchecked: + if item.checkState() == QtCore.Qt.CheckState.Unchecked: continue - key = item.data(QtCore.Qt.UserRole) + key = item.data(QtCore.Qt.ItemDataRole.UserRole) plot = self._plots[key] data, _ = self._dsc[key] @@ -214,7 +214,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): item = self.listWidget.item(idx) tree_item = QtWidgets.QTreeWidgetItem([item.text()]) - values = self._tg_value.get(item.data(QtCore.Qt.UserRole)) + values = self._tg_value.get(item.data(QtCore.Qt.ItemDataRole.UserRole)) if values is not None: for name, pos in values.items(): @@ -223,7 +223,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): self.tg_tree.addTopLevelItem(tree_item) - key = item.data(QtCore.Qt.UserRole) + key = item.data(QtCore.Qt.ItemDataRole.UserRole) plot = self._plots[key] data, _ = self._dsc[key] @@ -251,7 +251,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): @QtCore.pyqtSlot(QtWidgets.QListWidgetItem) def change_visibility(self, item: QtWidgets.QListWidgetItem): is_checked = bool(item.checkState()) - plot = self._plots[item.data(QtCore.Qt.UserRole)] + plot = self._plots[item.data(QtCore.Qt.ItemDataRole.UserRole)] for val in plot: val.setVisible(is_checked) @@ -275,10 +275,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): self.tnmh_tree.clear() for idx in range(self.listWidget.count()): item = self.listWidget.item(idx) - if item.checkState() == QtCore.Qt.Unchecked: + if item.checkState() == QtCore.Qt.CheckState.Unchecked: continue - key = item.data(QtCore.Qt.UserRole) + key = item.data(QtCore.Qt.ItemDataRole.UserRole) data = self.get_fictive(key, baselines) @@ -292,7 +292,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): item = self.listWidget.item(idx) tree_item = QtWidgets.QTreeWidgetItem([item.text()]) - values = self._fit.get(item.data(QtCore.Qt.UserRole)) + values = self._fit.get(item.data(QtCore.Qt.ItemDataRole.UserRole)) if values is not None: child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()]) @@ -305,10 +305,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): ret_dic = {} for idx in range(self.listWidget.count()): item = self.listWidget.item(idx) - if item.checkState() == QtCore.Qt.Unchecked: + if item.checkState() == QtCore.Qt.CheckState.Unchecked: continue - key = item.data(QtCore.Qt.UserRole) + key = item.data(QtCore.Qt.ItemDataRole.UserRole) cp = None if self.fictive_export_check.isChecked(): @@ -332,10 +332,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): m = [] for idx in range(self.listWidget.count()): item = self.listWidget.item(idx) - if item.checkState() == QtCore.Qt.Unchecked: + if item.checkState() == QtCore.Qt.CheckState.Unchecked: continue - key = item.data(QtCore.Qt.UserRole) + key = item.data(QtCore.Qt.ItemDataRole.UserRole) data, _ = self._dsc[key] try: tg_value = self._tg_value[key][tg_type][0] diff --git a/src/gui_qt/fit/fit_toolbar.py b/src/gui_qt/fit/fit_toolbar.py index 4cfa091..283c335 100644 --- a/src/gui_qt/fit/fit_toolbar.py +++ b/src/gui_qt/fit/fit_toolbar.py @@ -58,7 +58,8 @@ class FitToolbar(QtWidgets.QToolBar): @QtCore.pyqtSlot(QtWidgets.QAction) def change_limit_type(self, action: QtWidgets.QAction): - is_custom = (action.text() == 'Custom') + is_custom = (action.text() in ['Custom', 'Exclude region']) + print(is_custom) for w in [self.label, self.label2, self.lineedit, self.lineedit2]: w.setEnabled(is_custom) @@ -93,5 +94,6 @@ class FitToolbar(QtWidgets.QToolBar): return { 'None': 'none', 'Visible x range': 'x', - 'Custom': self.region.getRegion(), + 'Custom': ('in', self.region.getRegion()), + 'Exclude region': ('out', self.region.getRegion()), }[action_text] diff --git a/src/gui_qt/io/dscreader.py b/src/gui_qt/io/dscreader.py index a9b83a0..1ebda46 100644 --- a/src/gui_qt/io/dscreader.py +++ b/src/gui_qt/io/dscreader.py @@ -264,11 +264,16 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): except TypeError: return + if self.cp_checkBox.isChecked() and self.references: + y_label = 'cp' + else: + y_label = 'q' + rate, mode = self.current_run - new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})') + new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate}K-min ({mode}, {y_label})') if filesave: - new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_'))) + new_val.savetxt(self.fname.with_name(f'{self.fname.stem}_{rate}K-min_{y_label}{mode}.dat'.replace(' ', '_'))) close_after = False else: self.data_read.emit([new_val]) diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 80935b6..d91b3ac 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -888,7 +888,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.fit_dialog.load(self.management.active_sets) for item in self.fit_dialog.preview_lines: self.current_graph_widget.add_external(item) - if self.action_custom_range.isChecked(): + if self.action_custom_range.isChecked() or self.actionExclude_region.isChecked(): self.current_graph_widget.add_external(self.fit_toolbar.region) block_window = True @@ -904,7 +904,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): if self.current_graph_widget is None: return - if action == self.action_custom_range and self.fit_dialog.isVisible(): + if action in [self.action_custom_range, self.actionExclude_region] and self.fit_dialog.isVisible(): self.current_graph_widget.add_external(self.fit_toolbar.region) else: self.current_graph_widget.remove_external(self.fit_toolbar.region) diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index b8758b2..ab07037 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -511,13 +511,16 @@ class UpperManagement(QtCore.QObject): _x = data_i.x + # options for fit limits 'none', 'x', ('in', custom region), ('out', excluded region) if fit_limits == 'none': inside = slice(None) elif fit_limits == 'x': x_lim, _ = self.graphs[self.current_graph].ranges inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1])) + elif fit_limits[0] == 'in': + inside = np.where((_x >= fit_limits[1][0]) & (_x <= fit_limits[1][1])) else: - inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1])) + inside = np.where((_x < fit_limits[1][0]) | (_x > fit_limits[1][1])) try: if isinstance(we, str): diff --git a/src/nmreval/io/dsc.py b/src/nmreval/io/dsc.py index b531bd4..4aaf606 100644 --- a/src/nmreval/io/dsc.py +++ b/src/nmreval/io/dsc.py @@ -11,7 +11,8 @@ try: from scipy.integrate import simpson except ImportError: from scipy.integrate import simps as simpson -from scipy.interpolate import interp1d +from scipy.interpolate import CubicSpline + ReferenceValue = namedtuple('Reference', ['name', 'transitions']) Cyclohexane = ReferenceValue('Cyclohexane', [(-87.06+273.15, 79.58), (6.54+273.15, None)]) @@ -38,7 +39,7 @@ class DSCSample: def read_file(self, fname: str | Path) -> None: fname = Path(fname) - # file contains weird deg C character in stupiod ISO encoding + # file contains weird deg C character in stupid ISO encoding with fname.open('r', encoding='iso-8859-15') as f: ii = 1 for line in f: @@ -144,9 +145,12 @@ class DSCCalibrator: self.reference = [] self.ref_list = [] - def set_measurement(self, - fname: str | Path | DSCSample, mode: str = 'sample', - reference: ReferenceValue = Cyclohexane): + def set_measurement( + self: DSCCalibrator, + fname: str | Path | DSCSample, + mode: str = 'sample', + reference: ReferenceValue = Cyclohexane + ): if mode not in ['sample', 'empty', 'reference']: raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"') if mode == 'reference' and not isinstance(reference, ReferenceValue): @@ -266,7 +270,12 @@ class DSCCalibrator: return sol - def get_data(self, idx: int, slope: str = 'iso', limits: tuple[float, float] = None): + def get_data( + self: DSCCalibrator, + idx: int, + slope: str = 'iso', + limits: tuple[float, float] = None + ) -> tuple[np.ndarray, np.ndarray, np.ndarray,np.ndarray | None, np.ndarray]: if self.sample.steps[idx][0] == 'i': raise ValueError('baseline correction is not implemented for isotherms') @@ -292,7 +301,7 @@ class DSCCalibrator: empty_y = empty_data[1] if self.sample.length(idx) != self.empty.length(idx_empty): with np.errstate(all='ignore'): - empty_y = interp1d(empty_data[2]-empty_data[2, 0], empty_data[1], fill_value='extrapolate')(sample_data[2, 0]) + empty_y = CubicSpline(empty_data[2]-empty_data[2, 0], empty_data[1], extrapolate=True)(sample_data[2] - sample_data[2, 0]) sample_data[1] -= empty_y drift_value = sample_data.copy()[(2, 1), :] @@ -346,9 +355,10 @@ class DSCCalibrator: offset = region[0, 0] sample_data[1] -= m * (sample_data[2] - region[1, 0]) + offset - line = np.array([[sample_data[2, 0], sample_data[2, -1]], - [m * (sample_data[2, 0] - region[1, 0]) + offset, - m * (sample_data[2, -1] - region[1, 0]) + offset]]) + line = np.array([ + [sample_data[2, 0], sample_data[2, -1]], + [m * (sample_data[2, 0] - region[1, 0]) + offset, m * (sample_data[2, -1] - region[1, 0]) + offset] + ]) else: line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]]) diff --git a/src/resources/_ui/basewindow.ui b/src/resources/_ui/basewindow.ui index bf3de79..01987b8 100644 --- a/src/resources/_ui/basewindow.ui +++ b/src/resources/_ui/basewindow.ui @@ -247,6 +247,7 @@ + @@ -1021,6 +1022,14 @@ TNMH... + + + true + + + Exclude region + +