Dominik Demuth
af0e0fc76f
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m3s
closes issue #177
281 lines
10 KiB
Python
281 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from nmreval.io.asciireader import AsciiReader
|
|
from nmreval.utils import NUMBER_RE
|
|
|
|
from ..Qt import QtGui, QtCore, QtWidgets
|
|
from .._py.asciidialog import Ui_ascii_reader
|
|
|
|
|
|
class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|
data_read = QtCore.pyqtSignal(list)
|
|
file_ext = ['.dat', '.txt']
|
|
|
|
def __init__(self, fname=None, parent=None):
|
|
super().__init__(parent=parent)
|
|
self.setupUi(self)
|
|
|
|
self.reader = None
|
|
self._matches = []
|
|
self.regex_input.setText(NUMBER_RE.pattern)
|
|
self.custom_input.setValidator(QtGui.QDoubleValidator())
|
|
|
|
self.comment_textfield = QtWidgets.QPlainTextEdit(self.header_widget)
|
|
self.comment_textfield.setReadOnly(True)
|
|
pal = QtWidgets.QApplication.instance().palette()
|
|
rgb = pal.color(pal.Base).getRgb()[:3]
|
|
rgb2 = pal.color(pal.Text).getRgb()[:3]
|
|
self.comment_textfield.setStyleSheet(f'background-color: rgb{rgb}; color: rgb{rgb2};')
|
|
self.header_widget.setText('Header')
|
|
self.header_widget.addWidget(self.comment_textfield)
|
|
|
|
self.ascii_table.horizontalHeader().setStretchLastSection(True)
|
|
self.buttonbox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply)
|
|
self.buttonbox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.accept)
|
|
|
|
self.changestaggeredrange(0)
|
|
|
|
self.ascii_table.contextMenuEvent = self.ctx_table
|
|
self.ascii_table.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
|
self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table)
|
|
|
|
self.skip = False
|
|
|
|
if fname is not None:
|
|
self.__call__(fname)
|
|
|
|
def __call__(self, fname, *args, **kwargs):
|
|
self.reader = AsciiReader(fname)
|
|
|
|
self.check_filename(self.regex_input.text())
|
|
|
|
if self.skip:
|
|
self.accept()
|
|
return
|
|
|
|
for i in [self.stag_lineEdit, self.start_lineedit, self.end_lineedit,
|
|
self.comment_textfield, self.ascii_table, self.delay_lineedit]:
|
|
i.clear()
|
|
self.staggered_checkBox.setChecked(False)
|
|
self.log_checkBox.setChecked(False)
|
|
|
|
self.set_gui()
|
|
self.set_column_names(1)
|
|
|
|
self.skippy_checkbox.blockSignals(True)
|
|
self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
|
self.skippy_checkbox.blockSignals(False)
|
|
|
|
return self
|
|
|
|
def set_gui(self):
|
|
for text in self.reader.header:
|
|
self.comment_textfield.appendPlainText(text)
|
|
|
|
tmp = self.line_spinBox.value()
|
|
if self.reader.header:
|
|
self.line_spinBox.setMaximum(len(self.reader.header))
|
|
else:
|
|
self.line_spinBox.setValue(0)
|
|
self.line_spinBox.setEnabled(False)
|
|
self.show_preview(10)
|
|
self.line_spinBox.setValue(tmp)
|
|
|
|
if self.reader.delays is not None:
|
|
set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
|
|
self.delay_textfield.setPlainText(set_string)
|
|
self.delay_lineedit.setText(str(len(self.reader.delays)))
|
|
|
|
def ctx_table(self, _):
|
|
menu = QtWidgets.QMenu(self)
|
|
x_action = QtWidgets.QAction('Set as x', self)
|
|
x_action.triggered.connect(lambda: self.set_columns('x'))
|
|
y_action = QtWidgets.QAction('Set as y', self)
|
|
y_action.triggered.connect(lambda: self.set_columns('y'))
|
|
menu.addActions([x_action, y_action])
|
|
if self.deltay_lineEdit.isEnabled():
|
|
yerr_action = QtWidgets.QAction('Set as \u0394y', self)
|
|
yerr_action.triggered.connect(lambda: self.set_columns('yerr'))
|
|
menu.addAction(yerr_action)
|
|
menu.popup(QtGui.QCursor.pos())
|
|
|
|
def set_columns(self, mode: str):
|
|
cols = ' '.join([str(s.column()+1) for s in self.ascii_table.selectionModel().selectedColumns()])
|
|
try:
|
|
lineedit = {'x': self.x_lineedit, 'y': self.y_lineedit, 'yerr': self.deltay_lineEdit}[mode]
|
|
lineedit.setText(cols)
|
|
except KeyError:
|
|
pass
|
|
|
|
@QtCore.pyqtSlot(int, name='on_preview_spinBox_valueChanged')
|
|
def show_preview(self, line_no: int):
|
|
preview, width, comments = self.reader.make_preview(line_no)
|
|
self.ascii_table.setRowCount(min(line_no, len(preview)))
|
|
self.ascii_table.setColumnCount(width + 1)
|
|
|
|
for i, line in enumerate(preview):
|
|
comment_line = comments[i]
|
|
for j, field in enumerate(line):
|
|
it = QtWidgets.QTableWidgetItem(field)
|
|
self.ascii_table.setItem(i, j, it)
|
|
|
|
it = QtWidgets.QTableWidgetItem(comment_line)
|
|
self.ascii_table.setItem(i, len(line), it)
|
|
|
|
self.ascii_table.resizeColumnsToContents()
|
|
|
|
@QtCore.pyqtSlot(int, name='on_column_checkBox_stateChanged')
|
|
@QtCore.pyqtSlot(int, name='on_line_spinBox_valueChanged')
|
|
def set_column_names(self, _):
|
|
self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1)))
|
|
if self.column_checkBox.isChecked() and self.line_spinBox.isEnabled():
|
|
header_line = self.reader.header[self.line_spinBox.value()-1]
|
|
self.ascii_table.setHorizontalHeaderLabels(header_line.split())
|
|
|
|
@QtCore.pyqtSlot(int, name='on_staggered_checkBox_stateChanged')
|
|
def changestaggeredrange(self, state: int):
|
|
self.stag_lineEdit.setEnabled(state)
|
|
|
|
@QtCore.pyqtSlot(name='on_pushButton_clicked')
|
|
def calc_delays(self):
|
|
try:
|
|
start = float(self.start_lineedit.text())
|
|
stop = float(self.end_lineedit.text())
|
|
num_delays = int(self.delay_lineedit.text())
|
|
is_staggered = self.staggered_checkBox.isChecked()
|
|
if is_staggered:
|
|
staggered_range = int(self.stag_lineEdit.text())
|
|
else:
|
|
staggered_range = 0
|
|
except ValueError:
|
|
_ = QtWidgets.QMessageBox.information(self, 'No delays',
|
|
'Delays cannot be calculated: Not enough or wrong arguments.')
|
|
return
|
|
|
|
self.reader.calc_delays(start, stop, num_delays, log=self.log_checkBox.isChecked(),
|
|
stagg=staggered_range, stag_size=staggered_range)
|
|
|
|
if self.reader.delays is not None:
|
|
set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
|
|
self.delay_textfield.setPlainText(set_string)
|
|
|
|
@QtCore.pyqtSlot(name='on_delay_textfield_textChanged')
|
|
def delays_changed(self):
|
|
new_delays = str(self.delay_textfield.toPlainText()).rstrip('\n').split('\n')
|
|
self.delay_lineedit.setText(str(len(new_delays)))
|
|
if new_delays[0] == '':
|
|
new_delays = None
|
|
else:
|
|
for k, v in enumerate(new_delays):
|
|
try:
|
|
new_delays[k] = float(v)
|
|
except ValueError:
|
|
new_delays[k] = -1
|
|
self.reader.delays = new_delays
|
|
|
|
@QtCore.pyqtSlot()
|
|
def accept(self):
|
|
if self.apply():
|
|
self.close()
|
|
|
|
def apply(self):
|
|
# default row for x is the first row, it will be superseded if an integer number is given.
|
|
x = self.x_lineedit.text()
|
|
if x:
|
|
try:
|
|
x = int(x)-1
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
x = None
|
|
|
|
try:
|
|
y = [int(t)-1 for t in self.y_lineedit.text().split(' ')]
|
|
except ValueError:
|
|
y = None
|
|
|
|
try:
|
|
y_err = [int(t)-1 for t in self.deltay_lineEdit.text().split(' ')]
|
|
except ValueError:
|
|
y_err = None
|
|
|
|
col_header = None
|
|
if self.column_checkBox.isChecked():
|
|
col_header = []
|
|
for i in range(self.ascii_table.columnCount()):
|
|
if self.ascii_table.horizontalHeaderItem(i) is not None:
|
|
col_header.append(self.ascii_table.horizontalHeaderItem(i).text())
|
|
else:
|
|
col_header.append(i)
|
|
|
|
if y is not None and col_header is not None:
|
|
col_header = [col_header[i] for i in range(len(col_header))]
|
|
|
|
try:
|
|
ret_dic = self.reader.export(
|
|
x=x,
|
|
y=y,
|
|
yerr=y_err,
|
|
mode=self.buttonGroup.checkedButton().text(),
|
|
col_names=col_header,
|
|
num_value=self.get_numerical_value(),
|
|
)
|
|
self.data_read.emit(ret_dic)
|
|
|
|
except ImportError as e:
|
|
_ = QtWidgets.QMessageBox.information(self, 'Reading failed',
|
|
f'Import data failed with {e.args}')
|
|
|
|
return True
|
|
|
|
@QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked')
|
|
def show_error(self, val: int):
|
|
self.deltay_lineEdit.setEnabled(val == -3)
|
|
|
|
@QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
|
|
def skip_next_dial(self, _: int):
|
|
self.skip = self.skippy_checkbox.isChecked()
|
|
|
|
@QtCore.pyqtSlot(str, name='on_regex_input_textChanged')
|
|
def check_filename(self, pattern: str = NUMBER_RE.pattern):
|
|
if self.reader is None:
|
|
return
|
|
|
|
try:
|
|
pattern = re.compile(pattern)
|
|
self.regex_input.setStyleSheet('color: rgb(0, 0, 0)')
|
|
self._matches = [m for m in pattern.finditer(str(self.reader.fname.stem))]
|
|
except re.error:
|
|
self._matches = []
|
|
|
|
if self._matches:
|
|
self.re_match_index.blockSignals(True)
|
|
self.re_match_index.setMaximum(len(self._matches))
|
|
self.re_match_index.blockSignals(False)
|
|
else:
|
|
self.regex_input.setStyleSheet('color: rgb(255, 0, 0)')
|
|
|
|
self.show_match(self.re_match_index.value())
|
|
|
|
@QtCore.pyqtSlot(int, name='on_re_match_index_valueChanged')
|
|
def show_match(self, idx: int = 0):
|
|
fname = str(self.reader.fname.stem)
|
|
|
|
if self._matches:
|
|
m = self._matches[idx-1]
|
|
self.label_8.setText(f'{fname[:m.start()]}<b>{fname[m.start():m.end()]}</b>{fname[m.end():]}')
|
|
else:
|
|
self.label_8.setText(fname)
|
|
|
|
def get_numerical_value(self):
|
|
val = 0
|
|
if self.re_button.isChecked() and self._matches:
|
|
m = self._matches[self.re_match_index.value()-1]
|
|
val = float(NUMBER_RE.search(m.group()).group().replace('p', '.'))
|
|
elif self.custom_button.isChecked():
|
|
val = float(self.custom_input.text())
|
|
|
|
return val
|