From 76cd4acfb0b332b21523896fb1c56ef8f928f6cb Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Wed, 12 Jul 2023 20:48:28 +0200 Subject: [PATCH] add flag for partial fits; closes #83 --- src/gui_qt/data/container.py | 29 +++++++++ src/gui_qt/data/datawidget/datawidget.py | 2 +- src/gui_qt/fit/fit_forms.py | 2 +- src/gui_qt/fit/fitfunction.py | 2 +- src/gui_qt/fit/fitwindow.py | 3 +- src/gui_qt/graphs/graphwindow.py | 2 +- src/gui_qt/lib/__init__.py | 81 +----------------------- src/gui_qt/lib/enums.py | 8 +++ src/gui_qt/lib/iconloading.py | 66 +++++++++++++++++++ src/gui_qt/lib/styles.py | 2 +- src/gui_qt/lib/undos.py | 17 ++++- src/gui_qt/main/mainwindow.py | 2 +- src/gui_qt/main/management.py | 4 ++ 13 files changed, 132 insertions(+), 88 deletions(-) create mode 100644 src/gui_qt/lib/enums.py create mode 100644 src/gui_qt/lib/iconloading.py diff --git a/src/gui_qt/data/container.py b/src/gui_qt/data/container.py index d696e61..82dabc6 100644 --- a/src/gui_qt/data/container.py +++ b/src/gui_qt/data/container.py @@ -33,6 +33,7 @@ class ExperimentContainer(QtCore.QObject): self.id = str(identifier) self._fits = [] + self._relations = kwargs.get('relations', {}) self._data = data self._manager = kwargs.get('manager') self.graph = '' @@ -228,6 +229,8 @@ class ExperimentContainer(QtCore.QObject): return self.plot_real, self.plot_imag, self.plot_error def get_state(self): + # TODO preserve relationships + ret_dic = { 'id': self.id, 'data': self._data.get_state(), @@ -269,6 +272,32 @@ class ExperimentContainer(QtCore.QObject): else: self._fits.extend(value) + def has_relation(self, relation_type): + return relation_type in self._relations + + def get_relation(self, relation_type: int): + return self._relations.get(relation_type, []) + + def add_relation(self, relation_type: int, value: str): + if relation_type not in self._relations: + self._relations[relation_type] = [] + + self._relations[relation_type].append(value) + + def remove_relation(self, relation_type: int, value: str): + if relation_type not in self._relations: + raise ValueError(f'Relationship {relation_type!r} with id {value!r} doe not exist for {self.name}') + + related_id_value = self._relations[relation_type] + + idx = related_id_value.index(value) + if idx == -1: + raise ValueError(f'Relationship {relation_type!r} with id {value!r} doe not exist for {self.name}') + + related_id_value.pop(idx) + if len(related_id_value) == 0: + self._relations.pop(relation_type) + def _update_actions(self): self.actions.update({'sort': self._data.sort, 'cut': self._data.cut, diff --git a/src/gui_qt/data/datawidget/datawidget.py b/src/gui_qt/data/datawidget/datawidget.py index 36cf700..01c30a2 100644 --- a/src/gui_qt/data/datawidget/datawidget.py +++ b/src/gui_qt/data/datawidget/datawidget.py @@ -5,7 +5,7 @@ from nmreval.lib.colors import available_cycles from .properties import PropWidget from ...Qt import QtWidgets, QtGui, QtCore from ..._py.datawidget import Ui_DataWidget -from ...lib import make_action_icons +from ...lib.iconloading import make_action_icons from ...lib.delegates import HeaderDelegate diff --git a/src/gui_qt/fit/fit_forms.py b/src/gui_qt/fit/fit_forms.py index fe33211..f0f63f7 100644 --- a/src/gui_qt/fit/fit_forms.py +++ b/src/gui_qt/fit/fit_forms.py @@ -5,7 +5,7 @@ from nmreval.utils.text import convert from ..Qt import QtCore, QtWidgets, QtGui from .._py.fitmodelwidget import Ui_FitParameter from .._py.save_fitmodel_dialog import Ui_SaveDialog -from ..lib import get_icon +from ..lib.iconloading import get_icon class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter): diff --git a/src/gui_qt/fit/fitfunction.py b/src/gui_qt/fit/fitfunction.py index 11a6d1b..0d1c412 100644 --- a/src/gui_qt/fit/fitfunction.py +++ b/src/gui_qt/fit/fitfunction.py @@ -8,7 +8,7 @@ from nmreval.lib.importer import find_models from nmreval.lib.colors import BaseColor, Tab10 from nmreval.utils.text import convert -from ..lib import get_icon +from ..lib.iconloading import get_icon from .._py.fitfunctionwidget import Ui_Form from ..Qt import QtWidgets, QtCore, QtGui diff --git a/src/gui_qt/fit/fitwindow.py b/src/gui_qt/fit/fitwindow.py index 5ba0ea6..519216e 100644 --- a/src/gui_qt/fit/fitwindow.py +++ b/src/gui_qt/fit/fitwindow.py @@ -13,6 +13,7 @@ from nmreval.fit.result import FitResult from .fit_forms import FitTableWidget from .fit_parameter import QFitParameterWidget +from ..lib import Relations from ..lib.pg_objects import PlotItem from ..Qt import QtGui, QtCore, QtWidgets from .._py.fitdialog import Ui_FitDialog @@ -147,7 +148,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): # deselect all fit sets for i in range(self.data_table.rowCount()): data_id = self.data_table.item(i, 0).data(QtCore.Qt.UserRole+1) - if self._management[data_id].mode == 'fit': + if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf): self.data_table.item(i, 0).setCheckState(QtCore.Qt.Unchecked) if self.models: diff --git a/src/gui_qt/graphs/graphwindow.py b/src/gui_qt/graphs/graphwindow.py index d4339d7..134a6cc 100644 --- a/src/gui_qt/graphs/graphwindow.py +++ b/src/gui_qt/graphs/graphwindow.py @@ -18,7 +18,7 @@ from ..io.filedialog import FileDialog from ..lib.pg_objects import LegendItemBlock, RegionItem from ..Qt import QtCore, QtWidgets, QtGui from .._py.graph import Ui_GraphWindow -from ..lib import make_action_icons +from ..lib.iconloading import make_action_icons from ..lib.configurations import GraceMsgBox diff --git a/src/gui_qt/lib/__init__.py b/src/gui_qt/lib/__init__.py index 36d53d0..e82b229 100644 --- a/src/gui_qt/lib/__init__.py +++ b/src/gui_qt/lib/__init__.py @@ -1,80 +1 @@ -import sys - -if sys.version_info < (3, 7): - HAS_IMPORTLIB_RESOURCE = False - from pkg_resources import resource_filename -else: - HAS_IMPORTLIB_RESOURCE = True - from importlib.resources import path - -from ..Qt import QtGui, QtWidgets - - -# def get_path_importlib(package, resource): -# return path(package, resource) -# -# -# def _get_path_pkg(package, resource): -# return resource_filename(package, resource) -# -# -# if HAS_IMPORTLIB_RESOURCE: -# get_path = get_path_importlib -# else: -# get_path = _get_path_pkg - - -def make_action_icons(widget): - global HAS_IMPORTLIB_RESOURCE - icon_type = QtWidgets.QApplication.instance().theme - from json import loads - - if HAS_IMPORTLIB_RESOURCE: - with path('resources.icons', 'icons.json') as fp: - with fp.open('r') as f: - icon_list = loads(f.read()) - - for ac, img in icon_list[widget.objectName()].items(): - dirname = 'resources.icons.%s_light' % icon_type - with path(dirname, img+'.png') as imgpath: - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off) - getattr(widget, ac).setIcon(icon) - - else: - with open(resource_filename('resources.icons', 'icons.json'), 'r') as f: - icon_list = loads(f.read()) - - for ac, img in icon_list[widget.objectName()].items(): - dirname = 'resources.icons.%s_light' % icon_type - imgpath = resource_filename(dirname, img+'.png') - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off) - getattr(widget, ac).setIcon(icon) - - -def get_icon(icon_name): - try: - icon_type = QtWidgets.QApplication.instance().theme - except AttributeError: - icon_type = 'normal' - - global HAS_IMPORTLIB_RESOURCE - - if icon_name != 'logo': - dirname = f'resources.icons.{icon_type}_light' - else: - dirname = 'resources.icons' - - if HAS_IMPORTLIB_RESOURCE: - with path(dirname, icon_name+'.png') as imgpath: - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off) - - return icon - else: - imgpath = resource_filename(dirname, icon_name+'.png') - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(imgpath), QtGui.QIcon.Normal, QtGui.QIcon.Off) - - return icon +from .enums import Relations diff --git a/src/gui_qt/lib/enums.py b/src/gui_qt/lib/enums.py new file mode 100644 index 0000000..60b7923 --- /dev/null +++ b/src/gui_qt/lib/enums.py @@ -0,0 +1,8 @@ +from enum import IntEnum, auto + + +class Relations(IntEnum): + isFitOf = auto() + hasFit = auto() + isFitPartOf = auto() + hasFitPart = auto() diff --git a/src/gui_qt/lib/iconloading.py b/src/gui_qt/lib/iconloading.py new file mode 100644 index 0000000..0a41634 --- /dev/null +++ b/src/gui_qt/lib/iconloading.py @@ -0,0 +1,66 @@ +import sys + +if sys.version_info < (3, 7): + HAS_IMPORTLIB_RESOURCE = False + from pkg_resources import resource_filename +else: + HAS_IMPORTLIB_RESOURCE = True + from importlib.resources import path + +from ..Qt import QtGui, QtWidgets + + +def make_action_icons(widget): + global HAS_IMPORTLIB_RESOURCE + icon_type = QtWidgets.QApplication.instance().theme + from json import loads + + if HAS_IMPORTLIB_RESOURCE: + with path('resources.icons', 'icons.json') as fp: + with fp.open('r') as f: + icon_list = loads(f.read()) + + for ac, img in icon_list[widget.objectName()].items(): + dirname = 'resources.icons.%s_light' % icon_type + with path(dirname, img+'.png') as imgpath: + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off) + getattr(widget, ac).setIcon(icon) + + else: + with open(resource_filename('resources.icons', 'icons.json'), 'r') as f: + icon_list = loads(f.read()) + + for ac, img in icon_list[widget.objectName()].items(): + dirname = 'resources.icons.%s_light' % icon_type + imgpath = resource_filename(dirname, img+'.png') + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off) + getattr(widget, ac).setIcon(icon) + + +def get_icon(icon_name): + try: + icon_type = QtWidgets.QApplication.instance().theme + except AttributeError: + icon_type = 'normal' + + global HAS_IMPORTLIB_RESOURCE + + if icon_name != 'logo': + dirname = f'resources.icons.{icon_type}_light' + else: + dirname = 'resources.icons' + + if HAS_IMPORTLIB_RESOURCE: + with path(dirname, icon_name+'.png') as imgpath: + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off) + + return icon + else: + imgpath = resource_filename(dirname, icon_name+'.png') + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(imgpath), QtGui.QIcon.Normal, QtGui.QIcon.Off) + + return icon diff --git a/src/gui_qt/lib/styles.py b/src/gui_qt/lib/styles.py index 21290d6..df6d1fd 100644 --- a/src/gui_qt/lib/styles.py +++ b/src/gui_qt/lib/styles.py @@ -1,4 +1,4 @@ -from . import HAS_IMPORTLIB_RESOURCE +from .iconloading import HAS_IMPORTLIB_RESOURCE from ..Qt import QtGui, QtWidgets, QtCore diff --git a/src/gui_qt/lib/undos.py b/src/gui_qt/lib/undos.py index acc1f37..25f642a 100644 --- a/src/gui_qt/lib/undos.py +++ b/src/gui_qt/lib/undos.py @@ -4,6 +4,7 @@ import copy from numpy import argsort +from . import Relations from ..Qt import QtWidgets, QtCore from ..data.container import FitContainer from ..graphs.graphwindow import QGraphWindow @@ -242,10 +243,17 @@ class DeleteCommand(QtWidgets.QUndoCommand): if isinstance(val, FitContainer): try: - self.__container[sid].fitted_key._fits.remove(sid) + self.__container[val.fitted_key]._fits.remove(sid) except KeyError: pass + for (flag1, flag2) in ((Relations.isFitPartOf, Relations.hasFitPart), (Relations.hasFitPart, Relations.isFitPartOf)): + for related_item in val.get_relation(flag1): + try: + self.__container[related_item].remove_relation(flag2, sid) + except KeyError: + pass + del self.__container[sid] self.__graph_container[self.__graph_key].block(False) @@ -264,6 +272,13 @@ class DeleteCommand(QtWidgets.QUndoCommand): except KeyError: pass + for (flag1, flag2) in (('isFitPartOf', 'hasFitPartOf'), ('hasFitPartOf', 'isFitPartOf')): + for related_item in val.get_relation(flag1): + try: + self.__container[related_item].add_relation(flag2, sid) + except KeyError: + pass + self.__signal_add.emit(list(self.__keys), self.__graph_key) self.__graph_container[self.__graph_key].block(False) diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 66845f3..6973141 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -22,7 +22,7 @@ from ..graphs.graphwindow import QGraphWindow from ..graphs.movedialog import QMover from ..io.fcbatchreader import QFCReader from ..io.filedialog import * -from ..lib import get_icon, make_action_icons +from ..lib.iconloading import make_action_icons, get_icon from ..lib.pg_objects import RegionItem from ..lib.starter import make_starter from ..math.binning import BinningWindow diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index 8feb1cc..191fe7c 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -18,6 +18,7 @@ from nmreval.math.smooth import smooth from nmreval.nmr.relaxation import Relaxation from ..Qt import QtCore, QtWidgets +from ..lib import Relations from ..lib.undos import * from ..data.container import * from ..io.filereaders import QFileReader @@ -585,6 +586,9 @@ class UpperManagement(QtCore.QObject): subfunc.name += data_name sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No) + self[sub_f_id].add_relation(Relations.isFitPartOf, f_id) + self[f_id].add_relation(Relations.hasFitPart, sub_f_id) + f_id_list.append(sub_f_id) gid = data_k.graph self.delete_sets(tobedeleted)