From 71f8871445f3cc87523ee1741c9df1462e1da49d Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Sun, 8 Jan 2023 19:30:15 +0100 Subject: [PATCH] dialog to create fit functions --- src/gui_qt/_py/basewindow.py | 7 ++- src/gui_qt/fit/function_creation_dialog.py | 54 ++++++++++++++++++---- src/gui_qt/lib/utils.py | 2 +- src/gui_qt/main/mainwindow.py | 10 ++++ src/resources/_ui/basewindow.ui | 3 +- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index 42d4e34..a5e7ab9 100644 --- a/src/gui_qt/_py/basewindow.py +++ b/src/gui_qt/_py/basewindow.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui' # -# Created by: PyQt5 UI code generator 5.15.7 +# Created by: PyQt5 UI code generator 5.15.2 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -75,7 +75,7 @@ class Ui_BaseWindow(object): self.horizontalLayout.addWidget(self.splitter) BaseWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(BaseWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1386, 20)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1386, 24)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") @@ -413,7 +413,6 @@ class Ui_BaseWindow(object): self.menuFit.addAction(self.action_create_fit_function) self.menuFit.addAction(self.actionFunction_editor) self.menuFit.addSeparator() - self.menuFit.addAction(self.actionShow_fit_parameter) self.menuFit.addAction(self.menuMethod.menuAction()) self.menuFit.addAction(self.menuLimits.menuAction()) self.menuOptions.addAction(self.actionMouse_behaviour) @@ -480,7 +479,7 @@ class Ui_BaseWindow(object): self.retranslateUi(BaseWindow) self.tabWidget.setCurrentIndex(0) - self.action_close.triggered.connect(BaseWindow.close) # type: ignore + self.action_close.triggered.connect(BaseWindow.close) QtCore.QMetaObject.connectSlotsByName(BaseWindow) def retranslateUi(self, BaseWindow): diff --git a/src/gui_qt/fit/function_creation_dialog.py b/src/gui_qt/fit/function_creation_dialog.py index 8d6c2a0..b63ee29 100644 --- a/src/gui_qt/fit/function_creation_dialog.py +++ b/src/gui_qt/fit/function_creation_dialog.py @@ -2,7 +2,8 @@ from __future__ import annotations import inspect import numbers -import textwrap +import pathlib +import re from typing import Any import numpy as np @@ -14,15 +15,18 @@ from gui_qt.lib.namespace import QNamespaceWidget __all__ = ['QUserFitCreator'] validator = QtGui.QRegExpValidator(QtCore.QRegExp('[A-Za-z]\S*')) +pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE) class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog): - classCreated = QtCore.pyqtSignal(object) + classCreated = QtCore.pyqtSignal() - def __init__(self, parent=None): + def __init__(self, filepath: str|pathlib.Path, parent=None): super().__init__(parent=parent) self.setupUi(self) + self.filepath = pathlib.Path(filepath) + self.description_widget = DescWidget(self) self.args_widget = ArgWidget(self) self.kwargs_widget = KwargsWidget(self) @@ -44,10 +48,23 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog): self.update_function() - def __call__(self, *args, **kwargs): + def __call__(self, filepath: str|pathlib.Path): + self.filepath = pathlib.Path(filepath) + return self def update_function(self): + prev_text = self.plainTextEdit.toPlainText().split('\n') + func_body = '' + in_body = False + for line in prev_text: + if in_body: + func_body += line + continue + + if pattern.search(line) is not None: + in_body = True + try: var = self.args_widget.get_parameter() var += self.kwargs_widget.get_parameter() @@ -67,7 +84,12 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog): k += self.kwargs_widget.get_strings() k += '\n @staticmethod\n' - k += f" def func(x, {', '.join(var)}):\n" + if var: + k += f" def func(x, {', '.join(var)}):\n" + else: + k += f' def func(x):\n' + + k += func_body self.plainTextEdit.setPlainText(k) except Exception as e: @@ -85,6 +107,7 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog): ns = self.namespace_widget.namespace.namespace func_value = ns[invalue][0] ret_func = '' + import_name = '' if func_value is None: ret_func = invalue @@ -99,15 +122,28 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog): else: f_string = ns[invalue][-1] args = f_string[f_string.find('('):] + if inspect.ismethod(func_value): ret_func = func_value.__self__.__name__ + '.func'+args + import_name = func_value.__self__.__name__ + elif hasattr(func_value, '__qualname__'): - ret_func = func_value.__qualname__.split('.')[0] - self._imports.add((inspect.getmodule(func_value).__name__, ret_func)) + import_name = func_value.__qualname__.split('.')[0] + ret_func = func_value.__qualname__ + args + + self._imports.add((inspect.getmodule(func_value).__name__, import_name)) self.plainTextEdit.insertPlainText(ret_func) self.update_function() + def accept(self): + # maybe add a check for correctness + + with self.filepath.open('a') as f: + f.write('\n\n') + f.write(self.plainTextEdit.toPlainText()) + self.classCreated.emit() + super().accept() class KwargsWidget(QtWidgets.QWidget): Changed = QtCore.pyqtSignal() @@ -363,7 +399,7 @@ class ArgWidget(QtWidgets.QWidget): return var - def get_strings(self): + def get_strings(self) -> str: args = [] bnds = [] for row in range(self.table.rowCount()): @@ -434,7 +470,7 @@ class DescWidget(QtWidgets.QWidget): raise ValueError('Class name is empty') stringi = f'class {self.klass_lineedit.text()}:\n' \ f' name = {self.name_lineedit.text()!r}\n' \ - f' group = {self.group_lineedit.text()!r}\n' \ + f' type = {self.group_lineedit.text()!r}\n' \ f' equation = {self.eq_lineedit.text()!r}\n' return stringi diff --git a/src/gui_qt/lib/utils.py b/src/gui_qt/lib/utils.py index bafa3c6..c632038 100644 --- a/src/gui_qt/lib/utils.py +++ b/src/gui_qt/lib/utils.py @@ -13,7 +13,7 @@ import requests from numpy import linspace from scipy.interpolate import interp1d -from gui_qt.Qt import QtGui, QtWidgets, QtCore +from ..Qt import QtGui, QtWidgets, QtCore @contextmanager diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 1ccad4a..c9fc62b 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -917,6 +917,16 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.editor.setWindowModality(QtCore.Qt.ApplicationModal) self.editor.show() + @QtCore.pyqtSlot(name='on_action_create_fit_function_triggered') + def open_fitmodel_wizard(self): + from ..fit.function_creation_dialog import QUserFitCreator + + helper = QUserFitCreator(config_paths() / 'usermodels.py', parent=self) + helper.classCreated.connect(lambda: self.fit_dialog.read_and_load_functions()) + + helper.show() + + @QtCore.pyqtSlot(name='on_actionShift_triggered') def shift_dialog(self): s = QShift(self) diff --git a/src/resources/_ui/basewindow.ui b/src/resources/_ui/basewindow.ui index d80e9f0..0cb9372 100644 --- a/src/resources/_ui/basewindow.ui +++ b/src/resources/_ui/basewindow.ui @@ -136,7 +136,7 @@ 0 0 1386 - 20 + 24 @@ -251,7 +251,6 @@ -