dialog to create fit functions

This commit is contained in:
Dominik Demuth 2023-01-08 19:30:15 +01:00
parent f05d28f6e6
commit 71f8871445
5 changed files with 60 additions and 16 deletions

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui' # 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 # 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. # 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) self.horizontalLayout.addWidget(self.splitter)
BaseWindow.setCentralWidget(self.centralwidget) BaseWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(BaseWindow) 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.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
@ -413,7 +413,6 @@ class Ui_BaseWindow(object):
self.menuFit.addAction(self.action_create_fit_function) self.menuFit.addAction(self.action_create_fit_function)
self.menuFit.addAction(self.actionFunction_editor) self.menuFit.addAction(self.actionFunction_editor)
self.menuFit.addSeparator() self.menuFit.addSeparator()
self.menuFit.addAction(self.actionShow_fit_parameter)
self.menuFit.addAction(self.menuMethod.menuAction()) self.menuFit.addAction(self.menuMethod.menuAction())
self.menuFit.addAction(self.menuLimits.menuAction()) self.menuFit.addAction(self.menuLimits.menuAction())
self.menuOptions.addAction(self.actionMouse_behaviour) self.menuOptions.addAction(self.actionMouse_behaviour)
@ -480,7 +479,7 @@ class Ui_BaseWindow(object):
self.retranslateUi(BaseWindow) self.retranslateUi(BaseWindow)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
self.action_close.triggered.connect(BaseWindow.close) # type: ignore self.action_close.triggered.connect(BaseWindow.close)
QtCore.QMetaObject.connectSlotsByName(BaseWindow) QtCore.QMetaObject.connectSlotsByName(BaseWindow)
def retranslateUi(self, BaseWindow): def retranslateUi(self, BaseWindow):

View File

@ -2,7 +2,8 @@ from __future__ import annotations
import inspect import inspect
import numbers import numbers
import textwrap import pathlib
import re
from typing import Any from typing import Any
import numpy as np import numpy as np
@ -14,15 +15,18 @@ from gui_qt.lib.namespace import QNamespaceWidget
__all__ = ['QUserFitCreator'] __all__ = ['QUserFitCreator']
validator = QtGui.QRegExpValidator(QtCore.QRegExp('[A-Za-z]\S*')) validator = QtGui.QRegExpValidator(QtCore.QRegExp('[A-Za-z]\S*'))
pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE)
class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog): 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) super().__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
self.filepath = pathlib.Path(filepath)
self.description_widget = DescWidget(self) self.description_widget = DescWidget(self)
self.args_widget = ArgWidget(self) self.args_widget = ArgWidget(self)
self.kwargs_widget = KwargsWidget(self) self.kwargs_widget = KwargsWidget(self)
@ -44,10 +48,23 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
self.update_function() self.update_function()
def __call__(self, *args, **kwargs): def __call__(self, filepath: str|pathlib.Path):
self.filepath = pathlib.Path(filepath)
return self return self
def update_function(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: try:
var = self.args_widget.get_parameter() var = self.args_widget.get_parameter()
var += self.kwargs_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 += self.kwargs_widget.get_strings()
k += '\n @staticmethod\n' k += '\n @staticmethod\n'
if var:
k += f" def func(x, {', '.join(var)}):\n" k += f" def func(x, {', '.join(var)}):\n"
else:
k += f' def func(x):\n'
k += func_body
self.plainTextEdit.setPlainText(k) self.plainTextEdit.setPlainText(k)
except Exception as e: except Exception as e:
@ -85,6 +107,7 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
ns = self.namespace_widget.namespace.namespace ns = self.namespace_widget.namespace.namespace
func_value = ns[invalue][0] func_value = ns[invalue][0]
ret_func = '' ret_func = ''
import_name = ''
if func_value is None: if func_value is None:
ret_func = invalue ret_func = invalue
@ -99,15 +122,28 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
else: else:
f_string = ns[invalue][-1] f_string = ns[invalue][-1]
args = f_string[f_string.find('('):] args = f_string[f_string.find('('):]
if inspect.ismethod(func_value): if inspect.ismethod(func_value):
ret_func = func_value.__self__.__name__ + '.func'+args ret_func = func_value.__self__.__name__ + '.func'+args
import_name = func_value.__self__.__name__
elif hasattr(func_value, '__qualname__'): elif hasattr(func_value, '__qualname__'):
ret_func = func_value.__qualname__.split('.')[0] import_name = func_value.__qualname__.split('.')[0]
self._imports.add((inspect.getmodule(func_value).__name__, ret_func)) ret_func = func_value.__qualname__ + args
self._imports.add((inspect.getmodule(func_value).__name__, import_name))
self.plainTextEdit.insertPlainText(ret_func) self.plainTextEdit.insertPlainText(ret_func)
self.update_function() 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): class KwargsWidget(QtWidgets.QWidget):
Changed = QtCore.pyqtSignal() Changed = QtCore.pyqtSignal()
@ -363,7 +399,7 @@ class ArgWidget(QtWidgets.QWidget):
return var return var
def get_strings(self): def get_strings(self) -> str:
args = [] args = []
bnds = [] bnds = []
for row in range(self.table.rowCount()): for row in range(self.table.rowCount()):
@ -434,7 +470,7 @@ class DescWidget(QtWidgets.QWidget):
raise ValueError('Class name is empty') raise ValueError('Class name is empty')
stringi = f'class {self.klass_lineedit.text()}:\n' \ stringi = f'class {self.klass_lineedit.text()}:\n' \
f' name = {self.name_lineedit.text()!r}\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' f' equation = {self.eq_lineedit.text()!r}\n'
return stringi return stringi

View File

@ -13,7 +13,7 @@ import requests
from numpy import linspace from numpy import linspace
from scipy.interpolate import interp1d from scipy.interpolate import interp1d
from gui_qt.Qt import QtGui, QtWidgets, QtCore from ..Qt import QtGui, QtWidgets, QtCore
@contextmanager @contextmanager

View File

@ -917,6 +917,16 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.editor.setWindowModality(QtCore.Qt.ApplicationModal) self.editor.setWindowModality(QtCore.Qt.ApplicationModal)
self.editor.show() 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') @QtCore.pyqtSlot(name='on_actionShift_triggered')
def shift_dialog(self): def shift_dialog(self):
s = QShift(self) s = QShift(self)

View File

@ -136,7 +136,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1386</width> <width>1386</width>
<height>20</height> <height>24</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -251,7 +251,6 @@
<addaction name="action_create_fit_function"/> <addaction name="action_create_fit_function"/>
<addaction name="actionFunction_editor"/> <addaction name="actionFunction_editor"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionShow_fit_parameter"/>
<addaction name="menuMethod"/> <addaction name="menuMethod"/>
<addaction name="menuLimits"/> <addaction name="menuLimits"/>
</widget> </widget>