From dc906972e90474be6429365e01b57f780872bc11 Mon Sep 17 00:00:00 2001 From: dominik Date: Sat, 16 Apr 2022 20:41:26 +0200 Subject: [PATCH] bugfix: update sets after graph selection; check for config on start color cycles added; --- bin/evaluate.py | 5 + nmreval/configs.py | 31 +- nmreval/gui_qt/_py/basewindow.py | 8 +- nmreval/gui_qt/_py/color_palette.py | 78 ++-- nmreval/gui_qt/data/container.py | 42 +-- nmreval/gui_qt/data/datawidget/datawidget.py | 31 +- nmreval/gui_qt/graphs/graphwindow.py | 8 +- nmreval/gui_qt/lib/color_dialog.py | 19 +- nmreval/gui_qt/lib/delegates.py | 4 +- nmreval/gui_qt/lib/forms.py | 4 +- nmreval/gui_qt/lib/pg_objects.py | 22 ++ nmreval/gui_qt/lib/utils.py | 4 +- nmreval/gui_qt/main/mainwindow.py | 6 + nmreval/gui_qt/main/management.py | 10 +- nmreval/lib/colors.py | 163 +++++++-- .../models/{myfitmodels.py => usermodels.py} | 36 +- resources/Default.agr | 333 ++++++++++++++++++ resources/_ui/basewindow.ui | 6 +- resources/_ui/color_palette.ui | 140 +++++--- 19 files changed, 749 insertions(+), 201 deletions(-) rename nmreval/models/{myfitmodels.py => usermodels.py} (71%) create mode 100644 resources/Default.agr 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 @@ - + @@ -665,9 +665,9 @@ &Create fit function... - + - &Grace preferences... + Color cycles... diff --git a/resources/_ui/color_palette.ui b/resources/_ui/color_palette.ui index 041b3af..1db3aff 100644 --- a/resources/_ui/color_palette.ui +++ b/resources/_ui/color_palette.ui @@ -11,30 +11,25 @@ - Color Palette + Color cycles - - - - Append - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + @@ -44,7 +39,7 @@ - + @@ -57,34 +52,7 @@ - - - - - 0 - 0 - - - - - - - - Save as new list - - - - - - - Load - - - - - - - + @@ -97,7 +65,17 @@ - + + + + Load + + + + + + + Qt::Horizontal @@ -107,6 +85,64 @@ + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + + + + Save as new list + + + + + + + Color cycles + + + palette_combobox + + + + + + + Append + + + + + + + Available color + + + color_combobox + + + + + + + Qt::Horizontal + + +