BUGFIX: VFT;
change to src layout
This commit is contained in:
0
src/gui_qt/io/__init__.py
Normal file
0
src/gui_qt/io/__init__.py
Normal file
187
src/gui_qt/io/asciireader.py
Normal file
187
src/gui_qt/io/asciireader.py
Normal file
@@ -0,0 +1,187 @@
|
||||
from nmreval.io.asciireader import AsciiReader
|
||||
|
||||
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']
|
||||
skip = False
|
||||
|
||||
def __init__(self, fname=None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.reader = AsciiReader(fname)
|
||||
|
||||
pal = QtWidgets.QApplication.instance().palette()
|
||||
rgb = pal.color(pal.Base).getRgb()[:3]
|
||||
rgb2 = pal.color(pal.Text).getRgb()[:3]
|
||||
self.plainTextEdit_2.setStyleSheet(f'QPlainTextEdit {{ background-color: rgb{rgb} ; color: rgb{rgb2}; }}')
|
||||
|
||||
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)
|
||||
|
||||
def __call__(self, fname, *args, **kwargs):
|
||||
|
||||
for i in [self.stag_lineEdit, self.start_lineedit, self.end_lineedit,
|
||||
self.plainTextEdit_2, self.ascii_table, self.delay_lineedit]:
|
||||
i.clear()
|
||||
self.checkBox_2.setChecked(False)
|
||||
self.checkBox.setChecked(False)
|
||||
self.plainTextEdit_2.show()
|
||||
|
||||
self.reader = AsciiReader(fname)
|
||||
self.set_gui()
|
||||
|
||||
if QAsciiReader.skip:
|
||||
self.accept()
|
||||
else:
|
||||
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.plainTextEdit_2.appendPlainText(text)
|
||||
|
||||
self.ascii_table.setRowCount(self.reader.shape[0])
|
||||
self.ascii_table.setColumnCount(self.reader.shape[1])
|
||||
try:
|
||||
last_header_line = self.reader.header[-1].split()
|
||||
if len(last_header_line) == self.reader.shape[1]:
|
||||
self.ascii_table.setHorizontalHeaderLabels(last_header_line)
|
||||
except IndexError:
|
||||
self.plainTextEdit_2.hide()
|
||||
|
||||
for i, line in enumerate(self.reader.data):
|
||||
for j, field in enumerate(line):
|
||||
it = QtWidgets.QTableWidgetItem(field)
|
||||
self.ascii_table.setItem(i, j, it)
|
||||
|
||||
self.ascii_table.resizeColumnsToContents()
|
||||
|
||||
if self.reader.delays is not None:
|
||||
set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
|
||||
self.plainTextEdit.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.label_5.isVisible():
|
||||
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):
|
||||
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.lineEdit}[mode]
|
||||
lineedit.setText(cols)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_checkBox_2_stateChanged')
|
||||
def changestaggeredrange(self, evt):
|
||||
if evt == 2:
|
||||
self.stag_lineEdit.show()
|
||||
self.stag_lineEdit.setEnabled(True)
|
||||
else:
|
||||
self.stag_lineEdit.hide()
|
||||
self.stag_lineEdit.setDisabled(True)
|
||||
|
||||
@QtCore.pyqtSlot(name='on_pushButton_clicked')
|
||||
def calc_delays(self):
|
||||
self.reader.calc_delays(float(self.start_lineedit.text()), float(self.end_lineedit.text()),
|
||||
int(self.delay_lineedit.text()), log=self.checkBox.isChecked(),
|
||||
stagg=self.checkBox_2.isChecked(), stag_size=int(self.stag_lineEdit.text()))
|
||||
|
||||
set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
|
||||
self.plainTextEdit.setPlainText(set_string)
|
||||
|
||||
def update_header(self, text, comment='#'):
|
||||
text = text.replace(comment, '').lstrip().rstrip()
|
||||
self.plainTextEdit_2.appendPlainText(text)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_plainTextEdit_textChanged(self):
|
||||
new_delays = str(self.plainTextEdit.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.
|
||||
|
||||
try:
|
||||
x = [int(self.x_lineedit.text())-1]
|
||||
except ValueError:
|
||||
x = None
|
||||
try:
|
||||
y = [int(t)-1 for t in str(self.y_lineedit.text()).split(' ')]
|
||||
except ValueError:
|
||||
y = None
|
||||
try:
|
||||
y_err = [int(t)-1 for t in str(self.lineEdit.text()).split(' ')]
|
||||
except ValueError:
|
||||
y_err = None
|
||||
|
||||
ret_dic = self.reader.export(xidx=x, yidx=y, yerridx=y_err,
|
||||
mode=self.buttonGroup.checkedButton().text())
|
||||
|
||||
self.data_read.emit(ret_dic)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _update_dic(key, value, old_dic):
|
||||
old_dic_keys = list(old_dic.keys())
|
||||
if key in old_dic_keys:
|
||||
counter = 0
|
||||
replace_key = key + str(counter)
|
||||
while replace_key in old_dic_keys:
|
||||
counter += 1
|
||||
replace_key = key + str(counter)
|
||||
else:
|
||||
replace_key = key
|
||||
old_dic[replace_key] = value
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked')
|
||||
def show_error(self, val):
|
||||
if val == -2:
|
||||
self.widget.show()
|
||||
else:
|
||||
self.lineEdit.setText('')
|
||||
self.widget.hide()
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
|
||||
def skip_next_dial(self, _):
|
||||
QAsciiReader.skip = self.skippy_checkbox.isChecked()
|
77
src/gui_qt/io/bdsreader.py
Normal file
77
src/gui_qt/io/bdsreader.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from nmreval.io.bds_reader import BDSReader
|
||||
from ..Qt import QtCore, QtWidgets
|
||||
from .._py.bdsdialog import Ui_Dialog
|
||||
|
||||
|
||||
class QBDSReader(QtWidgets.QDialog, Ui_Dialog):
|
||||
data_read = QtCore.pyqtSignal(list)
|
||||
file_ext = ['.eps']
|
||||
|
||||
def __init__(self, fname: str = None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.reader = None
|
||||
self.fname = fname
|
||||
|
||||
if self.fname is not None:
|
||||
self.reader = BDSReader(fname)
|
||||
self.setup_gui()
|
||||
|
||||
def __call__(self, fname: str):
|
||||
self.reader = BDSReader(fname)
|
||||
self.fname = fname
|
||||
self.setup_gui()
|
||||
|
||||
return self
|
||||
|
||||
def setup_gui(self):
|
||||
self.listWidget.clear()
|
||||
self.label.setText(f'Found entries for {self.reader.fname.name}')
|
||||
|
||||
self.make_list(True)
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
|
||||
def change_list(self, bttn: QtWidgets.QAbstractButton):
|
||||
self.make_list(bttn == self.freq_button)
|
||||
|
||||
def make_list(self, use_freq: bool) -> None:
|
||||
self.listWidget.clear()
|
||||
if use_freq:
|
||||
secondary = self.reader.set_temp
|
||||
else:
|
||||
secondary = self.reader.frequencies
|
||||
|
||||
for x2 in secondary:
|
||||
item = QtWidgets.QListWidgetItem(f'{x2:.8g}')
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable)
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
self.listWidget.addItem(item)
|
||||
|
||||
def accept(self):
|
||||
data = []
|
||||
|
||||
x2 = []
|
||||
for i in range(self.listWidget.count()):
|
||||
item = self.listWidget.item(i)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
x2.append(i)
|
||||
|
||||
xmode = 'freq' if self.freq_button.isChecked() else 'temp'
|
||||
|
||||
for (mode, cb) in [
|
||||
('epsilon', self.eps_checkBox),
|
||||
('sigma', self.cond_checkBox),
|
||||
('modulus', self.modul_checkBox),
|
||||
('capacity', self.cap_checkBox),
|
||||
('time', self.time_checkBox),
|
||||
('sample_temp', self.temp_checkBox),
|
||||
('loss_factor', self.loss_checkBox)
|
||||
]:
|
||||
if cb.checkState() == QtCore.Qt.Checked:
|
||||
values = self.reader.export(ymode=mode, xmode=xmode)
|
||||
for t in x2:
|
||||
data.append(values[t])
|
||||
|
||||
self.data_read.emit(data)
|
||||
|
||||
super().accept()
|
267
src/gui_qt/io/dscreader.py
Normal file
267
src/gui_qt/io/dscreader.py
Normal file
@@ -0,0 +1,267 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from pyqtgraph import PlotDataItem
|
||||
|
||||
from nmreval.data.points import Points
|
||||
from nmreval.io.dsc import Cyclohexane, DSCCalibrator, DSCSample
|
||||
|
||||
from ..Qt import QtWidgets, QtCore
|
||||
from .._py.dscfile_dialog import Ui_Dialog
|
||||
|
||||
|
||||
class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
|
||||
data_read = QtCore.pyqtSignal(list)
|
||||
file_ext = ['.txt', '.dsc']
|
||||
|
||||
def __init__(self, sample=None, empty=None, reference=None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.calibrator = DSCCalibrator()
|
||||
self.current_run = (-1, 'h')
|
||||
self.sample_idx = None
|
||||
self.fname = None
|
||||
|
||||
self.raw_graph.setLabel('bottom', text='T', units='K')
|
||||
self.raw_graph.setTitle('Raw data')
|
||||
self.raw_graph.addLegend()
|
||||
self.raw_sample = PlotDataItem(x=[], y=[], name='Sample')
|
||||
self.empty_sample = PlotDataItem(x=[], y=[], pen={'dash': (5, 5)}, name='Empty')
|
||||
self.raw_graph.addItem(self.raw_sample)
|
||||
self.raw_graph.addItem(self.empty_sample)
|
||||
|
||||
self.end_graph.setTitle('End result')
|
||||
self.end_graph.setLabel('bottom', text='T', units='K')
|
||||
self.baseline_sample = PlotDataItem(x=[], y=[])
|
||||
self.end_graph.addItem(self.baseline_sample)
|
||||
|
||||
self.baseline_graph.setTitle('Time dependence')
|
||||
self.baseline_graph.setLabel('bottom', text='t', units='min')
|
||||
self.drift_sample = PlotDataItem(x=[], y=[])
|
||||
self.slope_graph = PlotDataItem(x=[], y=[], pen={'color': 'r', 'dash': (5, 5)})
|
||||
self.baseline_graph.addItem(self.drift_sample)
|
||||
self.baseline_graph.addItem(self.slope_graph)
|
||||
|
||||
self.calib_graph.setTitle('Calibration')
|
||||
self.calib_graph.setLabel('bottom', text='T', units='K')
|
||||
self.ref_plotitems = []
|
||||
|
||||
self.sample = None
|
||||
if sample is not None:
|
||||
self.add_sample(sample)
|
||||
|
||||
self.empty = None
|
||||
if empty is not None:
|
||||
self.add_empty(empty=empty)
|
||||
|
||||
self.references = []
|
||||
if reference is not None:
|
||||
if isinstance(reference, (str, Path, DSCSample)):
|
||||
reference = [reference]
|
||||
for r in reference:
|
||||
self.add_reference(ref=r)
|
||||
|
||||
def __call__(self, fname: Path | str):
|
||||
self.clear_plots()
|
||||
self.add_sample(fname)
|
||||
|
||||
return self
|
||||
|
||||
def add_sample(self, fname: Path | str):
|
||||
self.sample = self.calibrator.set_measurement(fname, mode='sample')
|
||||
self.fname = self.sample.fname
|
||||
|
||||
self.step_listWidget.clear()
|
||||
|
||||
for opts in self.sample.steps:
|
||||
item = QtWidgets.QListWidgetItem()
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
if opts[0] == 'i':
|
||||
item.setFlags(QtCore.Qt.NoItemFlags)
|
||||
item.setText(f'{opts[1]:.2f} K for {opts[1] / 60:.0f} min')
|
||||
else:
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
|
||||
item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min')
|
||||
|
||||
self.step_listWidget.addItem(item)
|
||||
|
||||
@QtCore.pyqtSlot(name='on_loadempty_button_clicked')
|
||||
def add_empty(self, empty=None):
|
||||
if empty is None:
|
||||
empty, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self.fname.parent))
|
||||
|
||||
if empty:
|
||||
self.empty = self.calibrator.set_measurement(empty, mode='empty')
|
||||
self.empty_label.setText(str(self.empty.fname.name))
|
||||
|
||||
self.update_plots()
|
||||
|
||||
@QtCore.pyqtSlot(name='on_delempty_button_clicked')
|
||||
def remove_empty(self):
|
||||
self.empty_label.setText('No empty measurement')
|
||||
self.calibrator.empty = None
|
||||
|
||||
self.update_plots()
|
||||
|
||||
@QtCore.pyqtSlot(name='on_ref_add_pushButton_clicked')
|
||||
def add_reference(self, ref: str | Path = None):
|
||||
if ref is None:
|
||||
ref, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self.fname.parent))
|
||||
|
||||
if ref:
|
||||
ref = self.calibrator.set_measurement(ref, mode='reference')
|
||||
|
||||
self.references.append(ref)
|
||||
item = QtWidgets.QTableWidgetItem(str(ref.fname.name))
|
||||
item.setData(QtCore.Qt.UserRole, ref.fname)
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
rowcnt = self.reference_tableWidget.rowCount()
|
||||
self.reference_tableWidget.setRowCount(rowcnt+1)
|
||||
self.reference_tableWidget.setItem(rowcnt, 0, item)
|
||||
self.reference_tableWidget.setCellWidget(rowcnt, 1, ReferenceComboBox())
|
||||
self.reference_tableWidget.resizeColumnsToContents()
|
||||
|
||||
self.update_plots()
|
||||
|
||||
@QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked')
|
||||
def remove_reference(self):
|
||||
idx = self.reference_tableWidget.currentRow()
|
||||
self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.UserRole))
|
||||
|
||||
self.reference_tableWidget.removeRow(idx)
|
||||
self.update_plots()
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name='on_step_listWidget_itemChanged')
|
||||
def select_run(self, item: QtWidgets.QListWidgetItem):
|
||||
|
||||
idx = self.step_listWidget.indexFromItem(item).row()
|
||||
self.step_listWidget.blockSignals(True)
|
||||
for row in range(self.step_listWidget.count()):
|
||||
if idx == row:
|
||||
continue
|
||||
self.step_listWidget.item(row).setCheckState(QtCore.Qt.Unchecked)
|
||||
self.step_listWidget.blockSignals(False)
|
||||
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
mode, rate, _, _ = self.sample.steps[idx]
|
||||
self.current_run = (rate, mode)
|
||||
self.sample_idx = idx
|
||||
self.update_plots()
|
||||
else:
|
||||
self.current_run = (-1, 'h')
|
||||
self.sample_idx = None
|
||||
self.clear_plots()
|
||||
|
||||
def get_data(self, ):
|
||||
if self.sample_idx is None:
|
||||
return
|
||||
|
||||
rate = self.current_run[0]
|
||||
slope_type = {self.none_radioButton: None,
|
||||
self.isotherm_radioButton: 'iso',
|
||||
self.slope_radioButton: 'curve'}[self.buttonGroup.checkedButton()]
|
||||
|
||||
try:
|
||||
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx,
|
||||
slope=slope_type)
|
||||
except ValueError as e:
|
||||
_msg = QtWidgets.QMessageBox.warning(self, 'No rate found', e.args[0])
|
||||
return
|
||||
|
||||
self.calibrator.ref_list = []
|
||||
for row in range(self.reference_tableWidget.rowCount()):
|
||||
self.calibrator.ref_list.append(self.reference_tableWidget.cellWidget(row, 1).get_reference())
|
||||
|
||||
calib_x, calib_y, regions = self.calibrator.get_calibration(rate)
|
||||
|
||||
drift_value[0] /= 60.
|
||||
slope[0] /= 60.
|
||||
|
||||
if calib_x is not None:
|
||||
sample_data[0] *= calib_x[0]
|
||||
sample_data[0] += calib_x[1]
|
||||
|
||||
if self.cp_checkBox.isChecked():
|
||||
sample_data[1] *= calib_y
|
||||
|
||||
return sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
|
||||
@QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged')
|
||||
def update_plots(self, _=None):
|
||||
sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions = self.get_data()
|
||||
|
||||
self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1])
|
||||
self.drift_sample.setData(x=drift_value[0], y=drift_value[1])
|
||||
self.slope_graph.setData(x=slope[0], y=slope[1])
|
||||
|
||||
if empty_data is not None:
|
||||
self.empty_sample.setData(x=empty_data[0], y=empty_data[1])
|
||||
|
||||
self.calib_graph.clear()
|
||||
|
||||
if calib_x is not None:
|
||||
for ref_zoom, onset, grad_points in regions:
|
||||
ref_plot = PlotDataItem(x=ref_zoom[0], y=ref_zoom[1])
|
||||
self.calib_graph.addItem(ref_plot)
|
||||
|
||||
self.calib_graph.addItem(PlotDataItem(x=grad_points[0], y=grad_points[1],
|
||||
symbol='x'))
|
||||
|
||||
view_limits = -np.max(ref_zoom[1])/10, np.max(ref_zoom[1]) * 1.1
|
||||
cut_idx, = np.where((onset < view_limits[1]) & (onset > view_limits[0]))
|
||||
self.calib_graph.addItem(PlotDataItem(x=ref_zoom[0, cut_idx], y=onset[cut_idx],
|
||||
pen={'color': 'r', 'dash': (2, 5)}))
|
||||
self.calib_graph.addItem(PlotDataItem(x=[ref_zoom[0, 0], ref_zoom[0, -1]], y=[0, 0],
|
||||
pen={'color': 'r', 'dash': (2, 5)}))
|
||||
|
||||
self.baseline_sample.setData(x=sample_data[0], y=sample_data[1])
|
||||
|
||||
def clear_plots(self):
|
||||
for plot in [self.raw_sample, self.baseline_sample, self.empty_sample, self.drift_sample, self.slope_graph]:
|
||||
plot.setData(x=[], y=[])
|
||||
|
||||
self.calib_graph.clear()
|
||||
|
||||
def export_data(self, filesave=False, close_after=True):
|
||||
try:
|
||||
sample_data = self.get_data()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
rate, mode = self.current_run
|
||||
new_val = Points(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})')
|
||||
|
||||
if filesave:
|
||||
new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_')))
|
||||
else:
|
||||
self.data_read.emit([new_val])
|
||||
|
||||
if close_after:
|
||||
super().accept()
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked')
|
||||
def button_clicked(self, bttn: QtWidgets.QAbstractButton):
|
||||
bttn_value = self.buttonBox.standardButton(bttn)
|
||||
if bttn_value in (self.buttonBox.Ok, self.buttonBox.Apply, self.buttonBox.Save):
|
||||
self.export_data(filesave=bttn_value==self.buttonBox.Save, close_after=bttn_value==self.buttonBox.Ok)
|
||||
else:
|
||||
super().close()
|
||||
|
||||
|
||||
class ReferenceComboBox(QtWidgets.QComboBox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.references = [Cyclohexane]
|
||||
for ref in self.references:
|
||||
self.addItem(ref.name)
|
||||
|
||||
def get_reference(self):
|
||||
return self.references[self.currentIndex()]
|
114
src/gui_qt/io/exporters.py
Normal file
114
src/gui_qt/io/exporters.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from numpy import c_
|
||||
|
||||
from nmreval.io.graceeditor import GraceEditor
|
||||
from nmreval.utils.text import convert
|
||||
|
||||
from ..Qt import QtGui, QtCore, QtPrintSupport
|
||||
|
||||
|
||||
class GraceExporter:
|
||||
def __init__(self, kwargs: dict):
|
||||
self.__agr = None
|
||||
self.__opts = kwargs
|
||||
|
||||
def export(self, outfile: str, mode: int | str = 'w'):
|
||||
if mode == 'w':
|
||||
self.__agr = GraceEditor()
|
||||
else:
|
||||
self.__agr = GraceEditor(outfile)
|
||||
|
||||
if isinstance(mode, str):
|
||||
new_g = self.__agr.new_graph()
|
||||
|
||||
new_g.set_limits(x=self.__opts['limits'][0], y=self.__opts['limits'][1])
|
||||
new_g.set_log(x=self.__opts['log'][0], y=self.__opts['log'][1])
|
||||
|
||||
new_g.set_onoff('legend', self.__opts['legend'])
|
||||
new_g.set_property(**{'title': f'"{convert(self.__opts["labels"][2], old="html", new="agr")}"',
|
||||
'legend loctype': 'view',
|
||||
'legend': ', '.join(str(i) for i in new_g.world_to_view(self.__opts['legend_pos']))})
|
||||
|
||||
for i, ax in enumerate('xy'):
|
||||
new_g.set_axis_property(ax, **{'label': f'"{convert(self.__opts["labels"][i], old="html", new="agr")}"',
|
||||
'tick major': self.__opts['ticks'][i][0],
|
||||
'tick minor ticks': self.__opts['ticks'][i][1],})
|
||||
new_g.set_axis_onoff(ax, 'tick major grid', self.__opts['grid'])
|
||||
g_idx = new_g.idx
|
||||
else:
|
||||
g_idx = mode
|
||||
|
||||
colors = self.__agr.colors
|
||||
|
||||
new_colors = []
|
||||
for plot_label, item in zip(self.__opts['in_legend'], self.__opts['items']):
|
||||
new_s = self.__agr.new_set(g_idx)
|
||||
|
||||
sc = item['symbolcolor']
|
||||
c_num = -1
|
||||
for i, (_, rgb) in colors.items():
|
||||
if rgb == sc:
|
||||
c_num = i
|
||||
break
|
||||
|
||||
if c_num == -1:
|
||||
c_num = max(colors.keys())
|
||||
colors[c_num + 1] = (f'color{c_num + 1}', sc)
|
||||
new_colors.append((c_num + 1, f'color{c_num + 1}', sc))
|
||||
|
||||
new_s.set_symbol(**{'symbol': item['symbol'].value, 'size': item['symbolsize'] / 10., 'color': c_num,
|
||||
'fill color': c_num, 'fill pattern': 1})
|
||||
new_s.set_onoff('errorbar', self.__opts['plots'][2])
|
||||
|
||||
lc = item['linecolor']
|
||||
c_num = -1
|
||||
for c_num, (_, rgb) in colors.items():
|
||||
if rgb == lc:
|
||||
break
|
||||
|
||||
if c_num == -1:
|
||||
c_num = max(colors.keys())
|
||||
colors[c_num + 1] = ()
|
||||
new_colors.append((c_num, f'color{c_num + 1}', sc))
|
||||
|
||||
new_s.set_line(**{'color': c_num, 'linewidth': item['linewidth'],
|
||||
'linestyle': item['linestyle'].to_agr()})
|
||||
|
||||
if plot_label:
|
||||
new_s.set_property(comment=f'"{item["name"]}"',
|
||||
legend=f'"{convert(item["name"], old="tex", new="agr")}"')
|
||||
else:
|
||||
new_s.set_property(comment=f'"{item["name"]}"')
|
||||
|
||||
data = self.__agr.dataset(g_idx, new_s.idx)
|
||||
if 'yerr' in item:
|
||||
data.type = 'xydy'
|
||||
data.data = c_[item['x'], item['y'], item['yerr']]
|
||||
new_s.set_property(**{'errorbar color': c_num})
|
||||
else:
|
||||
data.data = c_[item['x'], item['y']]
|
||||
|
||||
for c in new_colors:
|
||||
self.__agr.set_color(c[1], c[2], idx=c[0])
|
||||
|
||||
self.__agr.write(outfile)
|
||||
|
||||
|
||||
class PDFPrintExporter:
|
||||
def __init__(self, graphview):
|
||||
self.graphic = graphview
|
||||
|
||||
def export(self, outfile):
|
||||
printer = QtPrintSupport.QPrinter()
|
||||
|
||||
printer.setOutputFormat(printer.PdfFormat)
|
||||
printer.setOutputFileName(outfile)
|
||||
|
||||
printer.setPaperSize(QtCore.QSizeF(self.graphic.width(), self.graphic.height()),
|
||||
printer.DevicePixel)
|
||||
printer.setPageMargins(0, 0, 0, 0, printer.DevicePixel)
|
||||
|
||||
painter = QtGui.QPainter(printer)
|
||||
self.graphic.render(painter)
|
||||
painter.end()
|
107
src/gui_qt/io/fcbatchreader.py
Normal file
107
src/gui_qt/io/fcbatchreader.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import pathlib
|
||||
|
||||
from nmreval.io.fcbatchreader import FCReader
|
||||
|
||||
from ..lib.utils import busy_cursor
|
||||
from ..Qt import QtCore, QtWidgets, QtGui
|
||||
from .._py.fcreader import Ui_FCEval_dialog
|
||||
|
||||
|
||||
class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
|
||||
data_read = QtCore.pyqtSignal(list, str)
|
||||
|
||||
def __init__(self, path=None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
if path is None:
|
||||
path = pathlib.Path().home()
|
||||
self.path = path
|
||||
|
||||
self.start_lineedit.setValidator(QtGui.QDoubleValidator())
|
||||
self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
|
||||
|
||||
self.listWidget.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
|
||||
# intercept key press in listwidget to allow deletion with Del
|
||||
if evt.type() == QtCore.QEvent.KeyPress:
|
||||
if evt.key() == QtCore.Qt.Key_Delete:
|
||||
self.listWidget.takeItem(self.listWidget.currentRow())
|
||||
return True
|
||||
|
||||
return super().eventFilter(src, evt)
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
|
||||
def use_region(self, state: int):
|
||||
self.start_lineedit.setEnabled(state == QtCore.Qt.Checked)
|
||||
self.stop_lineedit.setEnabled(state == QtCore.Qt.Checked)
|
||||
|
||||
@QtCore.pyqtSlot(name='on_file_pushbutton_clicked')
|
||||
@QtCore.pyqtSlot(name='on_dir_pushbutton_clicked')
|
||||
def get_input(self):
|
||||
if self.sender() == self.file_pushbutton:
|
||||
infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(caption='Select HDF files',
|
||||
directory=str(self.path),
|
||||
filter='HDF files (*.h5)')
|
||||
if infiles:
|
||||
self.listWidget.addItems(infiles)
|
||||
self.label.setText(str(pathlib.Path(infiles[-1]).parent))
|
||||
else:
|
||||
infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory',
|
||||
directory=str(self.path),
|
||||
options=QtWidgets.QFileDialog.ShowDirsOnly)
|
||||
|
||||
if infiles:
|
||||
self.listWidget.addItem(infiles)
|
||||
self.label.setText(str(pathlib.Path(infiles).parent))
|
||||
|
||||
@QtCore.pyqtSlot(name='on_savebutton_clicked')
|
||||
def save_path(self):
|
||||
outfile = QtWidgets.QFileDialog.getExistingDirectory(self, caption='Select directory',
|
||||
directory=self.label.text(),
|
||||
options=QtWidgets.QFileDialog.ShowDirsOnly)
|
||||
if outfile:
|
||||
self.label.setText(outfile)
|
||||
|
||||
def accept(self):
|
||||
items = [self.listWidget.item(i).text() for i in range(self.listWidget.count())]
|
||||
if items:
|
||||
with busy_cursor():
|
||||
self.read(items)
|
||||
|
||||
self.close()
|
||||
|
||||
def read(self, items):
|
||||
region = (None, None)
|
||||
if self.region_box.isChecked():
|
||||
start = None
|
||||
if self.start_lineedit.text():
|
||||
start = float(self.start_lineedit.text())
|
||||
|
||||
stop = None
|
||||
if self.stop_lineedit.text():
|
||||
stop = float(self.stop_lineedit.text())
|
||||
region = (start, stop)
|
||||
|
||||
fc_eval = FCReader(items)
|
||||
try:
|
||||
fc_eval.load_magnetization(region=region, overwrite=self.overwrite_cb.isChecked())
|
||||
except OSError as e:
|
||||
QtWidgets.QMessageBox.warning(self, 'Missing data', e.strerror)
|
||||
return
|
||||
|
||||
fc_eval.fit(kww=self.kww_checkbox.isChecked(), save_fits=True, save_fig=True)
|
||||
|
||||
save_variables = []
|
||||
for name, cb in [('t1', self.t1_cb), ('beta', self.beta_cb), ('m0', self.m0_cb), ('off', self.off_cb)]:
|
||||
if cb.isChecked():
|
||||
save_variables.append(name)
|
||||
|
||||
ret_vals = []
|
||||
ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables))
|
||||
|
||||
grp = ''
|
||||
if self.graph_checkbox.isChecked():
|
||||
grp = self.graph_comboBox.currentData(QtCore.Qt.UserRole)
|
||||
|
||||
self.data_read.emit(ret_vals, grp)
|
122
src/gui_qt/io/filedialog.py
Normal file
122
src/gui_qt/io/filedialog.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
|
||||
from ..Qt import QtWidgets, QtCore
|
||||
|
||||
|
||||
class _FileDialog(QtWidgets.QFileDialog):
|
||||
def __init__(self, directory=None, caption=None, filters='', parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True)
|
||||
|
||||
self.setWindowTitle(caption)
|
||||
self.setDirectory(str(directory))
|
||||
self.setNameFilters(filters.split(';;'))
|
||||
|
||||
file_tree = self.findChild(QtWidgets.QTreeView, 'treeView')
|
||||
file_tree.setSortingEnabled(True)
|
||||
for i in range(file_tree.header().count()):
|
||||
file_tree.header().setSectionResizeMode(i, 0)
|
||||
file_tree.model().sort(0)
|
||||
|
||||
file_list = self.findChild(QtWidgets.QListView, 'listView')
|
||||
file_list.model().sort(0)
|
||||
|
||||
line = QtWidgets.QFrame(self)
|
||||
line.setFrameShape(line.HLine)
|
||||
line.setFrameShadow(line.Sunken)
|
||||
self.layout().addWidget(line, self.layout().rowCount(), 0, 1, self.layout().columnCount())
|
||||
|
||||
|
||||
class OpenFileDialog(_FileDialog):
|
||||
def __init__(self, directory=None, caption=None, filters='', parent=None):
|
||||
super().__init__(directory=directory, caption=caption, filters=filters, parent=parent)
|
||||
|
||||
self.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
|
||||
|
||||
self.checkBox = QtWidgets.QCheckBox(self)
|
||||
self.checkBox.setChecked(False)
|
||||
self.checkBox.setText('Use existing graph')
|
||||
self.checkBox.stateChanged.connect(self.checkbox_state)
|
||||
self.layout().addWidget(self.checkBox)
|
||||
|
||||
self.comboBox = QtWidgets.QComboBox(self)
|
||||
self.comboBox.setEnabled(False)
|
||||
self.layout().addWidget(self.comboBox)
|
||||
|
||||
self.add_to_graph = None
|
||||
|
||||
def set_graphs(self, graphs):
|
||||
self.comboBox.blockSignals(True)
|
||||
for gid, name in graphs:
|
||||
self.comboBox.addItem(name, userData=gid)
|
||||
self.comboBox.blockSignals(False)
|
||||
|
||||
if not self.comboBox.count():
|
||||
self.comboBox.hide()
|
||||
self.checkBox.hide()
|
||||
|
||||
def checkbox_state(self, checked: QtCore.Qt.CheckState):
|
||||
if checked == QtCore.Qt.Checked:
|
||||
self.comboBox.setEnabled(True)
|
||||
self.add_to_graph = self.comboBox.currentData()
|
||||
else:
|
||||
self.comboBox.setEnabled(False)
|
||||
self.add_to_graph = None
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
|
||||
def graph_changed(self, idx: int):
|
||||
self.add_to_graph = self.comboBox.itemData(idx)
|
||||
|
||||
|
||||
class SaveDirectoryDialog(_FileDialog):
|
||||
def __init__(self, directory=None, filters='', parent=None):
|
||||
super().__init__(directory=directory, filters=filters, parent=parent)
|
||||
|
||||
self.setOption(QtWidgets.QFileDialog.DontConfirmOverwrite, False)
|
||||
self.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
|
||||
|
||||
lay = self.layout()
|
||||
|
||||
self.label = QtWidgets.QLabel(self)
|
||||
self.label.setTextFormat(QtCore.Qt.RichText)
|
||||
self.label.setText('Use <b><label></b> as placeholder in filename. (e.g. <i>t1_<label>.dat</i>)')
|
||||
lay.addWidget(self.label, lay.rowCount(), 0, 1, lay.columnCount())
|
||||
|
||||
line = QtWidgets.QFrame(self)
|
||||
line.setFrameShape(line.HLine)
|
||||
line.setFrameShadow(line.Sunken)
|
||||
lay.addWidget(line, lay.rowCount(), 0, 1, lay.columnCount())
|
||||
|
||||
h_layout = QtWidgets.QHBoxLayout()
|
||||
h_layout.setContentsMargins(0, 0, 0, 0)
|
||||
h_layout.setSpacing(3)
|
||||
|
||||
self.checkBox = QtWidgets.QCheckBox(self)
|
||||
self.checkBox.setChecked(True)
|
||||
self.checkBox.setText('Replace spaces with _')
|
||||
h_layout.addWidget(self.checkBox)
|
||||
|
||||
self.agr_cb = QtWidgets.QCheckBox(self)
|
||||
self.agr_cb.setChecked(True)
|
||||
self.agr_cb.setText('Save graph as Grace file')
|
||||
h_layout.addWidget(self.agr_cb)
|
||||
|
||||
self.fit_cb = QtWidgets.QCheckBox(self)
|
||||
self.fit_cb.setChecked(True)
|
||||
self.fit_cb.setText('Save fit parameter')
|
||||
h_layout.addWidget(self.fit_cb)
|
||||
|
||||
lay.addLayout(h_layout, lay.rowCount(), 0, 1, lay.columnCount())
|
||||
|
||||
self.setWindowTitle('Save')
|
||||
self.setNameFilters(['All files (*.*)', 'Session file (*.nmr)', 'Text file (*.dat)',
|
||||
'HDF file (*.h5)', 'Grace files (*.agr)'])
|
||||
|
||||
def save_file(self) -> pathlib.Path | None:
|
||||
outfile = self.selectedFiles()
|
||||
if outfile:
|
||||
return pathlib.Path(outfile[0])
|
||||
return
|
129
src/gui_qt/io/filereaders.py
Executable file
129
src/gui_qt/io/filereaders.py
Executable file
@@ -0,0 +1,129 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import struct
|
||||
|
||||
from ..Qt import QtCore
|
||||
from .asciireader import QAsciiReader
|
||||
from .hdfreader import QHdfViewer
|
||||
from .bdsreader import QBDSReader
|
||||
from .gracereader import QGraceReader
|
||||
from .dscreader import QDSCReader
|
||||
from .nmrreader import QNMRReader
|
||||
|
||||
|
||||
class QFileReader(QtCore.QObject):
|
||||
data_read = QtCore.pyqtSignal([list], [dict])
|
||||
|
||||
def __init__(self, manager=None):
|
||||
QtCore.QObject.__init__(self)
|
||||
|
||||
self.select = 'all'
|
||||
self.data = []
|
||||
self.filenames = None
|
||||
self.extensions = set()
|
||||
|
||||
self.reader = {}
|
||||
|
||||
for ext, reader in [
|
||||
('txt', QAsciiReader), ('dsc', QDSCReader), ('agr', QGraceReader),
|
||||
('bds', QBDSReader), ('hdf', QHdfViewer), ('nmr', QNMRReader)
|
||||
]:
|
||||
self.register(ext, reader)
|
||||
|
||||
def __call__(self, files: list[str] | str) -> list:
|
||||
self.data = []
|
||||
if isinstance(files, str):
|
||||
self.filenames = [files]
|
||||
else:
|
||||
self.filenames = files
|
||||
|
||||
return self.readfiles(files)
|
||||
|
||||
def readfiles(self, fname: list[str] | str) -> list:
|
||||
if not isinstance(fname, list):
|
||||
fname = [fname]
|
||||
|
||||
for f in fname:
|
||||
f = Path(f)
|
||||
dtype = self.guess_type(f)
|
||||
if dtype in self.reader:
|
||||
r = self.reader[dtype]
|
||||
else:
|
||||
raise ValueError(f'Unknown type for file {f}')
|
||||
|
||||
if r(f) is not None:
|
||||
# If QAsciiReader.skip = True it accepts automatically and returns None
|
||||
r(f).exec()
|
||||
|
||||
self.data_read.emit(self.data)
|
||||
|
||||
try:
|
||||
self.reader['txt'].skip = False
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return self.data
|
||||
|
||||
def readtnt(self, fname):
|
||||
""" Special treatment for tnt. If data is a single measurement, skip dialog """
|
||||
raise NotImplementedError
|
||||
# tntreader = self.reader[2]
|
||||
# tntreader(fname)
|
||||
# if not tntreader.reader.onedimensional:
|
||||
# tntreader.exec()
|
||||
|
||||
@QtCore.pyqtSlot(list)
|
||||
def _update_dic(self, other: list):
|
||||
self.data.extend(other)
|
||||
|
||||
def register(self, key, new_reader):
|
||||
if key in self.reader:
|
||||
return self.reader[key]
|
||||
r = new_reader()
|
||||
self.reader[key] = r
|
||||
r.data_read.connect(self._update_dic)
|
||||
self.extensions.update(r.file_ext)
|
||||
|
||||
return r
|
||||
|
||||
def guess_type(self, fname: Path) -> str:
|
||||
ext = fname.suffix.lower()
|
||||
|
||||
if ext in self.extensions:
|
||||
if ext in ('.dat', '.txt'):
|
||||
with fname.open('r', encoding='iso-8859-15') as fp:
|
||||
line = fp.readline()
|
||||
if line.strip().startswith('Filename: C:\\'):
|
||||
return 'dsc'
|
||||
|
||||
return 'txt'
|
||||
|
||||
if ext in ('.h5', '.hdf', '.hdf5'):
|
||||
return 'hdf'
|
||||
|
||||
if ext == '.eps':
|
||||
return 'bds'
|
||||
|
||||
return ext[1:] # remove dot
|
||||
|
||||
else:
|
||||
with fname.open('rb') as fp:
|
||||
s16 = fp.read(16)
|
||||
|
||||
# copied from whichdb (Python 2.7) to look for magic value of dbhash
|
||||
(magic,) = struct.unpack("=l", s16[-4:])
|
||||
if magic in (0x00061561, 0x61150600):
|
||||
return 'nmr'
|
||||
|
||||
else:
|
||||
ret_types = ('nmr', 'bds', 'tnt', 'agr', 'dsc')
|
||||
strings = (b'NMREVAL', b'NOVOCONTROL', b'TNT1.005', b'# Grace project', b'Filename:\tC:')
|
||||
|
||||
(magic,) = struct.unpack('<16s', s16)
|
||||
for dtype, startstring in zip(ret_types, strings):
|
||||
if magic.startswith(startstring):
|
||||
return dtype
|
||||
|
||||
# nothing matched, text file is best guess
|
||||
return 'txt'
|
100
src/gui_qt/io/gracereader.py
Normal file
100
src/gui_qt/io/gracereader.py
Normal file
@@ -0,0 +1,100 @@
|
||||
from nmreval.lib.lines import LineStyle
|
||||
from nmreval.lib.symbols import SymbolStyle
|
||||
from nmreval.data.points import Points
|
||||
from nmreval.io.graceeditor import GraceEditor
|
||||
|
||||
from ..Qt import QtCore, QtWidgets, QtGui
|
||||
from .._py.gracereader import Ui_Dialog
|
||||
|
||||
|
||||
class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
|
||||
data_read = QtCore.pyqtSignal(list)
|
||||
file_ext = ['.agr']
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self._reader = GraceEditor()
|
||||
self.treeWidget.itemClicked.connect(self.show_property)
|
||||
|
||||
self.treeWidget.installEventFilter(self)
|
||||
|
||||
def __call__(self, fname, *args, **kwargs):
|
||||
self.read(fname)
|
||||
|
||||
return self
|
||||
|
||||
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent):
|
||||
if evt.type() == QtCore.QEvent.KeyPress:
|
||||
if evt.key() == QtCore.Qt.Key_Space:
|
||||
iterator = QtWidgets.QTreeWidgetItemIterator(self.treeWidget)
|
||||
while iterator.value():
|
||||
item = iterator.value()
|
||||
if item.parent() is not None and item.isSelected():
|
||||
item.setCheckState(0,
|
||||
QtCore.Qt.Checked if item.checkState(0) == QtCore.Qt.Unchecked else
|
||||
QtCore.Qt.Unchecked)
|
||||
|
||||
iterator += 1
|
||||
|
||||
return True
|
||||
|
||||
return super().eventFilter(src, evt)
|
||||
|
||||
def read(self, fname):
|
||||
self._reader.parse(fname)
|
||||
|
||||
for graphs in self._reader.graphs:
|
||||
item = QtWidgets.QTreeWidgetItem([f'Graph {graphs.idx} (Title "{graphs.get_property("title")}")'])
|
||||
for gset in graphs.set:
|
||||
item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {gset.get_property("legend")}, '
|
||||
f'shape: {self._reader.dataset(graphs.idx, gset.idx).shape})'])
|
||||
item_2.setCheckState(0, QtCore.Qt.Checked)
|
||||
item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx))
|
||||
item.addChild(item_2)
|
||||
|
||||
self.treeWidget.addTopLevelItem(item)
|
||||
self.treeWidget.expandAll()
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int)
|
||||
def show_property(self, item: QtWidgets.QTreeWidgetItem, col: int):
|
||||
keys = item.data(col, QtCore.Qt.UserRole)
|
||||
if keys is not None:
|
||||
cols = self._reader.colors
|
||||
|
||||
self.tableWidget.item(0, 1).setText(SymbolStyle(self._reader.get_property(*keys, 'symbol')).name)
|
||||
sym_col = cols[self._reader.get_property(*keys, 'symbol fill color')][1]
|
||||
self.tableWidget.item(1, 1).setBackground(QtGui.QBrush(QtGui.QColor(*sym_col)))
|
||||
|
||||
self.tableWidget.item(2, 1).setText(LineStyle(self._reader.get_property(*keys, 'line linestyle')).name)
|
||||
|
||||
line_col = cols[self._reader.get_property(*keys, 'line color')][1]
|
||||
self.tableWidget.item(3, 1).setBackground(QtGui.QBrush(QtGui.QColor(*line_col)))
|
||||
|
||||
def accept(self):
|
||||
data = []
|
||||
iterator = QtWidgets.QTreeWidgetItemIterator(self.treeWidget,
|
||||
QtWidgets.QTreeWidgetItemIterator.NoChildren |
|
||||
QtWidgets.QTreeWidgetItemIterator.Checked)
|
||||
|
||||
while iterator.value():
|
||||
item = iterator.value()
|
||||
key = (item.data(0, QtCore.Qt.UserRole))
|
||||
s = self._reader.dataset(*key)
|
||||
label = self._reader.get_property(*key, 'legend').replace('"', '')
|
||||
# label = self._reader.graphs[key[0]].sets[key[1]]['legend'].replace('"', '')
|
||||
sd = s.data
|
||||
if s.type == 'xydy':
|
||||
data.append(Points(x=sd[:, 0], y=sd[:, 1], y_err=sd[:, 2], name=label))
|
||||
else:
|
||||
data.append(Points(x=sd[:, 0], y=sd[:, 1], name=label))
|
||||
|
||||
iterator += 1
|
||||
|
||||
self.data_read.emit(data)
|
||||
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
self._reader.clear()
|
||||
super().close()
|
184
src/gui_qt/io/hdfreader.py
Normal file
184
src/gui_qt/io/hdfreader.py
Normal file
@@ -0,0 +1,184 @@
|
||||
from nmreval.io.hdfreader import HdfReader
|
||||
|
||||
from ..Qt import QtGui, QtCore, QtWidgets
|
||||
from .._py.hdftree import Ui_Hdf_Dialog
|
||||
|
||||
|
||||
class QHdfViewer(QtWidgets.QDialog, Ui_Hdf_Dialog):
|
||||
data_read = QtCore.pyqtSignal(list)
|
||||
file_ext = ['.h5', '.hdf', '.hdf5']
|
||||
|
||||
def __init__(self, fname: str = None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.treewidget = HDFTreeWidget(self)
|
||||
self.verticalLayout_3.addWidget(self.treewidget)
|
||||
|
||||
self.variables = VarTable(self)
|
||||
self.widget_2.addWidget(self.variables)
|
||||
self.widget_2.setText('Variables')
|
||||
|
||||
self._reader = HdfReader()
|
||||
|
||||
if fname is not None:
|
||||
self.__call__(fname)
|
||||
|
||||
def __call__(self, fname, *args, **kwargs):
|
||||
self.treewidget.clear()
|
||||
for cb in [self.comboBox, self.comboBox_2]:
|
||||
cb.clear()
|
||||
cb.addItem('Automatic for the people')
|
||||
cb.show()
|
||||
|
||||
self._reader(filename=fname)
|
||||
self._fill_boxes()
|
||||
self._populate_tree(self._reader, self.treewidget.invisibleRootItem())
|
||||
|
||||
if self.comboBox.count() == 1:
|
||||
self.comboBox.hide()
|
||||
self.comboBox_2.hide()
|
||||
|
||||
self.treewidget.expandToDepth(0)
|
||||
self.treewidget.resizeColumnToContents(1)
|
||||
|
||||
return self
|
||||
|
||||
def _populate_tree(self, node, item):
|
||||
self.treewidget.blockSignals(True)
|
||||
|
||||
# add varied parameter to comboboxes
|
||||
for key, value in node.title_parameter[1].items():
|
||||
if isinstance(value, list):
|
||||
idx = self.comboBox.findText(key)
|
||||
if idx == -1:
|
||||
self.comboBox.addItem(key)
|
||||
self.comboBox_2.addItem(key)
|
||||
|
||||
if node.children is not None:
|
||||
for child in node.children.values():
|
||||
label_item = QtWidgets.QTreeWidgetItem([child.name])
|
||||
label_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable)
|
||||
label_item.setCheckState(0, QtCore.Qt.Unchecked)
|
||||
if child.type == 'signal':
|
||||
label_item.setBackground(0, QtGui.QBrush(QtGui.QColor(31, 119, 180)))
|
||||
label_item.setCheckState(1, QtCore.Qt.Unchecked)
|
||||
elif child.type == 'points':
|
||||
label_item.setBackground(0, QtGui.QBrush(QtGui.QColor(255, 127, 14)))
|
||||
item.addChild(label_item)
|
||||
self._populate_tree(child, label_item)
|
||||
|
||||
self.treewidget.blockSignals(False)
|
||||
|
||||
def _fill_boxes(self):
|
||||
for k, v in self._reader.parameter.items():
|
||||
if isinstance(v, list):
|
||||
idx = self.comboBox.findText(k)
|
||||
if idx == -1:
|
||||
self.comboBox.addItem(k)
|
||||
self.comboBox_2.addItem(k)
|
||||
|
||||
self.variables.populate(self._reader.parameter)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def get_selected(self):
|
||||
|
||||
if self.comboBox.currentIndex() == 0:
|
||||
value = None
|
||||
else:
|
||||
value = self.comboBox.currentText()
|
||||
|
||||
if self.comboBox_2.currentIndex() == 0:
|
||||
group = None
|
||||
else:
|
||||
group = self.comboBox_2.currentText()
|
||||
|
||||
iterator = QtWidgets.QTreeWidgetItemIterator(self.treewidget, QtWidgets.QTreeWidgetItemIterator.NoChildren)
|
||||
selected = []
|
||||
while iterator.value():
|
||||
item = iterator.value()
|
||||
iterator += 1
|
||||
if item.checkState(0) == QtCore.Qt.Checked:
|
||||
path = item.text(0)
|
||||
parent = item.parent()
|
||||
while parent is not None:
|
||||
path = parent.text(0) + '/' + path
|
||||
parent = parent.parent()
|
||||
|
||||
if item.checkState(1) == QtCore.Qt.Checked:
|
||||
selected.extend(self._reader.get_selected(path, flag='spectrum', value=value, group=group))
|
||||
else:
|
||||
selected.extend(self._reader.get_selected(path, value=value, group=group))
|
||||
|
||||
self.data_read.emit(selected)
|
||||
|
||||
def accept(self):
|
||||
self.get_selected()
|
||||
self.close()
|
||||
|
||||
|
||||
class VarTable(QtWidgets.QTableWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.setColumnCount(2)
|
||||
self.setHorizontalHeaderLabels(['Key', 'Value'])
|
||||
self.horizontalHeader().setStretchLastSection(True)
|
||||
self.verticalHeader().setVisible(False)
|
||||
self.horizontalHeader().setVisible(True)
|
||||
|
||||
def populate(self, vars: dict):
|
||||
self.setHorizontalHeaderLabels(['Key', 'Value'])
|
||||
self.setRowCount(len(vars))
|
||||
for i, (k, v) in enumerate(vars.items()):
|
||||
key_item = QtWidgets.QTableWidgetItem(k)
|
||||
key_item.setFlags(QtCore.Qt.NoItemFlags)
|
||||
key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
|
||||
if isinstance(v, list):
|
||||
val = f'<{len(v)} values>'
|
||||
if len(v) < 40:
|
||||
tip = '\n'.join(str(p) for p in v)
|
||||
else:
|
||||
tip = '\n'.join(str(p) for p in v[:40])
|
||||
tip += '\n...'
|
||||
else:
|
||||
val = str(v)
|
||||
tip = val
|
||||
value_item = QtWidgets.QTableWidgetItem(val)
|
||||
value_item.setFlags(QtCore.Qt.NoItemFlags)
|
||||
value_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
|
||||
value_item.setToolTip(tip)
|
||||
self.setItem(i, 0, key_item)
|
||||
self.setItem(i, 1, value_item)
|
||||
|
||||
|
||||
class HDFTreeWidget(QtWidgets.QTreeWidget):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.setHeaderLabels(['Data', 'Spectrum?'])
|
||||
self.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
||||
self.header().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)
|
||||
self.header().setStretchLastSection(False)
|
||||
self.header().setCascadingSectionResizes(True)
|
||||
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems)
|
||||
|
||||
self.itemChanged.connect(self.change_item)
|
||||
|
||||
def keyPressEvent(self, evt: QtGui.QKeyEvent):
|
||||
if evt.key() == QtCore.Qt.Key_Space:
|
||||
for idx in self.selectedIndexes():
|
||||
item = self.itemFromIndex(idx)
|
||||
col = idx.column()
|
||||
cs = item.checkState(col)
|
||||
item.setCheckState(col, QtCore.Qt.Unchecked if cs == QtCore.Qt.Checked else QtCore.Qt.Checked)
|
||||
else:
|
||||
super().keyPressEvent(evt)
|
||||
|
||||
@staticmethod
|
||||
def change_item(item: QtWidgets.QTreeWidgetItem, column: int):
|
||||
state = item.checkState(column)
|
||||
for i in range(item.childCount()):
|
||||
child = item.child(i)
|
||||
child.setCheckState(column, state)
|
34
src/gui_qt/io/nmrreader.py
Normal file
34
src/gui_qt/io/nmrreader.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from struct import unpack
|
||||
|
||||
from nmreval.io.nmrreader import NMRReader
|
||||
|
||||
from ..Qt import QtCore
|
||||
|
||||
|
||||
class QNMRReader(QtCore.QObject):
|
||||
data_read = QtCore.pyqtSignal(list)
|
||||
file_ext = ['.nmr']
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._reader = NMRReader()
|
||||
|
||||
def __call__(self, fname):
|
||||
with fname.open('rb') as fp:
|
||||
s16 = fp.read(16)
|
||||
|
||||
# copied from whichdb (Python 2.7) to look for magic value of dbhash
|
||||
(magic,) = unpack("=l", s16[-4:])
|
||||
if magic in (0x00061561, 0x61150600):
|
||||
self._reader(fname, '-1')
|
||||
|
||||
else:
|
||||
(magic,) = unpack('<16s', s16)
|
||||
if magic.startswith(b'NMREVAL'):
|
||||
self._reader(fname, str(magic[7:].rstrip(b'\x00'), 'utf8'))
|
||||
|
||||
return self
|
||||
|
||||
def exec(self):
|
||||
data = self._reader.make_data()
|
||||
self.data_read.emit([data])
|
65
src/gui_qt/io/save_fitparameter.py
Normal file
65
src/gui_qt/io/save_fitparameter.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from ..Qt import QtWidgets, QtCore, QtGui
|
||||
from .._py.save_fit_parameter import Ui_fitparameter_save_dialog
|
||||
|
||||
|
||||
class QSaveFit(QtWidgets.QDialog, Ui_fitparameter_save_dialog):
|
||||
def __init__(self, path: str = None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.pnames = None
|
||||
self.outpath = path
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
self.tableWidget.cellDoubleClicked.connect(self.clicketyclick)
|
||||
self.tableWidget.setColumnCount(2)
|
||||
|
||||
self.save_path_line.textEdited.connect(lambda: self.change_path(self.save_path_line.text()))
|
||||
self.save_path_button.clicked.connect(self.set_save_path)
|
||||
|
||||
self.header_edit.hide()
|
||||
self.header_checkBox.stateChanged.connect(lambda state: self.header_edit.setVisible(state))
|
||||
|
||||
self.missing_value_line.setValidator(QtGui.QDoubleValidator())
|
||||
|
||||
def set_parameter(self, fits: dict):
|
||||
for model_name, parameter in fits.items():
|
||||
for p in parameter:
|
||||
item = QtWidgets.QTableWidgetItem(p)
|
||||
item2 = QtWidgets.QTableWidgetItem(model_name)
|
||||
item2.setToolTip(model_name)
|
||||
row = self.tableWidget.rowCount()
|
||||
self.tableWidget.setRowCount(row+1)
|
||||
self.tableWidget.setItem(row, 0, item)
|
||||
self.tableWidget.setItem(row, 1, item2)
|
||||
|
||||
self.tableWidget.resizeColumnsToContents()
|
||||
|
||||
@QtCore.pyqtSlot(int, int)
|
||||
def clicketyclick(self, row: int, _):
|
||||
if self.col_line.text():
|
||||
self.col_line.setText(self.col_line.text() + '; ' + self.tableWidget.item(row, 0).text())
|
||||
else:
|
||||
self.col_line.setText(self.tableWidget.item(row, 0).text())
|
||||
|
||||
def set_save_path(self):
|
||||
fname, _ = QtWidgets.QFileDialog.getSaveFileName(options=QtWidgets.QFileDialog.DontConfirmOverwrite,
|
||||
directory=self.outpath)
|
||||
|
||||
if fname:
|
||||
self.outpath = fname
|
||||
self.save_path_line.setText(fname)
|
||||
|
||||
def add_header(self, idx: int):
|
||||
self.textEdit.setVisible(idx != 0)
|
||||
|
||||
@QtCore.pyqtSlot(name='on_usage_button_clicked')
|
||||
def show_usage(self):
|
||||
_ = QtWidgets.QMessageBox.information(self, 'Usage',
|
||||
'Separate each column by semicolon.\n'
|
||||
'Use p_err to save error of parameter p.\n'
|
||||
'If a column shall contain different parameters use {p1/p2}.\n'
|
||||
'KWW mean is calculated by mean(τ,β), KWW peak by peak(τ,β).')
|
||||
|
||||
def accept(self):
|
||||
super().accept()
|
109
src/gui_qt/io/tntreader.py
Normal file
109
src/gui_qt/io/tntreader.py
Normal file
@@ -0,0 +1,109 @@
|
||||
from nmreval.io.tntreader import TNTReader
|
||||
|
||||
from ..Qt import QtCore, QtWidgets
|
||||
from .._py.tntdialog import Ui_tntdialog
|
||||
|
||||
|
||||
class QTNTReader(QtWidgets.QDialog, Ui_tntdialog):
|
||||
data_read = QtCore.pyqtSignal(dict)
|
||||
file_ext = ['.tnt']
|
||||
|
||||
def __init__(self, fname=None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.reader = TNTReader(fname)
|
||||
|
||||
self.frame.hide()
|
||||
|
||||
if fname is not None:
|
||||
if self.reader.onedimensional:
|
||||
self.export()
|
||||
else:
|
||||
self.set_gui()
|
||||
|
||||
def __call__(self, fname, *args, **kwargs):
|
||||
self.reader(fname)
|
||||
for s in [self.widget, self.widget_2, self.widget_3]:
|
||||
s.__call__()
|
||||
|
||||
self.frame.hide()
|
||||
self.frame_2.show()
|
||||
self.unknown_delay_combobox.clear()
|
||||
|
||||
if self.reader.onedimensional:
|
||||
self.export()
|
||||
else:
|
||||
self.set_gui()
|
||||
|
||||
return self
|
||||
|
||||
def set_gui(self):
|
||||
self.label_3.setText(' x '.join(map(str, self.reader.shape)))
|
||||
for i, w in enumerate([self.widget, self.widget_2, self.widget_3], start=1):
|
||||
if self.reader.shape[i] == 1:
|
||||
w.hide()
|
||||
else:
|
||||
w.label.setText('Dimension {}'.format(i+1))
|
||||
w.dim = i
|
||||
w.add_items([x[0] for x in self.reader.delays.items() if len(x[1]) == self.reader.shape[i]])
|
||||
w.get_data.connect(self.show_delays)
|
||||
w.newDelay.connect(self.calc_delays)
|
||||
for x in self.reader.delays.items():
|
||||
if len(x[1]) in self.reader.shape[1:]:
|
||||
continue
|
||||
else:
|
||||
self.unknown_delay_combobox.addItem(x[0])
|
||||
if self.unknown_delay_combobox.count() == 0:
|
||||
self.frame_2.hide()
|
||||
|
||||
@QtCore.pyqtSlot(str)
|
||||
def show_delays(self, label):
|
||||
vals = self.reader.delays[str(label)]
|
||||
self.sender().vals = '\n'.join(map(str, vals))
|
||||
|
||||
def calc_delays(self):
|
||||
self.frame.show()
|
||||
self._caller = self.sender()
|
||||
|
||||
@QtCore.pyqtSlot(name='on_pushButton_2_clicked')
|
||||
def cancel_delay(self):
|
||||
self.frame.hide()
|
||||
|
||||
@QtCore.pyqtSlot(name='on_pushButton_clicked')
|
||||
def add_delay(self):
|
||||
try:
|
||||
s = float(self.start_lineedit.text())
|
||||
except ValueError:
|
||||
return
|
||||
try:
|
||||
e = float(self.end_lineedit.text())
|
||||
except ValueError:
|
||||
return
|
||||
d = []
|
||||
for w in [self.widget, self.widget_2, self.widget_3]:
|
||||
if w.isVisible():
|
||||
d.append(w.comboBox.currentText())
|
||||
self.reader.add_delay(s, e, self._caller.dim, self.lineEdit.text(),
|
||||
staggered=self.checkBox_2.isChecked(), stag_steps=int(self.spinBox.value()),
|
||||
log=self.checkBox.isChecked())
|
||||
self._caller.add_items(self.lineEdit.text())
|
||||
self.frame.hide()
|
||||
|
||||
def export(self):
|
||||
d = []
|
||||
for w in [self.widget, self.widget_2, self.widget_3]:
|
||||
if w.isVisible():
|
||||
d.append(w.comboBox.currentText())
|
||||
else:
|
||||
d.append(None)
|
||||
|
||||
ret_dic = self.reader.export(d)
|
||||
|
||||
self.data_read.emit(ret_dic)
|
||||
self.close()
|
||||
|
||||
return ret_dic
|
||||
|
||||
def accept(self):
|
||||
self.export()
|
Reference in New Issue
Block a user