From 89a3313e74025fbec0dc2fd282d7447e7bb00a2e Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Tue, 10 Jan 2023 19:46:19 +0100 Subject: [PATCH] display logged error messages within program --- src/gui_qt/_py/basewindow.py | 10 ++- src/gui_qt/lib/logger.py | 114 ++++++++++++++++++++++++++++++++ src/gui_qt/main/mainwindow.py | 7 ++ src/resources/_ui/basewindow.ui | 8 ++- 4 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/gui_qt/lib/logger.py diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index a5e7ab9..f680529 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.2 +# Created by: PyQt5 UI code generator 5.15.7 # # 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, 24)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1386, 20)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") @@ -356,6 +356,8 @@ class Ui_BaseWindow(object): self.action_draw_object.setObjectName("action_draw_object") self.actionBugs = QtWidgets.QAction(BaseWindow) self.actionBugs.setObjectName("actionBugs") + self.actionShow_error_log = QtWidgets.QAction(BaseWindow) + self.actionShow_error_log.setObjectName("actionShow_error_log") self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.action_save_fit_parameter) @@ -380,6 +382,7 @@ class Ui_BaseWindow(object): self.menuData.addAction(self.action_cut) self.menuData.addSeparator() self.menuData.addAction(self.actionChange_datatypes) + self.menuHelp.addAction(self.actionShow_error_log) self.menuHelp.addAction(self.actionDocumentation) self.menuHelp.addAction(self.actionUpdate) self.menuHelp.addAction(self.actionBugs) @@ -479,7 +482,7 @@ class Ui_BaseWindow(object): self.retranslateUi(BaseWindow) self.tabWidget.setCurrentIndex(0) - self.action_close.triggered.connect(BaseWindow.close) + self.action_close.triggered.connect(BaseWindow.close) # type: ignore QtCore.QMetaObject.connectSlotsByName(BaseWindow) def retranslateUi(self, BaseWindow): @@ -608,6 +611,7 @@ class Ui_BaseWindow(object): self.actionMine.setText(_translate("BaseWindow", "Mine")) self.action_draw_object.setText(_translate("BaseWindow", "Draw objects...")) self.actionBugs.setText(_translate("BaseWindow", "Bugs! Problems! Wishes!")) + self.actionShow_error_log.setText(_translate("BaseWindow", "Show error log")) from ..data.datawidget.datawidget import DataWidget from ..data.integral_widget import IntegralWidget from ..data.point_select import PointSelectWidget diff --git a/src/gui_qt/lib/logger.py b/src/gui_qt/lib/logger.py new file mode 100644 index 0000000..43b6bb1 --- /dev/null +++ b/src/gui_qt/lib/logger.py @@ -0,0 +1,114 @@ +import sys +from pathlib import Path + +from .codeeditor import _make_textformats +from ..Qt import QtWidgets, QtCore, QtGui +from nmreval.configs import config_paths + + +STYLES = {'INFO': _make_textformats('black'), + 'WARNING': _make_textformats('blue'), + 'ERROR': _make_textformats('red', 'bold'), + 'DEBUG': _make_textformats('black', 'italic'), + 'file': _make_textformats('red', 'italic'), + 'PyError': _make_textformats('red', 'bold-italic')} + + +class LogHighlighter(QtGui.QSyntaxHighlighter): + msg = ['INFO', 'DEBUG', 'ERROR', 'WARNING'] + + def __init__(self, parent): + super(LogHighlighter, self).__init__(parent) + + rules = list() + rules += [(r'\b\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2} - %s - .*' % m, 0, STYLES[m]) for m in LogHighlighter.msg] + rules += [(r'\b[A-Z]\w*Error: .*\b', 0, STYLES['PyError'])] + rules += [(r'\bFile .*', 0, STYLES['file'])] + + self.rules = [(QtCore.QRegExp(pat), index, fmt) for (pat, index, fmt) in rules] + + def highlightBlock(self, text): + """Apply syntax highlighting to the given block of text. + """ + # Do other syntax formatting + for expression, nth, format in self.rules: + index = expression.indexIn(text, 0) + + while index >= 0: + # We actually want the index of the nth match + index = expression.pos(nth) + try: + length = expression.cap(nth).length() + except AttributeError: + length = len(expression.cap(nth)) + self.setFormat(index, length, format) + index = expression.indexIn(text, index + length) + + self.setCurrentBlockState(0) + + def match_multiline(self, text, delimiter, in_state, style): + if self.previousBlockState() == in_state: + start = 0 + add = 0 + else: + start = delimiter.indexIn(text) + add = delimiter.matchedLength() + + while start >= 0: + end = delimiter.indexIn(text, start + add) + if end >= add: + length = end - start + add + delimiter.matchedLength() + self.setCurrentBlockState(0) + else: + self.setCurrentBlockState(in_state) + try: + length = text.length() - start + add + except AttributeError: + length = len(text) - start + add + # Apply formatting + self.setFormat(start, length, style) + # Look for the next match + start = delimiter.indexIn(text, start + length) + + # Return True if still inside a multi-line string, False otherwise + if self.currentBlockState() == in_state: + return True + else: + return False + + +class QLog(QtWidgets.QDialog): + def __init__(self, parent=None): + super(QLog, self).__init__(parent=parent) + + self._initUi() + + self.logfile = config_paths() / 'errors.log' + + self.read_log() + + def _initUi(self): + + self.resize(960, 640) + layout = QtWidgets.QVBoxLayout() + self.plainTextEdit = QtWidgets.QPlainTextEdit() + self.plainTextEdit.highlight = LogHighlighter(self.plainTextEdit.document()) + self.plainTextEdit.setReadOnly(True) + self.plainTextEdit.setMaximumBlockCount(50) + layout.addWidget(self.plainTextEdit) + + self.buttonBox = QtWidgets.QDialogButtonBox() + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + layout.addWidget(self.buttonBox) + + self.setLayout(layout) + + def read_log(self): + with Path(self.logfile).open('r') as f: + text = f.readlines() + + for lines in text[-100:]: + self.plainTextEdit.appendPlainText(lines[:-1]) diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index c9fc62b..19877d5 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -1019,3 +1019,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): def look_for_update(self): w = UpdateDialog(parent=self) w.show() + + @QtCore.pyqtSlot(name='on_actionShow_error_log_triggered') + def open_log(self): + from ..lib.logger import QLog + + QLog(parent=self).show() + diff --git a/src/resources/_ui/basewindow.ui b/src/resources/_ui/basewindow.ui index 0cb9372..5edcf51 100644 --- a/src/resources/_ui/basewindow.ui +++ b/src/resources/_ui/basewindow.ui @@ -136,7 +136,7 @@ 0 0 1386 - 24 + 20 @@ -189,6 +189,7 @@ &Help + @@ -993,6 +994,11 @@ Bugs! Problems! Wishes! + + + Show error log + +