From d9f1c0b8c24f842ae23f393d61b5dfc2e227187d Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Wed, 25 Sep 2024 17:20:20 +0200 Subject: [PATCH 1/6] use correct attribute name --- src/gui_qt/lib/usermodeleditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui_qt/lib/usermodeleditor.py b/src/gui_qt/lib/usermodeleditor.py index d9e5761..28d2289 100644 --- a/src/gui_qt/lib/usermodeleditor.py +++ b/src/gui_qt/lib/usermodeleditor.py @@ -126,7 +126,7 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self.is_modified = False def closeEvent(self, evt: QtGui.QCloseEvent): - if not self.changes_saved: + if self.is_modified: evt.ignore() else: super().closeEvent(evt) From ff2ff01da7d707439a9d1c10423700bbfbfdcb50 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Wed, 25 Sep 2024 17:21:46 +0200 Subject: [PATCH 2/6] update internal fit infos after function removal --- src/gui_qt/fit/fit_parameter.py | 9 ++++++--- src/gui_qt/fit/fitwindow.py | 10 ++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/gui_qt/fit/fit_parameter.py b/src/gui_qt/fit/fit_parameter.py index e470691..756fb3f 100644 --- a/src/gui_qt/fit/fit_parameter.py +++ b/src/gui_qt/fit/fit_parameter.py @@ -12,12 +12,13 @@ from ..lib.forms import SelectionWidget class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): value_requested = QtCore.pyqtSignal(int) - def __init__(self, parent=None): + def __init__(self, func_id: int, parent=None): super().__init__(parent=parent) self.setupUi(self) self.func = None self.func_idx = None + self.func_id = func_id self.max_width = QtCore.QSize(0, 0) self.global_parameter = [] self.data_parameter = [] @@ -301,8 +302,10 @@ class ParameterSingleWidget(QtWidgets.QWidget): self.name = name self.parametername.setText(convert(name)) - self.parametername.setToolTip('If this is bold then this parameter is only for this data. ' - 'Otherwise, the general parameter is used and displayed') + self.parametername.setToolTip( + 'If this is bold then this parameter is only for this data. ' + 'Otherwise, the general parameter is used and displayed' + ) # self.value_line.setValidator(QtGui.QDoubleValidator()) self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0) diff --git a/src/gui_qt/fit/fitwindow.py b/src/gui_qt/fit/fitwindow.py index 6700493..9aeaed3 100644 --- a/src/gui_qt/fit/fitwindow.py +++ b/src/gui_qt/fit/fitwindow.py @@ -77,8 +77,12 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): """ w = self.param_widgets[idx] self.stackedWidget.removeWidget(w) + w.setParent(None) w.deleteLater() del self.param_widgets[idx] + _, func_id = self.functionwidget.get_selected() + + self.get_functions() self._current_function = None if len(self.param_widgets) == 0: @@ -104,7 +108,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): if function is None: return - dialog = QFitParameterWidget(self.stackedWidget) + dialog = QFitParameterWidget(function_id, self.stackedWidget) data_names = self.data_table.data_list(include_name=True) dialog.set_function(function, function_idx) @@ -206,9 +210,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): for m in self.models[model_id]: func_id = m['cnt'] - self.stackedWidget.removeWidget(self.param_widgets[func_id]) - - self.param_widgets.pop(func_id) + self.remove_function(func_id) self._complex.pop(model_id) self._func_list.pop(model_id) From 7ad1e4b84358eea3e35e67500b8841f1a2c78609 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Wed, 25 Sep 2024 18:09:10 +0200 Subject: [PATCH 3/6] use of properties did not work properly --- src/gui_qt/lib/codeeditor.py | 8 ++++---- src/gui_qt/lib/usermodeleditor.py | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/gui_qt/lib/codeeditor.py b/src/gui_qt/lib/codeeditor.py index f910b26..970b513 100644 --- a/src/gui_qt/lib/codeeditor.py +++ b/src/gui_qt/lib/codeeditor.py @@ -225,7 +225,7 @@ class CodeEditor(QtWidgets.QPlainTextEdit): def paintevent_linenumber(self, evt): painter = QtGui.QPainter(self.current_linenumber) - painter.fillRect(evt.rect(), QtCore.Qt.lightGray) + painter.fillRect(evt.rect(), QtCore.Qt.GlobalColor.lightGray) block = self.firstVisibleBlock() block_number = block.blockNumber() @@ -237,9 +237,9 @@ class CodeEditor(QtWidgets.QPlainTextEdit): while block.isValid() and (top <= evt.rect().bottom()): if block.isVisible() and (bottom >= evt.rect().top()): number = str(block_number + 1) - painter.setPen(QtCore.Qt.black) + painter.setPen(QtCore.Qt.GlobalColor.black) painter.drawText(0, int(top), self.current_linenumber.width() - 3, height, - QtCore.Qt.AlignRight, number) + QtCore.Qt.AlignmentFlag.AlignRight, number) block = block.next() top = bottom @@ -252,7 +252,7 @@ class CodeEditor(QtWidgets.QPlainTextEdit): if not self.isReadOnly(): selection = QtWidgets.QTextEdit.ExtraSelection() - line_color = QtGui.QColor(QtCore.Qt.yellow).lighter(180) + line_color = QtGui.QColor(QtCore.Qt.GlobalColor.yellow).lighter(180) selection.format.setBackground(line_color) selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True) diff --git a/src/gui_qt/lib/usermodeleditor.py b/src/gui_qt/lib/usermodeleditor.py index 28d2289..79867ae 100644 --- a/src/gui_qt/lib/usermodeleditor.py +++ b/src/gui_qt/lib/usermodeleditor.py @@ -55,13 +55,11 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self.size(), QtWidgets.qApp.desktop().availableGeometry() )) - @property def is_modified(self): - return self.edit_field.document().isModified() + return self.edit_field.editor.document().isModified() - @is_modified.setter - def is_modified(self, val: bool): - self.edit_field.document().setModified(val) + def set_modified(self, val: bool): + self.edit_field.editor.document().setModified(val) @QtCore.pyqtSlot() def open_file(self): @@ -83,9 +81,8 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self._dir = self.fname.parent self.setWindowTitle('Edit ' + str(fname)) - @property def changes_saved(self) -> bool: - if not self.is_modified: + if not self.is_modified(): return True ret = QtWidgets.QMessageBox.question(self, 'Time to think', @@ -97,9 +94,9 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self.save_file() if ret == QtWidgets.QMessageBox.No: - self.is_modified = False + self.set_modified(False) - return not self.is_modified + return not self.is_modified() @QtCore.pyqtSlot() def save_file(self): @@ -111,9 +108,9 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self.set_fname_opts(outfile) - self.is_modified = False + self.set_modified(False) - return self.is_modified + return self.is_modified() @QtCore.pyqtSlot() def overwrite_file(self): @@ -123,10 +120,10 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self.modelsChanged.emit() - self.is_modified = False + self.set_modified(False) def closeEvent(self, evt: QtGui.QCloseEvent): - if self.is_modified: + if not self.changes_saved(): evt.ignore() else: super().closeEvent(evt) From d07b85ae27bfb8697188ee01e3ade0da1bd9fa4e Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Thu, 26 Sep 2024 18:39:55 +0200 Subject: [PATCH 4/6] mvp for script runner --- src/gui_qt/_py/basewindow.py | 4 + src/gui_qt/editors/__init__.py | 0 src/gui_qt/{lib => editors}/codeeditor.py | 4 +- src/gui_qt/editors/script_editor.py | 137 ++++++++++++++++++ .../{lib => editors}/usermodeleditor.py | 14 +- src/gui_qt/fit/function_creation_dialog.py | 6 +- src/gui_qt/lib/logger.py | 2 +- src/gui_qt/main/mainwindow.py | 10 +- src/gui_qt/main/management.py | 18 ++- src/resources/_ui/basewindow.ui | 6 + 10 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 src/gui_qt/editors/__init__.py rename src/gui_qt/{lib => editors}/codeeditor.py (99%) create mode 100644 src/gui_qt/editors/script_editor.py rename src/gui_qt/{lib => editors}/usermodeleditor.py (92%) diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index 8d23235..cca4f0f 100644 --- a/src/gui_qt/_py/basewindow.py +++ b/src/gui_qt/_py/basewindow.py @@ -372,6 +372,8 @@ class Ui_BaseWindow(object): self.action_cut_xaxis.setObjectName("action_cut_xaxis") self.action_cut_yaxis = QtWidgets.QAction(BaseWindow) self.action_cut_yaxis.setObjectName("action_cut_yaxis") + self.actionUse_script = QtWidgets.QAction(BaseWindow) + self.actionUse_script.setObjectName("actionUse_script") self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.action_save_fit_parameter) @@ -399,6 +401,7 @@ class Ui_BaseWindow(object): self.menuData.addAction(self.menuCut_to_visible_range.menuAction()) self.menuData.addSeparator() self.menuData.addAction(self.actionChange_datatypes) + self.menuData.addAction(self.actionUse_script) self.menuHelp.addAction(self.actionShow_error_log) self.menuHelp.addAction(self.actionUpdate) self.menuHelp.addAction(self.actionBugs) @@ -647,6 +650,7 @@ class Ui_BaseWindow(object): self.action_cut_xaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible x range.")) self.action_cut_yaxis.setText(_translate("BaseWindow", "y axis")) self.action_cut_yaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible y range. Uses real part of points.")) + self.actionUse_script.setText(_translate("BaseWindow", "Use script...")) from ..data.datawidget.datawidget import DataWidget from ..data.integral_widget import IntegralWidget from ..data.point_select import PointSelectWidget diff --git a/src/gui_qt/editors/__init__.py b/src/gui_qt/editors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/gui_qt/lib/codeeditor.py b/src/gui_qt/editors/codeeditor.py similarity index 99% rename from src/gui_qt/lib/codeeditor.py rename to src/gui_qt/editors/codeeditor.py index 970b513..a90840f 100644 --- a/src/gui_qt/lib/codeeditor.py +++ b/src/gui_qt/editors/codeeditor.py @@ -187,10 +187,10 @@ class CodeEditor(QtWidgets.QPlainTextEdit): self.highlight = PythonHighlighter(self.document()) def keyPressEvent(self, evt): - if evt.key() == QtCore.Qt.Key_Tab: + if evt.key() == QtCore.Qt.Key.Key_Tab: # use spaces instead of tab self.insertPlainText(' '*4) - elif evt.key() == QtCore.Qt.Key_Insert: + elif evt.key() == QtCore.Qt.Key.Key_Insert: self.setOverwriteMode(not self.overwriteMode()) else: super().keyPressEvent(evt) diff --git a/src/gui_qt/editors/script_editor.py b/src/gui_qt/editors/script_editor.py new file mode 100644 index 0000000..4bf4dd9 --- /dev/null +++ b/src/gui_qt/editors/script_editor.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +from pathlib import Path + +from ..Qt import QtWidgets, QtCore, QtGui +from .codeeditor import EditorWidget + + +class QEditor(QtWidgets.QMainWindow): + runSignal = QtCore.pyqtSignal(str) + + def __init__(self, path: str | Path = None, parent=None): + super().__init__(parent=parent) + + self._init_gui() + + self.fname = None + self._dir = None + if path is not None: + self.read_file(path) + + def _init_gui(self): + self.centralwidget = QtWidgets.QWidget(self) + + layout = QtWidgets.QVBoxLayout(self.centralwidget) + layout.setContentsMargins(3, 3, 3, 3) + layout.setSpacing(3) + + self.edit_field = EditorWidget(self.centralwidget) + font = QtGui.QFont('default') + font.setStyleHint(font.Monospace) + font.setPointSize(10) + self.edit_field.setFont(font) + + layout.addWidget(self.edit_field) + self.setCentralWidget(self.centralwidget) + + self.statusbar = QtWidgets.QStatusBar(self) + self.setStatusBar(self.statusbar) + + self.menubar = self.menuBar() + + self.menuFile = QtWidgets.QMenu('File', self.menubar) + self.menubar.addMenu(self.menuFile) + + self.menuFile.addAction('Open...', self.open_file, QtGui.QKeySequence.Open) + self.menuFile.addAction('Save', self.overwrite_file, QtGui.QKeySequence.Save) + self.menuFile.addAction('Save as...', self.save_file, QtGui.QKeySequence('Ctrl+Shift+S')) + self.menuFile.addSeparator() + self.menuFile.addAction('Close', self.close, QtGui.QKeySequence.Quit) + + self.resize(800, 600) + self.setGeometry( + QtWidgets.QStyle.alignedRect( + QtCore.Qt.LayoutDirection.LeftToRight, + QtCore.Qt.AlignmentFlag.AlignCenter, + self.size(), + QtWidgets.qApp.desktop().availableGeometry() + ) + ) + + def is_modified(self): + return self.edit_field.editor.document().isModified() + + def set_modified(self, val: bool): + self.edit_field.editor.document().setModified(val) + + @QtCore.pyqtSlot() + def open_file(self): + overwrite = self.changes_saved + + if overwrite: + fname, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self._dir)) + if fname: + self.read_file(fname) + + def read_file(self, fname: str | Path): + self.set_fname_opts(fname) + + if self.fname is not None: + with self.fname.open('r') as f: + self.edit_field.setPlainText(f.read()) + + def set_fname_opts(self, fname: str | Path): + fname = Path(fname) + if fname.is_file(): + self.fname = Path(fname) + self._dir = self.fname.parent + elif fname.is_dir(): + self._dir = fname + self.setWindowTitle('Edit ' + str(fname)) + + def changes_saved(self) -> bool: + if not self.is_modified(): + return True + + ret = QtWidgets.QMessageBox.question(self, 'Time to think', + '

The document was modified.

\n' + '

Do you want to save changes?

', + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | + QtWidgets.QMessageBox.Cancel) + if ret == QtWidgets.QMessageBox.Yes: + self.save_file() + + if ret == QtWidgets.QMessageBox.No: + self.set_modified(False) + + return not self.is_modified() + + @QtCore.pyqtSlot() + def save_file(self): + outfile, _ = QtWidgets.QFileDialog().getSaveFileName(parent=self, caption='Save file', directory=str(self._dir)) + + if outfile: + with open(outfile, 'w') as f: + f.write(self.edit_field.toPlainText()) + + self.set_fname_opts(outfile) + + self.set_modified(False) + + return self.is_modified() + + @QtCore.pyqtSlot() + def overwrite_file(self): + if self.fname is not None: + with self.fname.open('w') as f: + f.write(self.edit_field.toPlainText()) + + self.modelsChanged.emit() + + self.set_modified(False) + + def closeEvent(self, evt: QtGui.QCloseEvent): + self.runSignal.emit(self.edit_field.toPlainText()) + + super().closeEvent(evt) diff --git a/src/gui_qt/lib/usermodeleditor.py b/src/gui_qt/editors/usermodeleditor.py similarity index 92% rename from src/gui_qt/lib/usermodeleditor.py rename to src/gui_qt/editors/usermodeleditor.py index 79867ae..e912aa0 100644 --- a/src/gui_qt/lib/usermodeleditor.py +++ b/src/gui_qt/editors/usermodeleditor.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path from ..Qt import QtWidgets, QtCore, QtGui -from ..lib.codeeditor import EditorWidget +from .codeeditor import EditorWidget class QUsermodelEditor(QtWidgets.QMainWindow): @@ -50,10 +50,14 @@ class QUsermodelEditor(QtWidgets.QMainWindow): self.menuFile.addAction('Close', self.close, QtGui.QKeySequence.Quit) self.resize(800, 600) - self.setGeometry(QtWidgets.QStyle.alignedRect( - QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, - self.size(), QtWidgets.qApp.desktop().availableGeometry() - )) + self.setGeometry( + QtWidgets.QStyle.alignedRect( + QtCore.Qt.LayoutDirection.LeftToRight, + QtCore.Qt.AlignmentFlag.AlignCenter, + self.size(), + QtWidgets.qApp.desktop().availableGeometry() + ) + ) def is_modified(self): return self.edit_field.editor.document().isModified() diff --git a/src/gui_qt/fit/function_creation_dialog.py b/src/gui_qt/fit/function_creation_dialog.py index 25562cd..caf7d42 100644 --- a/src/gui_qt/fit/function_creation_dialog.py +++ b/src/gui_qt/fit/function_creation_dialog.py @@ -8,9 +8,9 @@ from typing import Any import numpy as np -from gui_qt.Qt import QtCore, QtWidgets, QtGui -from gui_qt._py.fitcreationdialog import Ui_Dialog -from gui_qt.lib.namespace import QNamespaceWidget +from ..Qt import QtCore, QtWidgets, QtGui +from .._py.fitcreationdialog import Ui_Dialog +from ..editors.namespace import QNamespaceWidget __all__ = ['QUserFitCreator'] diff --git a/src/gui_qt/lib/logger.py b/src/gui_qt/lib/logger.py index 346b638..e495ee1 100644 --- a/src/gui_qt/lib/logger.py +++ b/src/gui_qt/lib/logger.py @@ -3,7 +3,7 @@ from pathlib import Path from PyQt5 import QtWidgets -from .codeeditor import _make_textformats +from ..editors.codeeditor import _make_textformats from ..Qt import QtWidgets, QtCore, QtGui from nmreval.configs import config_paths diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index bf2825c..d4c28eb 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -985,13 +985,21 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): @QtCore.pyqtSlot(name='on_actionFunction_editor_triggered') def edit_models(self): if self.editor is None: - from ..lib.usermodeleditor import QUsermodelEditor + from ..editors.usermodeleditor import QUsermodelEditor self.editor = QUsermodelEditor(config_paths() / 'usermodels.py', parent=self) self.editor.modelsChanged.connect(lambda: self.fit_dialog.read_and_load_functions()) self.editor.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) self.editor.show() + @QtCore.pyqtSlot(name='on_actionUse_script_triggered') + def open_editor(self): + from ..editors.script_editor import QEditor + + editor = QEditor(self.path, parent=self) + editor.runSignal.connect(self.management.run_script) + editor.show() + @QtCore.pyqtSlot(list, bool) def extend_fit(self, sets: list, only_subplots: bool): if only_subplots: diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index 21715fa..ef2022d 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -1067,6 +1067,8 @@ class UpperManagement(QtCore.QObject): @QtCore.pyqtSlot(list, list, bool) def eval_expression(self, cmds: list, set_ids: list, overwrite: bool): + if self.namespace is None: + self.namespace = self.get_namespace() ns = self.namespace.flatten() if overwrite: @@ -1099,13 +1101,27 @@ class UpperManagement(QtCore.QObject): if failures: err_msg = QtWidgets.QMessageBox(parent=self.sender()) - err_msg.setText('One or more errors occured during evaluation.') + err_msg.setText('One or more errors occurred during evaluation.') err_msg.setDetailedText('\n'.join(f'{d.name} failed with error: {err.args}' for d, err in failures)) err_msg.exec() self.sender().success = not failures self.sender().add_data(self.active_sets) + @QtCore.pyqtSlot(str) + def run_script(self, text): + self.namespace = self.get_namespace() + ns = self.namespace.flatten() + ns['return_list'] = [] + + exec(text, globals(), ns) + + new_sets = [] + for new_data in ns['return_list']: + new_sets.append(self.add(new_data)) + + self.newData.emit(new_sets, '') + @QtCore.pyqtSlot(list, dict) def create_from_function(self, cmds: list, opts: dict): ns = dict(self.namespace.flatten()) diff --git a/src/resources/_ui/basewindow.ui b/src/resources/_ui/basewindow.ui index 85c1f68..e2c4cab 100644 --- a/src/resources/_ui/basewindow.ui +++ b/src/resources/_ui/basewindow.ui @@ -192,6 +192,7 @@ + @@ -1049,6 +1050,11 @@ Remove data points outside visible y range. Uses real part of points. + + + Use script... + + From f185b2496734e926f4ca3f1b6cd01b034f8fb144 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Sun, 29 Sep 2024 14:33:28 +0200 Subject: [PATCH 5/6] fix problem with namespaces --- src/gui_qt/editors/script_editor.py | 129 ++------------------------ src/gui_qt/editors/usermodeleditor.py | 16 +++- src/gui_qt/io/fcbatchreader.py | 1 - src/gui_qt/lib/namespace.py | 3 + src/gui_qt/main/management.py | 5 +- 5 files changed, 26 insertions(+), 128 deletions(-) diff --git a/src/gui_qt/editors/script_editor.py b/src/gui_qt/editors/script_editor.py index 4bf4dd9..57d26d7 100644 --- a/src/gui_qt/editors/script_editor.py +++ b/src/gui_qt/editors/script_editor.py @@ -2,136 +2,25 @@ from __future__ import annotations from pathlib import Path +from .usermodeleditor import QUsermodelEditor from ..Qt import QtWidgets, QtCore, QtGui -from .codeeditor import EditorWidget -class QEditor(QtWidgets.QMainWindow): +class QEditor(QUsermodelEditor): runSignal = QtCore.pyqtSignal(str) def __init__(self, path: str | Path = None, parent=None): - super().__init__(parent=parent) + super().__init__(path, parent=parent) - self._init_gui() + self.add_run_button() - self.fname = None - self._dir = None - if path is not None: - self.read_file(path) + def add_run_button(self): + self.run_button = QtWidgets.QPushButton("Run") + self.centralwidget.layout().addWidget(self.run_button) - def _init_gui(self): - self.centralwidget = QtWidgets.QWidget(self) - - layout = QtWidgets.QVBoxLayout(self.centralwidget) - layout.setContentsMargins(3, 3, 3, 3) - layout.setSpacing(3) - - self.edit_field = EditorWidget(self.centralwidget) - font = QtGui.QFont('default') - font.setStyleHint(font.Monospace) - font.setPointSize(10) - self.edit_field.setFont(font) - - layout.addWidget(self.edit_field) - self.setCentralWidget(self.centralwidget) - - self.statusbar = QtWidgets.QStatusBar(self) - self.setStatusBar(self.statusbar) - - self.menubar = self.menuBar() - - self.menuFile = QtWidgets.QMenu('File', self.menubar) - self.menubar.addMenu(self.menuFile) - - self.menuFile.addAction('Open...', self.open_file, QtGui.QKeySequence.Open) - self.menuFile.addAction('Save', self.overwrite_file, QtGui.QKeySequence.Save) - self.menuFile.addAction('Save as...', self.save_file, QtGui.QKeySequence('Ctrl+Shift+S')) - self.menuFile.addSeparator() - self.menuFile.addAction('Close', self.close, QtGui.QKeySequence.Quit) - - self.resize(800, 600) - self.setGeometry( - QtWidgets.QStyle.alignedRect( - QtCore.Qt.LayoutDirection.LeftToRight, - QtCore.Qt.AlignmentFlag.AlignCenter, - self.size(), - QtWidgets.qApp.desktop().availableGeometry() - ) - ) - - def is_modified(self): - return self.edit_field.editor.document().isModified() - - def set_modified(self, val: bool): - self.edit_field.editor.document().setModified(val) + self.run_button.clicked.connect(self.start_script) @QtCore.pyqtSlot() - def open_file(self): - overwrite = self.changes_saved - - if overwrite: - fname, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self._dir)) - if fname: - self.read_file(fname) - - def read_file(self, fname: str | Path): - self.set_fname_opts(fname) - - if self.fname is not None: - with self.fname.open('r') as f: - self.edit_field.setPlainText(f.read()) - - def set_fname_opts(self, fname: str | Path): - fname = Path(fname) - if fname.is_file(): - self.fname = Path(fname) - self._dir = self.fname.parent - elif fname.is_dir(): - self._dir = fname - self.setWindowTitle('Edit ' + str(fname)) - - def changes_saved(self) -> bool: - if not self.is_modified(): - return True - - ret = QtWidgets.QMessageBox.question(self, 'Time to think', - '

The document was modified.

\n' - '

Do you want to save changes?

', - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | - QtWidgets.QMessageBox.Cancel) - if ret == QtWidgets.QMessageBox.Yes: - self.save_file() - - if ret == QtWidgets.QMessageBox.No: - self.set_modified(False) - - return not self.is_modified() - - @QtCore.pyqtSlot() - def save_file(self): - outfile, _ = QtWidgets.QFileDialog().getSaveFileName(parent=self, caption='Save file', directory=str(self._dir)) - - if outfile: - with open(outfile, 'w') as f: - f.write(self.edit_field.toPlainText()) - - self.set_fname_opts(outfile) - - self.set_modified(False) - - return self.is_modified() - - @QtCore.pyqtSlot() - def overwrite_file(self): - if self.fname is not None: - with self.fname.open('w') as f: - f.write(self.edit_field.toPlainText()) - - self.modelsChanged.emit() - - self.set_modified(False) - - def closeEvent(self, evt: QtGui.QCloseEvent): + def start_script(self): self.runSignal.emit(self.edit_field.toPlainText()) - super().closeEvent(evt) diff --git a/src/gui_qt/editors/usermodeleditor.py b/src/gui_qt/editors/usermodeleditor.py index e912aa0..3cc1dbc 100644 --- a/src/gui_qt/editors/usermodeleditor.py +++ b/src/gui_qt/editors/usermodeleditor.py @@ -77,13 +77,19 @@ class QUsermodelEditor(QtWidgets.QMainWindow): def read_file(self, fname: str | Path): self.set_fname_opts(fname) - with self.fname.open('r') as f: - self.edit_field.setPlainText(f.read()) + if self.fname is not None: + with self.fname.open('r') as f: + self.edit_field.setPlainText(f.read()) def set_fname_opts(self, fname: str | Path): - self.fname = Path(fname) - self._dir = self.fname.parent - self.setWindowTitle('Edit ' + str(fname)) + fname = Path(fname) + if fname.is_file(): + self.fname = Path(fname) + self._dir = self.fname.parent + self.setWindowTitle('Edit ' + str(fname)) + elif fname.is_dir(): + self._dir = fname + def changes_saved(self) -> bool: if not self.is_modified(): diff --git a/src/gui_qt/io/fcbatchreader.py b/src/gui_qt/io/fcbatchreader.py index a4444e6..c62acb2 100644 --- a/src/gui_qt/io/fcbatchreader.py +++ b/src/gui_qt/io/fcbatchreader.py @@ -88,7 +88,6 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog): def accept(self): items = [self.listWidget.item(i).text() for i in range(self.listWidget.count())] - print(items) if items: with busy_cursor(): self.read(items) diff --git a/src/gui_qt/lib/namespace.py b/src/gui_qt/lib/namespace.py index ae783ba..e164cd3 100644 --- a/src/gui_qt/lib/namespace.py +++ b/src/gui_qt/lib/namespace.py @@ -4,6 +4,8 @@ from collections import namedtuple import numpy as np +import nmreval + from nmreval import models from nmreval.configs import config_paths from nmreval.lib.importer import find_models, import_ @@ -28,6 +30,7 @@ class Namespace: 'y_err': (None, 'y error values'), 'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), 'np': (np, 'numpy module'), + 'nmreval': (nmreval, 'built-in classes and stuff') }, parents=('Basic', 'General'), ) diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index ef2022d..31553e3 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -1033,7 +1033,7 @@ class UpperManagement(QtCore.QObject): else: data = self.data[sets[0]] if isinstance(data.data, new_type): - error_list.append(f'{data.name} is alreade of type {new_type.__name__}') + error_list.append(f'{data.name} is already of type {new_type.__name__}') continue new_data = new_type(data.x, np.zeros(data.x.size)) @@ -1114,7 +1114,8 @@ class UpperManagement(QtCore.QObject): ns = self.namespace.flatten() ns['return_list'] = [] - exec(text, globals(), ns) + # custom namespace must be available in global namespace of exec, otherwise imports do not work in functions + exec(text, ns, ns) new_sets = [] for new_data in ns['return_list']: From 4b75aa9267830cebe3724fab66fcaef7945a5c20 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Sun, 29 Sep 2024 15:50:50 +0200 Subject: [PATCH 6/6] add disclaimer --- src/gui_qt/editors/script_editor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui_qt/editors/script_editor.py b/src/gui_qt/editors/script_editor.py index 57d26d7..ae3d175 100644 --- a/src/gui_qt/editors/script_editor.py +++ b/src/gui_qt/editors/script_editor.py @@ -15,6 +15,10 @@ class QEditor(QUsermodelEditor): self.add_run_button() def add_run_button(self): + self.disclaimer = QtWidgets.QLabel("This is work in progress and less than perfect :(") + self.disclaimer.setStyleSheet('QLabel {color: rgb(255, 0, 0); font-weight: bold; font-size: 2.5em;};') + self.centralwidget.layout().insertWidget(0, self.disclaimer) + self.run_button = QtWidgets.QPushButton("Run") self.centralwidget.layout().addWidget(self.run_button)