diff --git a/bin/evaluate.py b/bin/evaluate.py
index 5c98acc..f5fc10a 100755
--- a/bin/evaluate.py
+++ b/bin/evaluate.py
@@ -4,6 +4,11 @@ import sys
import pathlib
sys.path.append(str(pathlib.Path().cwd().parent))
+from nmreval.configs import check_for_config
+
+# does a directory for config stuff exist? create it if not
+check_for_config()
+
# pyqtgraph warns on Mac if QApplication is created when it is imported
# import pyqtgraph
diff --git a/nmreval/configs.py b/nmreval/configs.py
index 84f1a83..ae86ed5 100644
--- a/nmreval/configs.py
+++ b/nmreval/configs.py
@@ -1,25 +1,44 @@
import configparser
import pathlib
import pickle
-import logging.handlers
+from shutil import copyfile
+from importlib.resources import path as resource_path
-__all__ = ['config_paths', 'read_configuration', 'write_configuration', 'allowed_values', 'write_state', 'read_state']
+__all__ = ['config_paths', 'check_for_config', 'read_configuration', 'write_configuration', 'allowed_values', 'write_state', 'read_state']
+
+
+def check_for_config(make=True):
+ try:
+ config_paths()
+ except FileNotFoundError as e:
+ if make:
+ conf_path = pathlib.Path('~/.auswerten').expanduser()
+ conf_path.mkdir(parents=True)
+ cwd = pathlib.Path(__file__).parent
+ copyfile(cwd / 'models' / 'usermodels.py', conf_path / 'usermodels.py')
+ with resource_path('resources', 'Default.agr') as fp:
+ copyfile(fp, conf_path / 'Default.agr')
+
+ else:
+ raise e
def config_paths() -> pathlib.Path:
# TODO adjust for different OS
- searchpaths = ['~/.local/share/auswerten', '~/.auswerten', '/usr/share/nmreval']
- path = None
+ searchpaths = ['~/.config/nmreval', '~/.auswerten', '/usr/share/nmreval']
+ conf_path = None
+
for p in searchpaths:
path = pathlib.Path(p).expanduser()
if path.exists():
+ conf_path = path
break
- if path is None:
+ if conf_path is None:
raise FileNotFoundError('No valid configuration path found')
- return path
+ return conf_path
def read_configuration() -> configparser.ConfigParser:
diff --git a/nmreval/gui_qt/_py/basewindow.py b/nmreval/gui_qt/_py/basewindow.py
index 5e9fe2a..40b2100 100644
--- a/nmreval/gui_qt/_py/basewindow.py
+++ b/nmreval/gui_qt/_py/basewindow.py
@@ -233,8 +233,8 @@ class Ui_BaseWindow(object):
self.actionShow_log.setObjectName("actionShow_log")
self.action_create_fit_function = QtWidgets.QAction(BaseWindow)
self.action_create_fit_function.setObjectName("action_create_fit_function")
- self.actionGrace_preferences = QtWidgets.QAction(BaseWindow)
- self.actionGrace_preferences.setObjectName("actionGrace_preferences")
+ self.action_colorcycle = QtWidgets.QAction(BaseWindow)
+ self.action_colorcycle.setObjectName("action_colorcycle")
self.actionSave_session = QtWidgets.QAction(BaseWindow)
self.actionSave_session.setObjectName("actionSave_session")
self.actionMouse_behaviour = QtWidgets.QAction(BaseWindow)
@@ -420,7 +420,7 @@ class Ui_BaseWindow(object):
self.menuFit.addAction(self.menuLimits.menuAction())
self.menuOptions.addAction(self.actionMouse_behaviour)
self.menuOptions.addSeparator()
- self.menuOptions.addAction(self.actionGrace_preferences)
+ self.menuOptions.addAction(self.action_colorcycle)
self.menuOptions.addAction(self.actionConfiguration)
self.menuView.addAction(self.actionTile)
self.menuView.addAction(self.actionCascade_windows)
@@ -547,7 +547,7 @@ class Ui_BaseWindow(object):
self.actionShift.setText(_translate("BaseWindow", "&Shift/scale..."))
self.actionShow_log.setText(_translate("BaseWindow", "&Show log..."))
self.action_create_fit_function.setText(_translate("BaseWindow", "&Create fit function..."))
- self.actionGrace_preferences.setText(_translate("BaseWindow", "&Grace preferences..."))
+ self.action_colorcycle.setText(_translate("BaseWindow", "Color cycles..."))
self.actionSave_session.setText(_translate("BaseWindow", "Update session"))
self.actionMouse_behaviour.setText(_translate("BaseWindow", "Mouse behaviour"))
self.actionMouse_behaviour.setToolTip(_translate("BaseWindow", "Switch between zoom and pan in graph."))
diff --git a/nmreval/gui_qt/_py/color_palette.py b/nmreval/gui_qt/_py/color_palette.py
index 7d7e186..7aa29e6 100644
--- a/nmreval/gui_qt/_py/color_palette.py
+++ b/nmreval/gui_qt/_py/color_palette.py
@@ -15,12 +15,9 @@ class Ui_Dialog(object):
Dialog.setObjectName("Dialog")
Dialog.resize(390, 409)
self.gridLayout = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout")
- self.append_palette_button = QtWidgets.QPushButton(Dialog)
- self.append_palette_button.setObjectName("append_palette_button")
- self.gridLayout.addWidget(self.append_palette_button, 2, 0, 1, 1)
- spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.gridLayout.addItem(spacerItem, 4, 0, 1, 1)
self.palette_combobox = QtWidgets.QComboBox(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -28,7 +25,7 @@ class Ui_Dialog(object):
sizePolicy.setHeightForWidth(self.palette_combobox.sizePolicy().hasHeightForWidth())
self.palette_combobox.setSizePolicy(sizePolicy)
self.palette_combobox.setObjectName("palette_combobox")
- self.gridLayout.addWidget(self.palette_combobox, 0, 0, 1, 1)
+ self.gridLayout.addWidget(self.palette_combobox, 0, 1, 1, 2)
self.add_color_button = QtWidgets.QPushButton(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -36,24 +33,7 @@ class Ui_Dialog(object):
sizePolicy.setHeightForWidth(self.add_color_button.sizePolicy().hasHeightForWidth())
self.add_color_button.setSizePolicy(sizePolicy)
self.add_color_button.setObjectName("add_color_button")
- self.gridLayout.addWidget(self.add_color_button, 6, 0, 1, 1)
- self.color_combobox = ColorListEditor(Dialog)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.color_combobox.sizePolicy().hasHeightForWidth())
- self.color_combobox.setSizePolicy(sizePolicy)
- self.color_combobox.setObjectName("color_combobox")
- self.gridLayout.addWidget(self.color_combobox, 5, 0, 1, 1)
- self.save_button = QtWidgets.QPushButton(Dialog)
- self.save_button.setObjectName("save_button")
- self.gridLayout.addWidget(self.save_button, 9, 1, 1, 1)
- self.add_palette_button = QtWidgets.QPushButton(Dialog)
- self.add_palette_button.setObjectName("add_palette_button")
- self.gridLayout.addWidget(self.add_palette_button, 1, 0, 1, 1)
- self.new_name_edit = QtWidgets.QLineEdit(Dialog)
- self.new_name_edit.setObjectName("new_name_edit")
- self.gridLayout.addWidget(self.new_name_edit, 9, 2, 1, 1)
+ self.gridLayout.addWidget(self.add_color_button, 3, 2, 1, 1)
self.colorlist = QtWidgets.QListWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
@@ -62,12 +42,50 @@ class Ui_Dialog(object):
self.colorlist.setSizePolicy(sizePolicy)
self.colorlist.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
self.colorlist.setObjectName("colorlist")
- self.gridLayout.addWidget(self.colorlist, 0, 1, 9, 2)
+ self.gridLayout.addWidget(self.colorlist, 4, 1, 1, 2)
+ self.add_palette_button = QtWidgets.QPushButton(Dialog)
+ self.add_palette_button.setObjectName("add_palette_button")
+ self.gridLayout.addWidget(self.add_palette_button, 1, 1, 1, 1)
+ self.new_name_edit = QtWidgets.QLineEdit(Dialog)
+ self.new_name_edit.setObjectName("new_name_edit")
+ self.gridLayout.addWidget(self.new_name_edit, 6, 1, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
- self.gridLayout.addWidget(self.buttonBox, 10, 0, 1, 3)
+ self.gridLayout.addWidget(self.buttonBox, 7, 0, 1, 3)
+ self.color_combobox = ColorListEditor(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.color_combobox.sizePolicy().hasHeightForWidth())
+ self.color_combobox.setSizePolicy(sizePolicy)
+ self.color_combobox.setObjectName("color_combobox")
+ self.gridLayout.addWidget(self.color_combobox, 3, 1, 1, 1)
+ self.line = QtWidgets.QFrame(Dialog)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 2, 0, 1, 3)
+ self.save_button = QtWidgets.QPushButton(Dialog)
+ self.save_button.setObjectName("save_button")
+ self.gridLayout.addWidget(self.save_button, 6, 0, 1, 1)
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
+ self.append_palette_button = QtWidgets.QPushButton(Dialog)
+ self.append_palette_button.setObjectName("append_palette_button")
+ self.gridLayout.addWidget(self.append_palette_button, 1, 2, 1, 1)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 3, 0, 1, 1)
+ self.line_2 = QtWidgets.QFrame(Dialog)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout.addWidget(self.line_2, 5, 0, 1, 3)
+ self.label.setBuddy(self.palette_combobox)
+ self.label_2.setBuddy(self.color_combobox)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept)
@@ -76,9 +94,11 @@ class Ui_Dialog(object):
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
- Dialog.setWindowTitle(_translate("Dialog", "Color Palette"))
- self.append_palette_button.setText(_translate("Dialog", "Append"))
+ Dialog.setWindowTitle(_translate("Dialog", "Color cycles"))
self.add_color_button.setText(_translate("Dialog", "Add color"))
- self.save_button.setText(_translate("Dialog", "Save as new list"))
self.add_palette_button.setText(_translate("Dialog", "Load"))
+ self.save_button.setText(_translate("Dialog", "Save as new list"))
+ self.label.setText(_translate("Dialog", "Color cycles"))
+ self.append_palette_button.setText(_translate("Dialog", "Append"))
+ self.label_2.setText(_translate("Dialog", "Available color"))
from ..lib.delegates import ColorListEditor
diff --git a/nmreval/gui_qt/data/container.py b/nmreval/gui_qt/data/container.py
index 1c188e1..285f60e 100644
--- a/nmreval/gui_qt/data/container.py
+++ b/nmreval/gui_qt/data/container.py
@@ -14,7 +14,7 @@ from ...lib.lines import LineStyle
from ...lib.symbols import SymbolStyle, symbolcycle
from ...data.nmr import Spectrum, FID
-from ..Qt import QtCore
+from ..Qt import QtCore, QtGui
from ..io.exporters import GraceExporter
from ..lib.decorators import plot_update
from ..lib.pg_objects import ErrorBars, PlotItem
@@ -278,7 +278,9 @@ class ExperimentContainer(QtCore.QObject):
def get_properties(self) -> dict:
props = OrderedDict()
- props['General'] = OrderedDict([('Name', self.name), ('Value', str(self.value)), ('Group', str(self.group))])
+ props['General'] = OrderedDict([('Name', self.name),
+ ('Value', str(self.value)),
+ ('Group', str(self.group))])
props['Symbol'] = OrderedDict()
props['Line'] = OrderedDict()
@@ -302,34 +304,38 @@ class ExperimentContainer(QtCore.QObject):
return props
def setColor(self, color, symbol=False, line=False, mode='real'):
- if mode == 'real':
+ if mode in ['real', 'all']:
self.plot_real.set_color(color, symbol=symbol, line=line)
- if self.plot_real.symbol != SymbolStyle.No and symbol:
- err_pen = self.plot_error.pen
- err_pen.setColor(self.plot_real.symbolcolor.rbg())
- self.plot_error.setData(pen=err_pen)
- elif line:
- err_pen = self.plot_error.pen
- err_pen.setColor(self.plot_real.linecolor.rbg())
+ if self.plot_error is not None:
+ err_pen = self.plot_error.opts['pen']
+ err_pen.setColor(QtGui.QColor(*self.plot_real.symbolcolor.rgb()))
self.plot_error.setData(pen=err_pen)
elif mode == 'imag' and self.plot_imag is not None:
self.plot_imag.set_color(color, symbol=symbol, line=line)
else:
- print('Updating color failed for ' + str(self.id))
+ print('Updating color failed for ' + str(self.id))
def setSymbol(self, symbol=None, color=None, size=None, mode='real'):
- if mode == 'real':
+ if mode in ['real', 'all']:
self.plot_real.set_symbol(symbol=symbol, size=size, color=color)
- elif mode == 'imag' and self.plot_imag is not None:
+ if color is not None and self.plot_error is not None and self.plot_real.symbol != SymbolStyle.No:
+ err_pen = self.plot_error.opts['pen']
+ err_pen.setColor(QtGui.QColor(*self.plot_real.symbolcolor.rgb()))
+ self.plot_error.setData(pen=err_pen)
+ elif mode in ['imag', 'all'] and self.plot_imag is not None:
self.plot_imag.set_symbol(symbol=symbol, size=size, color=color)
else:
print('Updating symbol failed for ' + str(self.id))
def setLine(self, width=None, style=None, color=None, mode='real'):
- if mode == 'real':
+ if mode in ['real', 'all']:
self.plot_real.set_line(width=width, style=style, color=color)
- elif mode == 'imag' and self.plot_imag is not None:
+ if color is not None and self.plot_error is not None and self.plot_real.symbol == SymbolStyle.No:
+ err_pen = self.plot_error.opts['pen']
+ err_pen.setColor(QtGui.QColor(*self.plot_real.linecolor.rgb()))
+ self.plot_error.setData(pen=err_pen)
+ elif mode in ['imag', 'all'] and self.plot_imag is not None:
self.plot_imag.set_line(width=width, style=style, color=color)
else:
print('Updating line failed for ' + str(self.id))
@@ -532,12 +538,6 @@ class PointContainer(ExperimentContainer):
self.plot_error = ErrorBars(x=self._data.x, y=self._data.y, top=self._data.y_err, bottom=self._data.y_err,
pen=mkPen({'color': self.plot_real.linecolor.rgb()}))
- def update_property(self, key1: str, key2: str, value: Any):
- # update default color
- if key1 == 'Symbol' and key2 == 'Color':
- self.plot_real.set_color(value, symbol=True, line=False)
- super().update_property(key1, key2, value)
-
class FitContainer(ExperimentContainer):
def __init__(self, identifier, data, **kwargs):
diff --git a/nmreval/gui_qt/data/datawidget/datawidget.py b/nmreval/gui_qt/data/datawidget/datawidget.py
index ddc8fcb..55298ad 100644
--- a/nmreval/gui_qt/data/datawidget/datawidget.py
+++ b/nmreval/gui_qt/data/datawidget/datawidget.py
@@ -1,5 +1,6 @@
from typing import List, Tuple, Union
+from nmreval.lib.colors import available_cycles
from .properties import PropWidget
from ...Qt import QtWidgets, QtGui, QtCore
from ..._py.datawidget import Ui_DataWidget
@@ -99,6 +100,7 @@ class DataTree(QtWidgets.QTreeWidget):
child = item.child(i)
child.setCheckState(0, QtCore.Qt.Checked)
to_be_shown.add(child.data(0, QtCore.Qt.UserRole))
+ self._checked_sets.add(child.data(0, QtCore.Qt.UserRole))
self.blockSignals(False)
# check state change to unchecked
@@ -303,10 +305,10 @@ class DataTree(QtWidgets.QTreeWidget):
iterator += 1
- def contextMenuEvent(self, evt):
+ def contextMenuEvent(self, evt, color_list=None):
menu = QtWidgets.QMenu()
- d = menu.addAction('Hello')
- d.setEnabled(False)
+ _ = menu.addAction('Hello')
+ _.setEnabled(False)
menu.addSeparator()
idx = self.selectedIndexes()
@@ -326,6 +328,10 @@ class DataTree(QtWidgets.QTreeWidget):
cat_action = menu.addAction('Join us!')
plt_action = None
save_action = None
+ menu.addSeparator()
+ col_menu = menu.addMenu('Color cycle')
+ for c in available_cycles.keys():
+ col_menu.addAction(c)
idx = {}
has_fits = False
@@ -355,10 +361,11 @@ class DataTree(QtWidgets.QTreeWidget):
action = menu.exec(evt.globalPos())
+ s = []
+ for gid, sets in idx.items():
+ s.extend(sets)
+
if action == del_action:
- s = []
- for gid, sets in idx.items():
- s.extend(sets)
self.management.delete_sets(s)
elif action == cp_action:
@@ -366,23 +373,17 @@ class DataTree(QtWidgets.QTreeWidget):
self.management.copy_sets(sets, gid)
elif action == cat_action:
- s = []
- for gid, sets in idx.items():
- s.extend(sets)
self.management.cat(s)
elif action == plt_action:
- s = []
- for gid, sets in idx.items():
- s.extend(sets)
self.management.make_fit_parameter(s)
elif action == save_action:
- s = []
- for gid, sets in idx.items():
- s.extend(sets)
self.saveFits.emit(s)
+ elif action.parent() == col_menu:
+ self.management.set_cycle(s, action.text())
+
evt.accept()
def highlight(self, gid: str):
diff --git a/nmreval/gui_qt/graphs/graphwindow.py b/nmreval/gui_qt/graphs/graphwindow.py
index ef79125..c0a03f9 100644
--- a/nmreval/gui_qt/graphs/graphwindow.py
+++ b/nmreval/gui_qt/graphs/graphwindow.py
@@ -8,7 +8,7 @@ from typing import List, Union
from numpy import errstate, floor, log10
from pyqtgraph import GraphicsObject, getConfigOption, mkColor
-from ..lib.pg_objects import RegionItem
+from ..lib.pg_objects import LegendItemBlock, RegionItem
from ...utils.text import convert
from ..Qt import QtCore, QtWidgets, QtGui
from .._py.graph import Ui_GraphWindow
@@ -86,10 +86,10 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
ax.setStyle(showValues=False)
ax.setWidth(10)
- self.legend = self.plotItem.addLegend()
+ self.legend = LegendItemBlock(offset=(20, 20))
+ self.legend.setParentItem(self.plotItem.vb)
+ self.plotItem.legend = self.legend
self.legend.setVisible(True)
- # self.legend.setBrush(color=self._bgcolor)
- self.legend.layout.setContentsMargins(1, 1, 1, 1)
self.plotItem.setMenuEnabled(False, True)
self.plotItem.ctrl.logXCheck.blockSignals(True)
diff --git a/nmreval/gui_qt/lib/color_dialog.py b/nmreval/gui_qt/lib/color_dialog.py
index c85e334..f9a5601 100644
--- a/nmreval/gui_qt/lib/color_dialog.py
+++ b/nmreval/gui_qt/lib/color_dialog.py
@@ -1,16 +1,16 @@
from ...configs import config_paths
from ..Qt import QtWidgets, QtCore, QtGui
from .._py.color_palette import Ui_Dialog
-from ...lib.colors import Colors, get_palettes
+from ...lib.colors import Colors, available_cycles
-class PaletteDialog(QtWidgets.QDialog, Ui_Dialog):
+class ColorDialog(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
- self._palettes = get_palettes()
+ self._palettes = available_cycles
self.palette_combobox.addItems(self._palettes.keys())
self.colorlist.installEventFilter(self)
@@ -26,7 +26,7 @@ class PaletteDialog(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(name='on_add_palette_button_pressed')
@QtCore.pyqtSlot(name='on_append_palette_button_pressed')
- def set_palette(self, clear=True):
+ def set_palette(self, clear: bool = False):
if self.sender() == self.add_palette_button or clear:
self.colorlist.clear()
@@ -49,15 +49,15 @@ class PaletteDialog(QtWidgets.QDialog, Ui_Dialog):
return
if not self.new_name_edit.text():
- _ = QtWidgets.QMessageBox.warning(self, 'Error', 'New colors have no name.')
+ _ = QtWidgets.QMessageBox.warning(self, 'Error', 'New list has no name.')
return
- color_name = self.new_name_edit.text()
+ color_name = self.new_name_edit.text() # type: str
if color_name in self._palettes:
- _ = QtWidgets.QMessageBox.warning(self, 'Error', 'Name already used.')
+ _ = QtWidgets.QMessageBox.warning(self, 'Error', 'Name is already used.')
return
- color_file = config_paths() / 'colorschemes.cfg'
+ color_file = config_paths() / 'colors.cfg'
new_palette = []
with color_file.open('a') as f:
@@ -66,8 +66,7 @@ class PaletteDialog(QtWidgets.QDialog, Ui_Dialog):
item = self.colorlist.item(i)
r, g, b = item.data(QtCore.Qt.DecorationRole).getRgbF()[:3]
new_palette.append(Colors.from_rgb(r, g, b, normed=True))
- f.write('{r*255:.1f}, {g*255:.1f}, {b*255:.1f}\n')
- f.write('%.1f, %.1f}, %.1f}\n' % (r*255, g*255, b*255))
+ f.write(f'{r*255:.1f}, {g*255:.1f}, {b*255:.1f}\n')
f.write('\n')
self._palettes[color_name] = new_palette
diff --git a/nmreval/gui_qt/lib/delegates.py b/nmreval/gui_qt/lib/delegates.py
index 5054e47..d26c1f0 100644
--- a/nmreval/gui_qt/lib/delegates.py
+++ b/nmreval/gui_qt/lib/delegates.py
@@ -2,7 +2,7 @@ import re
from ..Qt import QtWidgets, QtGui, QtCore
-from ...lib.colors import BaseColor, TUColors
+from ...lib.colors import BaseColor, Colors
from ...lib.lines import LineStyle
from ...lib.symbols import SymbolStyle, make_symbol_pixmap
@@ -106,7 +106,7 @@ class ColorListEditor(QtWidgets.QComboBox):
break
def populateList(self):
- for i, colorName in enumerate(TUColors):
+ for i, colorName in enumerate(Colors):
color = QtGui.QColor(*colorName.value)
self.insertItem(i, colorName.name)
self.setItemData(i, colorName)
diff --git a/nmreval/gui_qt/lib/forms.py b/nmreval/gui_qt/lib/forms.py
index 0d22f06..cf4a3a2 100644
--- a/nmreval/gui_qt/lib/forms.py
+++ b/nmreval/gui_qt/lib/forms.py
@@ -145,9 +145,9 @@ class FormWidget(QtWidgets.QWidget):
if self._type == 'str':
self.vals.setText(val)
elif self._type == 'int':
- self.vals.setText(f'{val:.0f}')
+ self.vals.setText(f'{float(val):.0f}')
else:
- self.vals.setText(f'{val:.5g}')
+ self.vals.setText(f'{float(val):.5g}')
def setChecked(self, enable):
if self._checkable:
diff --git a/nmreval/gui_qt/lib/pg_objects.py b/nmreval/gui_qt/lib/pg_objects.py
index 99559a5..d412a06 100644
--- a/nmreval/gui_qt/lib/pg_objects.py
+++ b/nmreval/gui_qt/lib/pg_objects.py
@@ -5,6 +5,7 @@ from pyqtgraph import (
LinearRegionItem, mkBrush,
mkColor, mkPen,
PlotDataItem,
+ LegendItem,
)
from ...lib.colors import BaseColor, Colors, TUColors
@@ -438,3 +439,24 @@ class RegionItem(LinearRegionItem):
self.prepareGeometryChange()
return br
+
+
+class LegendItemBlock(LegendItem):
+ """
+ Simple subclass that stops dragging legend outside of view
+ """
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self.layout.setContentsMargins(1, 1, 1, 1)
+
+ def mouseDragEvent(self, ev):
+ if ev.button() == QtCore.Qt.LeftButton:
+ ev.accept()
+ dpos = ev.pos() - ev.lastPos()
+ vb_rect = self.parentItem().rect()
+ pos = self.pos()
+ # upper left corner and a point a little more to the bottom right must be inside
+ if vb_rect.contains(pos+dpos) and vb_rect.contains(pos+dpos+QtCore.QPointF(20., 20.)):
+ self.autoAnchor(pos + dpos)
+ else:
+ self.autoAnchor(pos)
diff --git a/nmreval/gui_qt/lib/utils.py b/nmreval/gui_qt/lib/utils.py
index adba102..f5a49c6 100644
--- a/nmreval/gui_qt/lib/utils.py
+++ b/nmreval/gui_qt/lib/utils.py
@@ -3,13 +3,13 @@ from contextlib import contextmanager
from numpy import linspace
from scipy.interpolate import interp1d
-from ..Qt import QtGui, QtWidgets
+from ..Qt import QtGui, QtWidgets, QtCore
@contextmanager
def busy_cursor():
try:
- cursor = QtGui.QCursor(QtGui.QPixmap('/autohome/dominik/Downloads/slowbro.gif'))
+ cursor = QtGui.QCursor(QtCore.Qt.ForbiddenCursor)
QtWidgets.QApplication.setOverrideCursor(cursor)
yield
diff --git a/nmreval/gui_qt/main/mainwindow.py b/nmreval/gui_qt/main/mainwindow.py
index cca3b5b..4aa4f97 100644
--- a/nmreval/gui_qt/main/mainwindow.py
+++ b/nmreval/gui_qt/main/mainwindow.py
@@ -936,6 +936,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
dialog = GeneralConfiguration(self)
dialog.show()
+ @QtCore.pyqtSlot(name='on_action_colorcycle_triggered')
+ def open_color_dialog(self):
+ from ..lib.color_dialog import ColorDialog
+ dialog = ColorDialog(self)
+ dialog.show()
+
def close(self):
write_state({'recent_path': str(self.path)})
diff --git a/nmreval/gui_qt/main/management.py b/nmreval/gui_qt/main/management.py
index 76eced2..2f96332 100644
--- a/nmreval/gui_qt/main/management.py
+++ b/nmreval/gui_qt/main/management.py
@@ -9,7 +9,7 @@ from ...fit import data as fit_d
from ...fit.model import Model
from ...fit.result import FitResult
from ...fit.minimizer import FitRoutine
-from ...lib.colors import TUColorsC
+from ...lib.colors import TUColorsC, available_cycles
from ...math.interpol import interpolate
from ...math.logfourier import logft
from ...math.smooth import smooth
@@ -697,10 +697,10 @@ class UpperManagement(QtCore.QObject):
self.newData.emit(new_data, self.current_graph)
@QtCore.pyqtSlot()
- def update_color(self):
- UpperManagement._colors = cycle(TUColors)
- for i in self.active:
- self.data[i].color = next(UpperManagement._colors)
+ def set_cycle(self, set_idx: list, cycle_name: str):
+ col = cycle(available_cycles[cycle_name])
+ for s_id in set_idx:
+ self.data[s_id].setColor(next(col), symbol=True, line=True, mode='all')
@QtCore.pyqtSlot(dict, tuple)
def shift_scale(self, values: dict, options: tuple):
diff --git a/nmreval/lib/colors.py b/nmreval/lib/colors.py
index 9bdb6f6..be91545 100644
--- a/nmreval/lib/colors.py
+++ b/nmreval/lib/colors.py
@@ -1,5 +1,6 @@
import enum
import re
+from math import atan2, cos, exp, pi, sin, sqrt
from ..configs import config_paths
@@ -49,10 +50,11 @@ class BaseColor(enum.Enum):
if normed:
return hue/360, saturation, lightness
else:
- return hue, saturation*255, lightness*255
+ return hue, saturation*100, lightness*100
- def xyz(self):
+ def xyz(self, normed=True):
rgb = list(self.rgb(normed=True))
+
for i, val in enumerate(rgb):
rgb[i] = ((val+0.055)/1.055)**2.4 if val > 0.04045 else val/12.92
@@ -60,7 +62,10 @@ class BaseColor(enum.Enum):
y = sum(i*j for i, j in zip([0.2126, 0.7152, 0.0722], rgb))
z = sum(i*j for i, j in zip([0.0193, 0.1192, 0.9505], rgb))
- return x, y, z
+ if normed:
+ return x, y, z
+ else:
+ return x*100, y*100, z*100
def hsv(self, normed=False):
r, g, b = self.rgb(normed=True)
@@ -95,17 +100,17 @@ class BaseColor(enum.Enum):
if normed:
return hue / 360, saturation, col_max
else:
- return hue, saturation * 255, col_max * 255
+ return hue, saturation * 100, col_max * 100
def lab(self):
- x, y, z = self.xyz()
+ x, y, z = self.xyz(normed=False)
x /= 95.0489
y /= 100
z /= 108.884
- x = x**(1 / 3) if x > 0.008856 else 7.787 * x + 16 / 116
- y = y**(1 / 3) if y > 0.008856 else 7.787 * y + 16 / 116
- z = z**(1 / 3) if z > 0.008856 else 7.787 * z + 16 / 116
+ x = x**(1./3.) if x > 0.008856 else 7.787 * x + 16 / 116
+ y = y**(1./3.) if y > 0.008856 else 7.787 * y + 16 / 116
+ z = z**(1./3.) if z > 0.008856 else 7.787 * z + 16 / 116
l = 116 * y - 16
a = 500 * (x - y)
@@ -127,7 +132,11 @@ class BaseColor(enum.Enum):
if normed:
return c, m, y, k
else:
- return c*255, m*255, y*255, k*255
+ return c*100, m*100, y*100, k*100
+
+ def hex(self):
+ r, g, b = self.value
+ return f'#{r:02x}{g:02x}{b:02x}'
@classmethod
def from_rgb(cls, r, g, b, normed=False):
@@ -262,6 +271,19 @@ class Tab20(BaseColor):
TabTurquoise2 = (158, 218, 229)
+class ColorBlind(BaseColor):
+ c0 = (0, 107, 164)
+ c1 = (255, 128, 14)
+ c2 = (171, 171, 171)
+ c3 = (89, 89, 89)
+ c4 = (95, 158, 209)
+ c5 = (200, 82, 0)
+ c6 = (137, 137, 137)
+ c7 = (162, 200, 237)
+ c8 = (255, 188, 121)
+ c9 = (207, 207, 207)
+
+
class GraceColors(BaseColor):
Red = (255, 0, 0)
Green = (0, 255, 0)
@@ -272,7 +294,7 @@ class GraceColors(BaseColor):
Violet = (148, 0, 211)
Cyan = (0, 255, 255)
Magenta = (255, 0, 255)
- Orange = (255, 255, 255)
+ Orange = (255, 165, 0)
Indigo = (114, 33, 188)
Maroon = (103, 7, 72)
Turquoise = (64, 224, 208)
@@ -292,39 +314,120 @@ TUColors = enum.Enum(
Colors = enum.Enum(
value='Colors',
- names={member.name: member.value for palette in [TUColors, TUGrays, GraceColors, Tab10, BlackWhite] for member in palette},
+ names={member.name: member.value for palette in [TUColors, TUGrays, GraceColors] for member in palette},
type=BaseColor,
)
+def ciede2000(c1: BaseColor, c2: BaseColor):
+ """
+ Calculate difference between to colors with CIEDE2000 [1]_
+
+ Args:
+ c1: Color
+ c2: Color
+
+ [1]: G. Sharma, W.Wu, E. N. Dalal: The CIEDE2000 color-difference formula:
+ Implementation notes, supplementary test data, and mathematical observations. Color (2004)
+ https://doi.org/10.1002/col.20070
+
+ """
+ l1, a1, b1 = c1.lab()
+ l2, a2, b2 = c2.lab()
+
+ l_bar = (l1 + l2) / 2
+ delta_l = l2 - l1
+
+ c1 = sqrt(a1**2 + b1**2)
+ c2 = sqrt(a2**2 + b2**2)
+ c_bar = (c1 + c2) / 2
+
+ c_scale = (1 - sqrt(c_bar**7 / (c_bar**7 + 25**7))) / 2
+ a1_prime = a1 * (1 + c_scale)
+ a2_prime = a2 * (1 + c_scale)
+
+ c1_prime = sqrt(a1_prime**2 + b1**2)
+ c2_prime = sqrt(a2_prime**2 + b2**2)
+ c_bar_prime = (c1_prime + c2_prime) / 2
+ delta_c = c2_prime - c1_prime
+
+ h1 = (atan2(b1, a1) * 180 / pi + 360) % 360
+ h2 = (atan2(b2, a2) * 180 / pi + 360) % 360
+
+ if c1_prime * c2_prime == 0:
+ dh = 0
+ else:
+ if abs(h1 - h2) <= 180:
+ dh = h2 - h1
+ else:
+ if h2 > h1:
+ dh = h2 - h1 - 360
+ else:
+ dh = h2 - h1 + 360
+
+ delta_h = 2 * sqrt(c1_prime * c2_prime) * sin(dh * pi / 360)
+
+ if c1 * c2 == 0:
+ h_bar = h1 + h2
+ else:
+ dh_abs = abs(h1 - h2)
+ h_bar = h1 + h2
+ if dh_abs > 180:
+ if h_bar < 360:
+ h_bar += 360
+ else:
+ h_bar -= 360
+ h_bar /= 2
+
+ t = 1 - 0.17 * cos((h_bar - 30) * pi / 180) + 0.25 * cos(h_bar * pi / 90) + 0.32 * cos(
+ (3 * h_bar - 6) * pi / 180) - 0.2 * cos((4 * h_bar - 63) * pi / 180)
+
+ s_l = 1 + 0.015 * (l_bar - 50)**2 / sqrt(20 + (l_bar - 50)**2)
+ s_c = 1 + 0.045 * c_bar_prime
+ s_h = 1 + 0.015 * c_bar_prime * t
+
+ r_t = -2 * sqrt(c_bar_prime**7 / (c_bar_prime**7 + 25**7)) * sin(pi / 3 * exp(-((h_bar - 275) / 25)**2))
+
+ delta_e = (delta_l / s_l)**2 + (delta_c / s_c)**2 + (delta_h / s_h)**2 + r_t * delta_c / s_c * delta_h / s_h
+
+ return sqrt(delta_e)
+
+
def get_palettes():
palettes = {
- 'Full': Colors,
+ 'TuDa': TUColors,
'TuDa:a': TUColorsA,
'TuDa:b': TUColorsB,
'TuDa:c': TUColorsC,
'TuDa:d': TUColorsD,
- 'Tab10': Tab10,
- 'Grace': GraceColors
+ 'TuDa:gray': TUGrays,
+ 'Grace': GraceColors,
+ 'mpl': [Colors.TuDa2b, Colors.TuDa8a, Colors.TuDa4d, Colors.TuDa9b, Colors.TuDa11a,
+ Colors.TuDa8d, Colors.TuDa10a, Colors.TuDa0a, Colors.TuDa5c, Colors.TuDa2a],
+ 'colorblind': [Colors.TuDa2c, Colors.TuDa8a, Colors.TuDa0b, Colors.TuDa0d, Colors.TuDa2a,
+ Colors.TuDa8c, Colors.TuDa0c, Colors.TuDa2b, Colors.TuDa7a, Colors.TuDa0a],
}
- with (config_paths() / 'colorschemes.cfg').open() as f:
- palette_open = False
- for line in f:
- if line.startswith('%--'):
- color_name = line[4:-1]
- palette_open = True
- color_list = []
- continue
+ user_colors = config_paths() / 'colors.cfg'
- if palette_open:
- m = re.match('(\d{1,3}.\d*), (\d{1,3}.\d*), (\d{1,3}.\d*)', line)
- if m:
- color_list.append(Colors.from_rgb(*(float(c) for c in m.groups())))
- elif line == '\n':
- palettes[color_name] = color_list
- palette_open = False
+ if user_colors.exists():
+ with user_colors.open() as f:
+ palette_open = False
+ for line in f:
+ if line.startswith('#--'):
+ color_name = line[4:-1]
+ palette_open = True
+ color_list = []
+ continue
+
+ if palette_open:
+ m = re.match('(\d{1,3}.\d*), (\d{1,3}.\d*), (\d{1,3}.\d*)', line)
+ if m:
+ color_list.append(Colors.from_rgb(*(float(c) for c in m.groups())))
+ elif line == '\n':
+ palettes[color_name] = color_list
+ palette_open = False
return palettes
-
+available_cycles = get_palettes()
\ No newline at end of file
diff --git a/nmreval/models/myfitmodels.py b/nmreval/models/usermodels.py
similarity index 71%
rename from nmreval/models/myfitmodels.py
rename to nmreval/models/usermodels.py
index 4470466..d3293e5 100644
--- a/nmreval/models/myfitmodels.py
+++ b/nmreval/models/usermodels.py
@@ -1,5 +1,5 @@
import numpy as np
-from ..utils.constants import *
+from nmreval.utils.constants import *
r"""
Create user-defined fitfunctions using this template.
@@ -44,23 +44,27 @@ class Template(object):
"""
-class _Template(object):
+class Template:
name = 'Template'
type = 'User defined'
- equation = r'A_{infty} x^{2} - k_{B} \beta_{1} exp(-x) + C_{\delta} + \gamma'
- params = [r'A_{\infty}', r'\beta_{1}']
- bounds = [(0, None), (7, 8)]
- choices = [('a', {'choice 1': 'x', 'choice 2': 'inv_x'}), ('g', gamma)]
+ equation = r'A_{\infty} x^{2} - k_{B} \beta_{1} exp(-x) + C_{\delta} + \gamma'
+ params = [r'A_{\infty}', r'\beta_{1}', r'C_{\delta}']
+ bounds = [(0, None), (7, 8), (None, None)]
+ choices = [('name in dialog', 'c1',
+ {'Increase by': 'increase', 'Invert': 'inv_x'}),
+ ('\gamma', 'g', gamma)]
@staticmethod
- def func(p, x, a, g):
- # p: list of fit parameters and external parameters:
- # [A_infty, beta_1, C_delta]
- # a: first choice (x or inv_x)
- # g: gyromagnetic ratio of chosen nucleus (e.g. gamma['1H'])
- if a == 'x':
- x = x
- elif a == 'inv_x':
- x = 1/x
+ def func(x,
+ a_inf, beta, delta,
+ c1='inv_x', gamma=gamma['1H']):
+
+ if c1 == 'increase':
+ x = x
+ elif c1 == 'inv_x':
+ x = 1/x
+ else:
+ x = x
+
+ return a_inf * x**2 - kB * beta * np.exp(-x) + delta + gamma
- return p[0] * x**2 - kB * p[1] * np.exp(-x) + p[2] + g
diff --git a/resources/Default.agr b/resources/Default.agr
new file mode 100644
index 0000000..cac772b
--- /dev/null
+++ b/resources/Default.agr
@@ -0,0 +1,333 @@
+# Grace project file
+#
+@version 50125
+@page size 841, 595
+@page scroll 5%
+@page inout 5%
+@link page off
+@map font 14 to "Charter-Regular", "Charter-Regular"
+@map font 8 to "Courier", "Courier"
+@map font 10 to "Courier-Bold", "Courier-Bold"
+@map font 11 to "Courier-BoldOblique", "Courier-BoldOblique"
+@map font 15 to "Courier-Italic", "Courier-Italic"
+@map font 9 to "Courier-Oblique", "Courier-Oblique"
+@map font 17 to "Courier-Regular", "Courier-Regular"
+@map font 19 to "Dingbats-Regular", "Dingbats-Regular"
+@map font 20 to "FrontPage-Medium", "FrontPage-Medium"
+@map font 21 to "FrontPage-Regular", "FrontPage-Regular"
+@map font 4 to "Helvetica", "Helvetica"
+@map font 6 to "Helvetica-Bold", "Helvetica-Bold"
+@map font 7 to "Helvetica-BoldOblique", "Helvetica-BoldOblique"
+@map font 5 to "Helvetica-Oblique", "Helvetica-Oblique"
+@map font 34 to "NimbusMonoL-Bold", "NimbusMonoL-Bold"
+@map font 35 to "NimbusMonoL-BoldOblique", "NimbusMonoL-BoldOblique"
+@map font 36 to "NimbusMonoL-Regular", "NimbusMonoL-Regular"
+@map font 37 to "NimbusMonoL-RegularOblique", "NimbusMonoL-RegularOblique"
+@map font 38 to "NimbusRomanNo9L-Medium", "NimbusRomanNo9L-Medium"
+@map font 39 to "NimbusRomanNo9L-MediumItalic", "NimbusRomanNo9L-MediumItalic"
+@map font 40 to "NimbusRomanNo9L-Regular", "NimbusRomanNo9L-Regular"
+@map font 41 to "NimbusRomanNo9L-RegularItalic", "NimbusRomanNo9L-RegularItalic"
+@map font 42 to "NimbusSansL-Bold", "NimbusSansL-Bold"
+@map font 43 to "NimbusSansL-BoldCondensed", "NimbusSansL-BoldCondensed"
+@map font 44 to "NimbusSansL-BoldCondensedItalic", "NimbusSansL-BoldCondensedItalic"
+@map font 45 to "NimbusSansL-BoldItalic", "NimbusSansL-BoldItalic"
+@map font 46 to "NimbusSansL-Regular", "NimbusSansL-Regular"
+@map font 47 to "NimbusSansL-RegularCondensed", "NimbusSansL-RegularCondensed"
+@map font 48 to "NimbusSansL-RegularCondensedItalic", "NimbusSansL-RegularCondensedItalic"
+@map font 49 to "NimbusSansL-RegularItalic", "NimbusSansL-RegularItalic"
+@map font 54 to "Stafford-Regular", "Stafford-Regular"
+@map font 57 to "StandardSymbolsL-Regular", "StandardSymbolsL-Regular"
+@map font 12 to "Symbol", "Symbol"
+@map font 2 to "Times-Bold", "Times-Bold"
+@map font 3 to "Times-BoldItalic", "Times-BoldItalic"
+@map font 1 to "Times-Italic", "Times-Italic"
+@map font 0 to "Times-Roman", "Times-Roman"
+@map font 64 to "URWBookmanL-DemiBold", "URWBookmanL-DemiBold"
+@map font 65 to "URWBookmanL-DemiBoldItalic", "URWBookmanL-DemiBoldItalic"
+@map font 66 to "URWBookmanL-Light", "URWBookmanL-Light"
+@map font 67 to "URWBookmanL-LightItalic", "URWBookmanL-LightItalic"
+@map font 68 to "URWChanceryL-MediumItalic", "URWChanceryL-MediumItalic"
+@map font 69 to "URWGothicL-Book", "URWGothicL-Book"
+@map font 70 to "URWGothicL-BookOblique", "URWGothicL-BookOblique"
+@map font 71 to "URWGothicL-Demi", "URWGothicL-Demi"
+@map font 72 to "URWGothicL-DemiOblique", "URWGothicL-DemiOblique"
+@map font 73 to "URWPalladioL-Bold", "URWPalladioL-Bold"
+@map font 74 to "URWPalladioL-BoldItalic", "URWPalladioL-BoldItalic"
+@map font 75 to "URWPalladioL-Italic", "URWPalladioL-Italic"
+@map font 76 to "URWPalladioL-Roman", "URWPalladioL-Roman"
+@map font 61 to "Utopia-Bold", "Utopia-Bold"
+@map font 62 to "Utopia-BoldItalic", "Utopia-BoldItalic"
+@map font 63 to "Utopia-Italic", "Utopia-Italic"
+@map font 13 to "ZapfDingbats", "ZapfDingbats"
+@map color 0 to (255, 255, 255), "white"
+@map color 1 to (0, 0, 0), "black"
+@map color 2 to (93, 133, 195), "TuDa1a"
+@map color 3 to (0, 156, 218), "TuDa2a"
+@map color 4 to (80, 182, 149), "TuDa3a"
+@map color 5 to (176, 204, 80), "TuDa4a"
+@map color 6 to (221, 223, 72), "TuDa5a"
+@map color 7 to (255, 224, 92), "TuDa6a"
+@map color 8 to (248, 186, 60), "TuDa7a"
+@map color 9 to (238, 122, 52), "TuDa8a"
+@map color 10 to (233, 80, 62), "TuDa9a"
+@map color 11 to (201, 48, 142), "TuDa10a"
+@map color 12 to (128, 69, 151), "TuDa11a"
+@map color 13 to (0, 90, 169), "TuDa1b"
+@map color 14 to (0, 130, 204), "TuDa2b"
+@map color 15 to (0, 157, 129), "TuDa3b"
+@map color 16 to (153, 192, 0), "TuDa4b"
+@map color 17 to (201, 212, 0), "TuDa5b"
+@map color 18 to (253, 202, 0), "TuDa6b"
+@map color 19 to (248, 163, 0), "TuDa7b"
+@map color 20 to (236, 101, 0), "TuDa8b"
+@map color 21 to (239, 0, 26), "TuDa9b"
+@map color 22 to (166, 0, 132), "TuDa10b"
+@map color 23 to (114, 16, 133), "TuDa11b"
+@map color 24 to (0, 78, 138), "TuDa1c"
+@map color 25 to (0, 104, 157), "TuDa2c"
+@map color 26 to (0, 136, 119), "TuDa3c"
+@map color 27 to (127, 171, 22), "TuDa4c"
+@map color 28 to (177, 189, 0), "TuDa5c"
+@map color 29 to (215, 172, 0), "TuDa6c"
+@map color 30 to (210, 135, 0), "TuDa7c"
+@map color 31 to (204, 76, 3), "TuDa8c"
+@map color 32 to (185, 15, 34), "TuDa9c"
+@map color 33 to (149, 17, 105), "TuDa10c"
+@map color 34 to (97, 28, 115), "TuDa11c"
+@map color 35 to (36, 53, 114), "TuDa1d"
+@map color 36 to (0, 78, 115), "TuDa2d"
+@map color 37 to (0, 113, 94), "TuDa3d"
+@map color 38 to (106, 139, 55), "TuDa4d"
+@map color 39 to (153, 166, 4), "TuDa5d"
+@map color 40 to (174, 142, 0), "TuDa6d"
+@map color 41 to (190, 111, 0), "TuDa7d"
+@map color 42 to (169, 73, 19), "TuDa8d"
+@map color 43 to (188, 28, 38), "TuDa9d"
+@map color 44 to (115, 32, 84), "TuDa10d"
+@map color 45 to (76, 34, 106), "TuDa11d"
+@map color 46 to (220, 220, 220), "TuDa0a"
+@map color 47 to (181, 181, 181), "TuDa0b"
+@map color 48 to (137, 137, 137), "TuDa0c"
+@map color 49 to (83, 83, 83), "TuDa0d"
+@map color 50 to (255, 0, 0), "red"
+@map color 51 to (0, 255, 0), "green"
+@map color 52 to (0, 0, 255), "blue"
+@map color 53 to (255, 255, 0), "yellow"
+@map color 54 to (188, 143, 143), "brown"
+@map color 55 to (148, 0, 211), "violet"
+@map color 56 to (0, 255, 255), "cyan"
+@map color 57 to (255, 0, 255), "magenta"
+@map color 58 to (255, 165, 0), "orange"
+@map color 59 to (114, 33, 188), "indigo"
+@map color 60 to (103, 7, 72), "maroon"
+@map color 61 to (64, 224, 208), "turquoise"
+@map color 62 to (0, 139, 0), "green4"
+@reference date 0
+@date wrap off
+@date wrap year 1950
+@default linewidth 1.0
+@default linestyle 1
+@default color 1
+@default pattern 1
+@default font 0
+@default char size 1.000000
+@default symbol size 1.000000
+@default sformat "%.8g"
+@background color 0
+@page background fill on
+@timestamp off
+@timestamp 0.03, 0.03
+@timestamp color 1
+@timestamp rot 0
+@timestamp font 0
+@timestamp char size 1.000000
+@timestamp def "Thu Apr 14 17:47:29 2022"
+@r0 off
+@link r0 to g0
+@r0 type above
+@r0 linestyle 1
+@r0 linewidth 1.0
+@r0 color 1
+@r0 line 0, 0, 0, 0
+@r1 off
+@link r1 to g0
+@r1 type above
+@r1 linestyle 1
+@r1 linewidth 1.0
+@r1 color 1
+@r1 line 0, 0, 0, 0
+@r2 off
+@link r2 to g0
+@r2 type above
+@r2 linestyle 1
+@r2 linewidth 1.0
+@r2 color 1
+@r2 line 0, 0, 0, 0
+@r3 off
+@link r3 to g0
+@r3 type above
+@r3 linestyle 1
+@r3 linewidth 1.0
+@r3 color 1
+@r3 line 0, 0, 0, 0
+@r4 off
+@link r4 to g0
+@r4 type above
+@r4 linestyle 1
+@r4 linewidth 1.0
+@r4 color 1
+@r4 line 0, 0, 0, 0
+@g0 on
+@g0 hidden false
+@g0 type XY
+@g0 stacked false
+@g0 bar hgap 0.000000
+@g0 fixedpoint off
+@g0 fixedpoint type 0
+@g0 fixedpoint xy 0.000000, 0.000000
+@g0 fixedpoint format general general
+@g0 fixedpoint prec 6, 6
+@with g0
+@ world 0, 0, 1, 1
+@ stack world 0, 0, 0, 0
+@ znorm 1
+@ view 0.150000, 0.120000, 1.314214, 0.870000
+@ title ""
+@ title font 4
+@ title size 1.250000
+@ title color 1
+@ subtitle ""
+@ subtitle font 4
+@ subtitle size 1.250000
+@ subtitle color 1
+@ xaxes scale Normal
+@ yaxes scale Normal
+@ xaxes invert off
+@ yaxes invert off
+@ xaxis on
+@ xaxis type zero false
+@ xaxis offset 0.000000 , 0.000000
+@ xaxis bar on
+@ xaxis bar color 1
+@ xaxis bar linestyle 1
+@ xaxis bar linewidth 2.0
+@ xaxis label ""
+@ xaxis label layout para
+@ xaxis label place auto
+@ xaxis label char size 1.250000
+@ xaxis label font 4
+@ xaxis label color 1
+@ xaxis label place normal
+@ xaxis tick on
+@ xaxis tick major 0.2
+@ xaxis tick minor ticks 1
+@ xaxis tick default 6
+@ xaxis tick place rounded true
+@ xaxis tick in
+@ xaxis tick major size 1.000000
+@ xaxis tick major color 1
+@ xaxis tick major linewidth 1.5
+@ xaxis tick major linestyle 1
+@ xaxis tick major grid off
+@ xaxis tick minor color 1
+@ xaxis tick minor linewidth 1.5
+@ xaxis tick minor linestyle 1
+@ xaxis tick minor grid off
+@ xaxis tick minor size 0.500000
+@ xaxis ticklabel on
+@ xaxis ticklabel format general
+@ xaxis ticklabel prec 5
+@ xaxis ticklabel formula ""
+@ xaxis ticklabel append ""
+@ xaxis ticklabel prepend ""
+@ xaxis ticklabel angle 0
+@ xaxis ticklabel skip 0
+@ xaxis ticklabel stagger 0
+@ xaxis ticklabel place normal
+@ xaxis ticklabel offset auto
+@ xaxis ticklabel offset 0.000000 , 0.010000
+@ xaxis ticklabel start type auto
+@ xaxis ticklabel start 0.000000
+@ xaxis ticklabel stop type auto
+@ xaxis ticklabel stop 0.000000
+@ xaxis ticklabel char size 1.250000
+@ xaxis ticklabel font 4
+@ xaxis ticklabel color 1
+@ xaxis tick place both
+@ xaxis tick spec type none
+@ yaxis on
+@ yaxis type zero false
+@ yaxis offset 0.000000 , 0.000000
+@ yaxis bar on
+@ yaxis bar color 1
+@ yaxis bar linestyle 1
+@ yaxis bar linewidth 2.0
+@ yaxis label ""
+@ yaxis label layout para
+@ yaxis label place auto
+@ yaxis label char size 1.250000
+@ yaxis label font 4
+@ yaxis label color 1
+@ yaxis label place normal
+@ yaxis tick on
+@ yaxis tick major 0.2
+@ yaxis tick minor ticks 1
+@ yaxis tick default 6
+@ yaxis tick place rounded true
+@ yaxis tick in
+@ yaxis tick major size 1.000000
+@ yaxis tick major color 1
+@ yaxis tick major linewidth 1.5
+@ yaxis tick major linestyle 1
+@ yaxis tick major grid off
+@ yaxis tick minor color 1
+@ yaxis tick minor linewidth 1.5
+@ yaxis tick minor linestyle 1
+@ yaxis tick minor grid off
+@ yaxis tick minor size 0.500000
+@ yaxis ticklabel on
+@ yaxis ticklabel format general
+@ yaxis ticklabel prec 5
+@ yaxis ticklabel formula ""
+@ yaxis ticklabel append ""
+@ yaxis ticklabel prepend ""
+@ yaxis ticklabel angle 0
+@ yaxis ticklabel skip 0
+@ yaxis ticklabel stagger 0
+@ yaxis ticklabel place normal
+@ yaxis ticklabel offset auto
+@ yaxis ticklabel offset 0.000000 , 0.010000
+@ yaxis ticklabel start type auto
+@ yaxis ticklabel start 0.000000
+@ yaxis ticklabel stop type auto
+@ yaxis ticklabel stop 0.000000
+@ yaxis ticklabel char size 1.250000
+@ yaxis ticklabel font 4
+@ yaxis ticklabel color 1
+@ yaxis tick place both
+@ yaxis tick spec type none
+@ altxaxis off
+@ altyaxis off
+@ legend on
+@ legend loctype view
+@ legend 0.8, 0.8
+@ legend box color 1
+@ legend box pattern 1
+@ legend box linewidth 1.0
+@ legend box linestyle 0
+@ legend box fill color 0
+@ legend box fill pattern 1
+@ legend font 4
+@ legend char size 1.250000
+@ legend color 1
+@ legend length 4
+@ legend vgap 1
+@ legend hgap 1
+@ legend invert false
+@ frame type 0
+@ frame linestyle 1
+@ frame linewidth 1.5
+@ frame color 1
+@ frame pattern 1
+@ frame background color 0
+@ frame background pattern 0
diff --git a/resources/_ui/basewindow.ui b/resources/_ui/basewindow.ui
index 23dfb11..431cc71 100644
--- a/resources/_ui/basewindow.ui
+++ b/resources/_ui/basewindow.ui
@@ -250,7 +250,7 @@
-
+