qcodespp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qcodespp/__init__.py +77 -0
- qcodespp/actions.py +237 -0
- qcodespp/data/__init__.py +0 -0
- qcodespp/data/data_array.py +555 -0
- qcodespp/data/data_set.py +926 -0
- qcodespp/data/format.py +338 -0
- qcodespp/data/gnuplot_format.py +425 -0
- qcodespp/data/hdf5_format.py +537 -0
- qcodespp/data/hdf5_format_hickle.py +63 -0
- qcodespp/data/io.py +241 -0
- qcodespp/data/location.py +171 -0
- qcodespp/instrument_drivers/Keysight/Keysight_34450A.py +9 -0
- qcodespp/instrument_drivers/Keysight/__init__.py +0 -0
- qcodespp/instrument_drivers/Keysight/private/Keysight_344xxA.py +456 -0
- qcodespp/instrument_drivers/Keysight/private/__init__.py +0 -0
- qcodespp/instrument_drivers/Lakeshore/Model_340.py +483 -0
- qcodespp/instrument_drivers/Lakeshore/__init__.py +0 -0
- qcodespp/instrument_drivers/QDevil/159fit_params_high_latest.json +386 -0
- qcodespp/instrument_drivers/QDevil/159fit_params_low_latest.json +386 -0
- qcodespp/instrument_drivers/QDevil/QDAC2.py +3229 -0
- qcodespp/instrument_drivers/QDevil/QDAC2_Array.py +300 -0
- qcodespp/instrument_drivers/QDevil/QSwitch_elab.py +652 -0
- qcodespp/instrument_drivers/QDevil/__init__.py +0 -0
- qcodespp/instrument_drivers/ZI/ZIHF2LI.py +415 -0
- qcodespp/instrument_drivers/ZI/ZIMFLI.py +764 -0
- qcodespp/instrument_drivers/ZI/ZIMFLI_sync.py +108 -0
- qcodespp/instrument_drivers/ZI/ZIUHFLI.py +2179 -0
- qcodespp/instrument_drivers/ZI/__init__.py +1 -0
- qcodespp/instrument_drivers/__init__.py +0 -0
- qcodespp/instrument_drivers/cryogenic/CryogenicSMS120C.py +599 -0
- qcodespp/instrument_drivers/cryogenic/__init__.py +0 -0
- qcodespp/instrument_drivers/instrument_mocks.py +379 -0
- qcodespp/instrument_drivers/multiplexer_instrument.py +287 -0
- qcodespp/instrument_drivers/national_instruments/PXI_4461.py +451 -0
- qcodespp/instrument_drivers/national_instruments/PXI_4462.py +290 -0
- qcodespp/instrument_drivers/national_instruments/PXI_6259.py +436 -0
- qcodespp/instrument_drivers/national_instruments/PXI_6733.py +230 -0
- qcodespp/instrument_drivers/national_instruments/PXI_6733_e.py +243 -0
- qcodespp/instrument_drivers/national_instruments/__init__.py +0 -0
- qcodespp/instrument_drivers/oxford/__init__.py +0 -0
- qcodespp/instrument_drivers/oxford/mercuryiPS_serial.py +658 -0
- qcodespp/instrument_drivers/oxford/triton.py +434 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2000_Scan.py +65 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2182a.py +190 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2400.py +158 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2450.py +242 -0
- qcodespp/instrument_drivers/tektronix/Keithley_2700.py +397 -0
- qcodespp/instrument_drivers/tektronix/Keithley_6500.py +161 -0
- qcodespp/instrument_drivers/tektronix/__init__.py +0 -0
- qcodespp/instrument_drivers/yokogawa/GS200.py +513 -0
- qcodespp/instrument_drivers/yokogawa/__init__.py +0 -0
- qcodespp/loops.py +1342 -0
- qcodespp/measure.py +310 -0
- qcodespp/parameters.py +311 -0
- qcodespp/plotting/RemotePlot.py +469 -0
- qcodespp/plotting/RemotePlotClient.py +367 -0
- qcodespp/plotting/RemoteQtPlotWidgets.py +871 -0
- qcodespp/plotting/__init__.py +0 -0
- qcodespp/plotting/analysis_tools.py +195 -0
- qcodespp/plotting/colors.py +135 -0
- qcodespp/plotting/offline/__init__.py +0 -0
- qcodespp/plotting/offline/datatypes.py +999 -0
- qcodespp/plotting/offline/design.py +906 -0
- qcodespp/plotting/offline/design.ui +1578 -0
- qcodespp/plotting/offline/editor.py +2966 -0
- qcodespp/plotting/offline/filters.py +561 -0
- qcodespp/plotting/offline/fits.py +1171 -0
- qcodespp/plotting/offline/helpers.py +288 -0
- qcodespp/plotting/offline/iconGadget.png +0 -0
- qcodespp/plotting/offline/main.py +55 -0
- qcodespp/plotting/offline/popupwindows.py +1848 -0
- qcodespp/plotting/offline/qcodes_pp_extension.py +328 -0
- qcodespp/plotting/offline/sidebars.py +1108 -0
- qcodespp/station.py +290 -0
- qcodespp/utils/__init__.py +0 -0
- qcodespp/utils/helpers.py +618 -0
- qcodespp/utils/visa_helpers.py +61 -0
- qcodespp/version.py +12 -0
- qcodespp-0.1.0.dist-info/METADATA +91 -0
- qcodespp-0.1.0.dist-info/RECORD +82 -0
- qcodespp-0.1.0.dist-info/WHEEL +4 -0
- qcodespp-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1108 @@
|
|
|
1
|
+
from PyQt5 import QtWidgets, QtCore, QtGui
|
|
2
|
+
from json import load as jsonload
|
|
3
|
+
from json import dump as jsondump
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
|
|
7
|
+
import qcodespp.plotting.offline.fits as fits
|
|
8
|
+
|
|
9
|
+
from matplotlib import colormaps as cm
|
|
10
|
+
import numpy as np
|
|
11
|
+
from lmfit.model import save_modelresult
|
|
12
|
+
|
|
13
|
+
import matplotlib.style as mplstyle
|
|
14
|
+
mplstyle.use('fast')
|
|
15
|
+
|
|
16
|
+
from qcodespp.plotting.offline.helpers import cmaps
|
|
17
|
+
|
|
18
|
+
class Sidebar1D(QtWidgets.QWidget):
|
|
19
|
+
def __init__(self, parent, editor_window=None):
|
|
20
|
+
super().__init__()
|
|
21
|
+
|
|
22
|
+
self.init_cmap='viridis'
|
|
23
|
+
self.setMaximumWidth(500)
|
|
24
|
+
self.parent = parent
|
|
25
|
+
self.running = True
|
|
26
|
+
self.init_widgets()
|
|
27
|
+
self.init_connections()
|
|
28
|
+
self.init_layouts()
|
|
29
|
+
self.set_main_layout()
|
|
30
|
+
self.init_trace_table()
|
|
31
|
+
self.fit_type_changed()
|
|
32
|
+
if editor_window:
|
|
33
|
+
self.editor_window = editor_window
|
|
34
|
+
|
|
35
|
+
def init_widgets(self):
|
|
36
|
+
# Widgets in trace list box
|
|
37
|
+
self.add_trace_button = QtWidgets.QPushButton('Add')
|
|
38
|
+
self.remove_trace_button = QtWidgets.QPushButton('Remove')
|
|
39
|
+
self.clear_traces_button = QtWidgets.QPushButton('Clear list')
|
|
40
|
+
|
|
41
|
+
self.trace_table = QtWidgets.QTableWidget()
|
|
42
|
+
|
|
43
|
+
self.duplicate_trace_button = QtWidgets.QPushButton('Duplicate')
|
|
44
|
+
self.move_up_button = QtWidgets.QPushButton('Move Up')
|
|
45
|
+
self.move_down_button = QtWidgets.QPushButton('Move Down')
|
|
46
|
+
|
|
47
|
+
self.colormap_type_box = QtWidgets.QComboBox()
|
|
48
|
+
self.colormap_box = QtWidgets.QComboBox()
|
|
49
|
+
self.apply_colormap_to_box = QtWidgets.QComboBox() # All by index, all by num, selected by ind, selected by num, checked by ind, checked by num
|
|
50
|
+
self.apply_button = QtWidgets.QPushButton('Apply')
|
|
51
|
+
|
|
52
|
+
applymethods=['All', 'Checked']
|
|
53
|
+
self.apply_colormap_to_box.addItems(applymethods)
|
|
54
|
+
for cmap_type in cmaps:
|
|
55
|
+
self.colormap_type_box.addItem(cmap_type)
|
|
56
|
+
self.colormap_box.addItems(list(cmaps.values())[0])
|
|
57
|
+
self.colormap_box.setCurrentText(self.init_cmap)
|
|
58
|
+
|
|
59
|
+
# Fitting widgets
|
|
60
|
+
self.lims_label = QtWidgets.QLabel('Fit limits (left-click plot):')
|
|
61
|
+
self.xmin_label = QtWidgets.QLabel('Xmin:')
|
|
62
|
+
self.xmin_label.setStyleSheet("QLabel { color : blue; }")
|
|
63
|
+
self.xmax_label = QtWidgets.QLabel('Xmax:')
|
|
64
|
+
self.xmax_label.setStyleSheet("QLabel { color : red; }")
|
|
65
|
+
self.xmin_box = QtWidgets.QLineEdit()
|
|
66
|
+
self.xmax_box = QtWidgets.QLineEdit()
|
|
67
|
+
self.reset_axes_button = QtWidgets.QPushButton('Reset')
|
|
68
|
+
|
|
69
|
+
self.fit_functions_label = QtWidgets.QLabel('Select fit function:')
|
|
70
|
+
self.fit_class_box = QtWidgets.QComboBox()
|
|
71
|
+
self.fit_class_box.addItems(fits.get_class_names())
|
|
72
|
+
self.fit_class_box.setCurrentIndex(0)
|
|
73
|
+
self.fit_class_box.SizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
|
74
|
+
self.fit_box = QtWidgets.QComboBox()
|
|
75
|
+
self.fit_box.addItems(fits.get_names(fitclass=self.fit_class_box.currentText()))
|
|
76
|
+
self.fit_box.setCurrentIndex(0)
|
|
77
|
+
self.fit_box.SizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
|
78
|
+
|
|
79
|
+
self.input_label = QtWidgets.QLabel('Input info:')
|
|
80
|
+
self.input_edit = QtWidgets.QLineEdit()
|
|
81
|
+
|
|
82
|
+
self.guess_checkbox = QtWidgets.QCheckBox('Initial guess:')
|
|
83
|
+
self.guess_edit = QtWidgets.QLineEdit()
|
|
84
|
+
|
|
85
|
+
self.output_window = QtWidgets.QTextEdit()
|
|
86
|
+
self.output_window.setReadOnly(True)
|
|
87
|
+
|
|
88
|
+
self.fit_button = QtWidgets.QPushButton('Fit')
|
|
89
|
+
self.save_result_button = QtWidgets.QPushButton('Save fit result')
|
|
90
|
+
self.clear_fit_button = QtWidgets.QPushButton('Clear fit')
|
|
91
|
+
self.fit_checked_button = QtWidgets.QPushButton('Fit checked')
|
|
92
|
+
self.save_all_fits_button = QtWidgets.QPushButton('Save all fits')
|
|
93
|
+
self.clear_all_fits_button = QtWidgets.QPushButton('Clear all fits')
|
|
94
|
+
self.save_preset_button = QtWidgets.QPushButton('Save fit preset')
|
|
95
|
+
self.load_preset_button = QtWidgets.QPushButton('Load fit preset')
|
|
96
|
+
|
|
97
|
+
def init_connections(self):
|
|
98
|
+
self.add_trace_button.clicked.connect(self.add_trace_manually)
|
|
99
|
+
self.remove_trace_button.clicked.connect(lambda: self.remove_trace('selected'))
|
|
100
|
+
self.clear_traces_button.clicked.connect(lambda: self.remove_trace('all'))
|
|
101
|
+
self.duplicate_trace_button.clicked.connect(self.duplicate_trace)
|
|
102
|
+
self.move_up_button.clicked.connect(lambda: self.move_trace('up'))
|
|
103
|
+
self.move_down_button.clicked.connect(lambda: self.move_trace('down'))
|
|
104
|
+
self.apply_button.clicked.connect(self.apply_colormap)
|
|
105
|
+
|
|
106
|
+
self.trace_table.itemClicked.connect(self.item_clicked)
|
|
107
|
+
|
|
108
|
+
self.clear_fit_button.clicked.connect(lambda: self.clear_fit(line='manual'))
|
|
109
|
+
self.fit_class_box.currentIndexChanged.connect(self.fit_class_changed)
|
|
110
|
+
self.fit_box.currentIndexChanged.connect(self.fit_type_changed)
|
|
111
|
+
self.fit_button.clicked.connect(lambda: self.start_fitting(line='manual'))
|
|
112
|
+
self.save_result_button.clicked.connect(self.save_fit_result)
|
|
113
|
+
self.fit_checked_button.clicked.connect(self.fit_checked)
|
|
114
|
+
self.save_all_fits_button.clicked.connect(self.save_all_fits)
|
|
115
|
+
self.clear_all_fits_button.clicked.connect(self.clear_all_fits)
|
|
116
|
+
self.save_preset_button.clicked.connect(self.save_fit_preset)
|
|
117
|
+
self.load_preset_button.clicked.connect(self.load_fit_preset)
|
|
118
|
+
self.xmin_box.editingFinished.connect(self.limits_edited)
|
|
119
|
+
self.xmax_box.editingFinished.connect(self.limits_edited)
|
|
120
|
+
self.reset_axes_button.clicked.connect(self.reset_limits)
|
|
121
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
122
|
+
self.colormap_type_box.currentIndexChanged.connect(self.colormap_type_edited)
|
|
123
|
+
|
|
124
|
+
def init_layouts(self):
|
|
125
|
+
# Sub-layouts in Linetrace list box:
|
|
126
|
+
self.table_buttons_layout_top = QtWidgets.QHBoxLayout()
|
|
127
|
+
self.table_buttons_layout_bot = QtWidgets.QHBoxLayout()
|
|
128
|
+
self.colormap_layout = QtWidgets.QHBoxLayout()
|
|
129
|
+
|
|
130
|
+
# Populating
|
|
131
|
+
self.table_buttons_layout_top.addWidget(self.add_trace_button)
|
|
132
|
+
self.table_buttons_layout_top.addWidget(self.remove_trace_button)
|
|
133
|
+
self.table_buttons_layout_top.addWidget(self.clear_traces_button)
|
|
134
|
+
self.table_buttons_layout_top.addStretch()
|
|
135
|
+
|
|
136
|
+
self.table_buttons_layout_bot.addWidget(self.duplicate_trace_button)
|
|
137
|
+
self.table_buttons_layout_bot.addWidget(self.move_up_button)
|
|
138
|
+
self.table_buttons_layout_bot.addWidget(self.move_down_button)
|
|
139
|
+
self.table_buttons_layout_bot.addStretch()
|
|
140
|
+
|
|
141
|
+
self.colormap_layout.addWidget(self.colormap_type_box)
|
|
142
|
+
self.colormap_layout.addWidget(self.colormap_box)
|
|
143
|
+
self.colormap_layout.addWidget(self.apply_colormap_to_box)
|
|
144
|
+
self.colormap_layout.addWidget(self.apply_button)
|
|
145
|
+
|
|
146
|
+
# Sub-layouts(s) in fitting box
|
|
147
|
+
self.lims_layout_top = QtWidgets.QHBoxLayout()
|
|
148
|
+
self.lims_layout_bot = QtWidgets.QHBoxLayout()
|
|
149
|
+
self.fit_layout = QtWidgets.QHBoxLayout()
|
|
150
|
+
self.inputs_layout = QtWidgets.QHBoxLayout()
|
|
151
|
+
self.guess_layout = QtWidgets.QHBoxLayout()
|
|
152
|
+
self.guess_input_layout = QtWidgets.QHBoxLayout()
|
|
153
|
+
self.output_layout = QtWidgets.QVBoxLayout()
|
|
154
|
+
self.fit_buttons_layout = QtWidgets.QHBoxLayout()
|
|
155
|
+
self.fit_all_buttons_layout = QtWidgets.QHBoxLayout()
|
|
156
|
+
self.fit_presets_layout = QtWidgets.QHBoxLayout()
|
|
157
|
+
|
|
158
|
+
# Populating
|
|
159
|
+
self.lims_layout_top.addWidget(self.lims_label)
|
|
160
|
+
self.lims_layout_top.addStretch()
|
|
161
|
+
self.lims_layout_bot.addWidget(self.xmin_label)
|
|
162
|
+
self.lims_layout_bot.addWidget(self.xmin_box)
|
|
163
|
+
self.lims_layout_bot.addWidget(self.xmax_label)
|
|
164
|
+
self.lims_layout_bot.addWidget(self.xmax_box)
|
|
165
|
+
self.lims_layout_bot.addWidget(self.reset_axes_button)
|
|
166
|
+
self.lims_layout_bot.addStretch()
|
|
167
|
+
|
|
168
|
+
self.fit_layout.addWidget(self.fit_functions_label)
|
|
169
|
+
self.fit_layout.addWidget(self.fit_class_box)
|
|
170
|
+
self.fit_layout.addWidget(self.fit_box)
|
|
171
|
+
self.fit_layout.addStretch()
|
|
172
|
+
|
|
173
|
+
self.inputs_layout.addWidget(self.input_label)
|
|
174
|
+
self.inputs_layout.addWidget(self.input_edit)
|
|
175
|
+
#self.inputs_layout.addStretch()
|
|
176
|
+
|
|
177
|
+
self.guess_layout.addWidget(self.guess_checkbox)
|
|
178
|
+
self.guess_input_layout.addWidget(self.guess_edit)
|
|
179
|
+
#self.guess_layout.addStretch()
|
|
180
|
+
|
|
181
|
+
self.output_layout.addWidget(self.output_window)
|
|
182
|
+
|
|
183
|
+
self.fit_buttons_layout.addWidget(self.fit_button)
|
|
184
|
+
self.fit_buttons_layout.addWidget(self.save_result_button)
|
|
185
|
+
self.fit_buttons_layout.addWidget(self.clear_fit_button)
|
|
186
|
+
self.fit_buttons_layout.addStretch()
|
|
187
|
+
self.fit_buttons_layout.addWidget(self.save_preset_button)
|
|
188
|
+
self.fit_all_buttons_layout.addWidget(self.fit_checked_button)
|
|
189
|
+
self.fit_all_buttons_layout.addWidget(self.save_all_fits_button)
|
|
190
|
+
self.fit_all_buttons_layout.addWidget(self.clear_all_fits_button)
|
|
191
|
+
self.fit_all_buttons_layout.addStretch()
|
|
192
|
+
self.fit_all_buttons_layout.addWidget(self.load_preset_button)
|
|
193
|
+
|
|
194
|
+
def set_main_layout(self):
|
|
195
|
+
self.main_layout = QtWidgets.QVBoxLayout()
|
|
196
|
+
|
|
197
|
+
self.tablebox=QtWidgets.QGroupBox(f'1D traces: {self.parent.label}')
|
|
198
|
+
self.table_layout = QtWidgets.QVBoxLayout()
|
|
199
|
+
self.table_layout.addLayout(self.table_buttons_layout_top)
|
|
200
|
+
self.table_layout.addLayout(self.table_buttons_layout_bot)
|
|
201
|
+
self.table_layout.addWidget(self.trace_table)
|
|
202
|
+
self.table_layout.addLayout(self.colormap_layout)
|
|
203
|
+
self.tablebox.setLayout(self.table_layout)
|
|
204
|
+
|
|
205
|
+
self.fittingbox=QtWidgets.QGroupBox('Curve Fitting')
|
|
206
|
+
self.fittinglayout = QtWidgets.QVBoxLayout()
|
|
207
|
+
self.fittinglayout.addLayout(self.lims_layout_top)
|
|
208
|
+
self.fittinglayout.addLayout(self.lims_layout_bot)
|
|
209
|
+
self.fittinglayout.addLayout(self.fit_layout)
|
|
210
|
+
self.fittinglayout.addLayout(self.inputs_layout)
|
|
211
|
+
self.fittinglayout.addLayout(self.guess_layout)
|
|
212
|
+
self.fittinglayout.addLayout(self.guess_input_layout)
|
|
213
|
+
self.fittinglayout.addLayout(self.output_layout)
|
|
214
|
+
self.fittinglayout.addLayout(self.fit_buttons_layout)
|
|
215
|
+
self.fittinglayout.addLayout(self.fit_all_buttons_layout)
|
|
216
|
+
#self.fittinglayout.addLayout(self.fit_presets_layout)
|
|
217
|
+
self.fittingbox.setLayout(self.fittinglayout)
|
|
218
|
+
|
|
219
|
+
self.main_layout.addWidget(self.tablebox)
|
|
220
|
+
self.main_layout.addWidget(self.fittingbox)
|
|
221
|
+
self.setLayout(self.main_layout)
|
|
222
|
+
|
|
223
|
+
def init_trace_table(self):
|
|
224
|
+
headerlabels=['#','X data','Y data','style','color', 'width','Xerr','Yerr','show fit','show fit cmpts','show fit err']
|
|
225
|
+
self.trace_table.setColumnCount(len(headerlabels))
|
|
226
|
+
self.trace_table.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked)
|
|
227
|
+
h = self.trace_table.horizontalHeader()
|
|
228
|
+
h.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
|
229
|
+
for col in range(len(headerlabels)):
|
|
230
|
+
h.setSectionResizeMode(col, QtWidgets.QHeaderView.ResizeToContents)
|
|
231
|
+
self.trace_table.setHorizontalHeaderLabels(headerlabels)
|
|
232
|
+
v=self.trace_table.verticalHeader()
|
|
233
|
+
v.setVisible(False)
|
|
234
|
+
|
|
235
|
+
self.trace_table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
|
236
|
+
self.trace_table.customContextMenuRequested.connect(self.open_trace_table_menu)
|
|
237
|
+
|
|
238
|
+
def plot_type_changed(self):
|
|
239
|
+
# This is called when the data type is changed in the main window.
|
|
240
|
+
# It updates the trace table with the new data.
|
|
241
|
+
self.trace_table.setRowCount(0)
|
|
242
|
+
self.trace_table.clear()
|
|
243
|
+
if self.parent.plot_type == 'Histogram':
|
|
244
|
+
self.trace_table.setHorizontalHeaderLabels(['#','Bins','Data','style','color', 'width','Xerr','Yerr','show fit','show fit cmpts','show fit err'])
|
|
245
|
+
else:
|
|
246
|
+
self.trace_table.setHorizontalHeaderLabels(['#','X data','Y data','style','color', 'width','Xerr','Yerr','show fit','show fit cmpts','show fit err'])
|
|
247
|
+
for line in self.parent.plotted_lines.keys():
|
|
248
|
+
self.append_trace_to_table(line)
|
|
249
|
+
|
|
250
|
+
def item_clicked(self, item):
|
|
251
|
+
# displays the fit result and/or information.
|
|
252
|
+
row = self.trace_table.currentRow()
|
|
253
|
+
line = int(self.trace_table.item(row,0).text())
|
|
254
|
+
if 'fit' in self.parent.plotted_lines[line].keys():
|
|
255
|
+
fit_result = self.parent.plotted_lines[line]['fit']['fit_result']
|
|
256
|
+
self.output_window.setText(fit_result.fit_report())
|
|
257
|
+
elif 'stats' in self.parent.plotted_lines[line].keys():
|
|
258
|
+
text='Statistics:\n'
|
|
259
|
+
for key in self.parent.plotted_lines[line]['stats'].keys():
|
|
260
|
+
text+=f'{key}: {self.parent.plotted_lines[line]['stats'][key]}\n'
|
|
261
|
+
self.output_window.setText(text)
|
|
262
|
+
else:
|
|
263
|
+
fit_function=fits.functions[self.fit_class_box.currentText()][self.fit_box.currentText()]
|
|
264
|
+
self.output_window.setText('Information about selected fit type:\n'+
|
|
265
|
+
fit_function['description'])
|
|
266
|
+
|
|
267
|
+
# ... and the filters.
|
|
268
|
+
self.editor_window.show_current_filters()
|
|
269
|
+
|
|
270
|
+
def get_checked_items(self, return_indices = False, traces_or_fits='traces'):
|
|
271
|
+
# Note this is a bit different to the main window, where the entire item is returned.
|
|
272
|
+
# Here we just return the identifier for the linetrace
|
|
273
|
+
if traces_or_fits == 'traces':
|
|
274
|
+
column = 0
|
|
275
|
+
elif traces_or_fits == 'fits':
|
|
276
|
+
column = 8
|
|
277
|
+
indices = [index for index in range(self.trace_table.rowCount())
|
|
278
|
+
if self.trace_table.item(index,column).checkState() == 2 or
|
|
279
|
+
self.trace_table.item(index,column).checkState()== QtCore.Qt.Checked]
|
|
280
|
+
checked_items = [int(self.trace_table.item(index,0).text()) for index in indices]
|
|
281
|
+
if return_indices:
|
|
282
|
+
return checked_items, indices
|
|
283
|
+
else:
|
|
284
|
+
return checked_items
|
|
285
|
+
|
|
286
|
+
def duplicate_trace(self):
|
|
287
|
+
try:
|
|
288
|
+
max_index=np.max(list(self.parent.plotted_lines.keys()))
|
|
289
|
+
except ValueError:
|
|
290
|
+
max_index=-1
|
|
291
|
+
current_row = self.trace_table.currentRow()
|
|
292
|
+
try:
|
|
293
|
+
line = int(self.trace_table.item(current_row,0).text())
|
|
294
|
+
new_line = int(max_index+1)
|
|
295
|
+
self.parent.plotted_lines[new_line] = copy.deepcopy(self.parent.plotted_lines[line])
|
|
296
|
+
self.append_trace_to_table(new_line)
|
|
297
|
+
self.editor_window.show_current_plot_settings()
|
|
298
|
+
self.editor_window.update_plots(update_data=False)
|
|
299
|
+
except Exception as e:
|
|
300
|
+
print('Cannot duplicate data: '+e)
|
|
301
|
+
|
|
302
|
+
def add_trace_manually(self): # When 'add' button pressed
|
|
303
|
+
try:
|
|
304
|
+
max_index=np.max(list(self.parent.plotted_lines.keys()))
|
|
305
|
+
except ValueError:
|
|
306
|
+
max_index=-1
|
|
307
|
+
try:
|
|
308
|
+
line={'checkstate': 2,
|
|
309
|
+
'X data': self.parent.all_parameter_names[0],
|
|
310
|
+
'Y data': self.parent.all_parameter_names[1],
|
|
311
|
+
'Bins': 100,
|
|
312
|
+
'Xerr':0,
|
|
313
|
+
'Yerr':0,
|
|
314
|
+
'linecolor': (1,0,0,1),
|
|
315
|
+
'linewidth': 1.5,
|
|
316
|
+
'linestyle': '-',
|
|
317
|
+
'filters':[]}
|
|
318
|
+
self.parent.plotted_lines[int(max_index+1)] = line
|
|
319
|
+
self.parent.prepare_data_for_plot(reload_data=True,reload_from_file=False,linefrompopup=int(max_index+1))
|
|
320
|
+
self.parent.plotted_lines[int(max_index+1)]['processed_data'] = self.parent.processed_data
|
|
321
|
+
self.parent.plotted_lines[int(max_index+1)]['raw_data'] = self.parent.raw_data
|
|
322
|
+
self.append_trace_to_table(int(max_index+1))
|
|
323
|
+
self.editor_window.show_current_plot_settings()
|
|
324
|
+
except Exception as e:
|
|
325
|
+
print('Cannot add data: '+e)
|
|
326
|
+
self.editor_window.update_plots(update_data=False)
|
|
327
|
+
|
|
328
|
+
def append_trace_to_table(self,index):
|
|
329
|
+
row = self.trace_table.rowCount()
|
|
330
|
+
line=self.parent.plotted_lines[index]
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
self.trace_table.itemChanged.disconnect(self.trace_table_edited)
|
|
334
|
+
self.trace_table.insertRow(row)
|
|
335
|
+
v = self.trace_table.verticalHeader()
|
|
336
|
+
v.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
|
337
|
+
for rownum in range(int(row+1)):
|
|
338
|
+
v.setSectionResizeMode(rownum, QtWidgets.QHeaderView.ResizeToContents)
|
|
339
|
+
|
|
340
|
+
linetrace_item = QtWidgets.QTableWidgetItem(str(index))
|
|
341
|
+
linetrace_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
342
|
+
QtCore.Qt.ItemIsEnabled |
|
|
343
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
344
|
+
linetrace_item.setText(str(index))
|
|
345
|
+
linetrace_item.setCheckState(line['checkstate'])
|
|
346
|
+
|
|
347
|
+
Xdata_item = QtWidgets.QTableWidgetItem(line['X data'])
|
|
348
|
+
bins_item = QtWidgets.QTableWidgetItem(str(line['Bins']))
|
|
349
|
+
Ydata_item = QtWidgets.QTableWidgetItem(line['Y data'])
|
|
350
|
+
|
|
351
|
+
style_item = QtWidgets.QTableWidgetItem(line['linestyle'])
|
|
352
|
+
|
|
353
|
+
color_box = QtWidgets.QTableWidgetItem('')
|
|
354
|
+
if type(line['linecolor'])==str:
|
|
355
|
+
color_box.setBackground(QtGui.QColor(line['linecolor']))
|
|
356
|
+
else:
|
|
357
|
+
rgbavalue = [int(line['linecolor'][0]*255), int(line['linecolor'][1]*255), int(line['linecolor'][2]*255),int(line['linecolor'][3]*255)]
|
|
358
|
+
color_box.setBackground(QtGui.QColor(*rgbavalue))
|
|
359
|
+
|
|
360
|
+
width_item = QtWidgets.QTableWidgetItem(str(line['linewidth']))
|
|
361
|
+
|
|
362
|
+
plot_fit_item = QtWidgets.QTableWidgetItem('')
|
|
363
|
+
if 'fit' in line.keys():
|
|
364
|
+
plot_fit_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
365
|
+
QtCore.Qt.ItemIsEnabled |
|
|
366
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
367
|
+
plot_fit_item.setCheckState(line['fit']['fit_checkstate'])
|
|
368
|
+
|
|
369
|
+
plot_components_item = QtWidgets.QTableWidgetItem('')
|
|
370
|
+
if 'fit' in line.keys():
|
|
371
|
+
plot_components_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
372
|
+
QtCore.Qt.ItemIsEnabled |
|
|
373
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
374
|
+
plot_components_item.setCheckState(line['fit']['fit_components_checkstate'])
|
|
375
|
+
|
|
376
|
+
plot_uncertainty_item = QtWidgets.QTableWidgetItem('')
|
|
377
|
+
if 'fit' in line.keys():
|
|
378
|
+
plot_uncertainty_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
379
|
+
QtCore.Qt.ItemIsEnabled |
|
|
380
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
381
|
+
plot_uncertainty_item.setCheckState(line['fit']['fit_uncertainty_checkstate'])
|
|
382
|
+
|
|
383
|
+
Xerr_item = QtWidgets.QTableWidgetItem(str(line['Xerr']))
|
|
384
|
+
Yerr_item = QtWidgets.QTableWidgetItem(str(line['Yerr']))
|
|
385
|
+
|
|
386
|
+
self.trace_table.setItem(row,0,linetrace_item)
|
|
387
|
+
if self.parent.plot_type == 'Histogram':
|
|
388
|
+
self.trace_table.setItem(row,1,bins_item)
|
|
389
|
+
else:
|
|
390
|
+
self.trace_table.setItem(row,1,Xdata_item)
|
|
391
|
+
self.trace_table.setItem(row,2,Ydata_item)
|
|
392
|
+
self.trace_table.setItem(row,3,style_item)
|
|
393
|
+
self.trace_table.setItem(row,4,color_box)
|
|
394
|
+
self.trace_table.setItem(row,5,width_item)
|
|
395
|
+
self.trace_table.setItem(row,6,Xerr_item)
|
|
396
|
+
self.trace_table.setItem(row,7,Yerr_item)
|
|
397
|
+
self.trace_table.setItem(row,8,plot_fit_item)
|
|
398
|
+
self.trace_table.setItem(row,9,plot_components_item)
|
|
399
|
+
self.trace_table.setItem(row,10,plot_uncertainty_item)
|
|
400
|
+
|
|
401
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
402
|
+
self.trace_table.setCurrentCell(row,0)
|
|
403
|
+
self.item_clicked(self.trace_table.currentItem())
|
|
404
|
+
|
|
405
|
+
def trace_table_edited(self,item):
|
|
406
|
+
current_item = item
|
|
407
|
+
current_col = item.column()
|
|
408
|
+
current_row = item.row()
|
|
409
|
+
line = int(self.trace_table.item(current_row,0).text())
|
|
410
|
+
|
|
411
|
+
if self.parent.plot_type == 'Histogram':
|
|
412
|
+
edit_dict={1:'Bins',2:'Y data',3:'linestyle',5:'linewidth',6:'Xerr',7:'Yerr'}
|
|
413
|
+
else:
|
|
414
|
+
edit_dict={1:'X data',2:'Y data',3:'linestyle',5:'linewidth',6:'Xerr',7:'Yerr'}
|
|
415
|
+
|
|
416
|
+
if current_col in edit_dict.keys():
|
|
417
|
+
self.parent.plotted_lines[line][edit_dict[current_col]] = current_item.text()
|
|
418
|
+
|
|
419
|
+
elif current_col == 8:
|
|
420
|
+
self.parent.plotted_lines[line]['fit']['fit_checkstate'] = current_item.checkState()
|
|
421
|
+
elif current_col == 9:
|
|
422
|
+
self.parent.plotted_lines[line]['fit']['fit_components_checkstate'] = current_item.checkState()
|
|
423
|
+
elif current_col == 10:
|
|
424
|
+
self.parent.plotted_lines[line]['fit']['fit_uncertainty_checkstate'] = current_item.checkState()
|
|
425
|
+
|
|
426
|
+
elif current_col == 0: # It's the checkstate for the linetrace.
|
|
427
|
+
self.parent.plotted_lines[line]['checkstate'] = current_item.checkState()
|
|
428
|
+
|
|
429
|
+
# If the X or Y data is changed, we need to update the processed data.
|
|
430
|
+
if current_col in [1,2,6,7]:
|
|
431
|
+
try:
|
|
432
|
+
self.parent.prepare_data_for_plot(reload_data=True,reload_from_file=False,linefrompopup=line)
|
|
433
|
+
self.parent.plotted_lines[line]['processed_data'] = [self.parent.processed_data[0],
|
|
434
|
+
self.parent.processed_data[1]]
|
|
435
|
+
self.editor_window.show_current_plot_settings()
|
|
436
|
+
|
|
437
|
+
except Exception as e:
|
|
438
|
+
print('Error changing plotted data: '+str(e))
|
|
439
|
+
self.editor_window.update_plots(update_data=False)
|
|
440
|
+
|
|
441
|
+
def remove_trace(self,which='selected'):
|
|
442
|
+
# which = 'selected', 'all'
|
|
443
|
+
if which=='selected':
|
|
444
|
+
try:
|
|
445
|
+
row = self.trace_table.currentRow()
|
|
446
|
+
linetrace = int(self.trace_table.item(row,0).text())
|
|
447
|
+
self.parent.plotted_lines.pop(linetrace)
|
|
448
|
+
self.trace_table.removeRow(row)
|
|
449
|
+
except Exception as e:
|
|
450
|
+
print(e)
|
|
451
|
+
elif which=='all':
|
|
452
|
+
self.parent.plotted_lines = {}
|
|
453
|
+
self.trace_table.setRowCount(0)
|
|
454
|
+
|
|
455
|
+
self.editor_window.update_plots(update_data=False)
|
|
456
|
+
|
|
457
|
+
def move_trace(self, direction):
|
|
458
|
+
self.trace_table.itemChanged.disconnect(self.trace_table_edited)
|
|
459
|
+
try:
|
|
460
|
+
current_row = self.trace_table.currentRow()
|
|
461
|
+
if direction == 'up' and current_row > 0:
|
|
462
|
+
delta=-1
|
|
463
|
+
elif direction == 'down' and current_row < self.trace_table.rowCount()-1:
|
|
464
|
+
delta=1
|
|
465
|
+
if delta in [-1,1]:
|
|
466
|
+
current_col = self.trace_table.currentColumn()
|
|
467
|
+
items = [self.trace_table.takeItem(current_row, c) for c in range(self.trace_table.columnCount())]
|
|
468
|
+
self.trace_table.removeRow(current_row)
|
|
469
|
+
new_row = current_row + delta
|
|
470
|
+
self.trace_table.insertRow(new_row)
|
|
471
|
+
for i, item in enumerate(items):
|
|
472
|
+
self.trace_table.setItem(new_row, i, item)
|
|
473
|
+
if current_col >= 0:
|
|
474
|
+
self.trace_table.setCurrentCell(new_row, current_col)
|
|
475
|
+
|
|
476
|
+
except Exception as e:
|
|
477
|
+
pass
|
|
478
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
479
|
+
|
|
480
|
+
def apply_colormap(self):
|
|
481
|
+
# Apply the colormap to the selected lines in the traces table.
|
|
482
|
+
self.trace_table.itemChanged.disconnect(self.trace_table_edited)
|
|
483
|
+
selected_colormap = cm.get_cmap(self.colormap_box.currentText())
|
|
484
|
+
applymethod = self.apply_colormap_to_box.currentText()
|
|
485
|
+
|
|
486
|
+
if applymethod == 'All':
|
|
487
|
+
lines_to_color = [int(self.trace_table.item(row,0).text()) for row in range(self.trace_table.rowCount())]
|
|
488
|
+
|
|
489
|
+
elif applymethod == 'Checked':
|
|
490
|
+
lines_to_color = self.get_checked_items(traces_or_fits='traces')
|
|
491
|
+
|
|
492
|
+
line_colors = selected_colormap(np.linspace(0.1,0.9,len(lines_to_color)))
|
|
493
|
+
rows = [self.trace_table.row(self.trace_table.findItems(str(line), QtCore.Qt.MatchExactly)[0]) for line in lines_to_color]
|
|
494
|
+
|
|
495
|
+
for i,line in enumerate(lines_to_color):
|
|
496
|
+
self.parent.plotted_lines[line]['linecolor'] = line_colors[i]
|
|
497
|
+
rgbavalue = [int(line_colors[i][0]*255), int(line_colors[i][1]*255), int(line_colors[i][2]*255),int(line_colors[i][3]*255)]
|
|
498
|
+
self.trace_table.item(rows[i],4).setBackground(QtGui.QColor(*rgbavalue))
|
|
499
|
+
|
|
500
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
501
|
+
self.editor_window.update_plots(update_data=False)
|
|
502
|
+
|
|
503
|
+
def colormap_type_edited(self):
|
|
504
|
+
self.colormap_box.clear()
|
|
505
|
+
self.colormap_box.addItems(cmaps[self.colormap_type_box.currentText()])
|
|
506
|
+
|
|
507
|
+
def open_trace_table_menu(self,position):
|
|
508
|
+
item=self.trace_table.currentItem()
|
|
509
|
+
column=self.trace_table.currentColumn()
|
|
510
|
+
row=self.trace_table.currentRow()
|
|
511
|
+
if column==4:
|
|
512
|
+
# Choose colour
|
|
513
|
+
menu = QtWidgets.QMenu(self)
|
|
514
|
+
color_action = menu.addAction("Choose Color")
|
|
515
|
+
|
|
516
|
+
# Show the menu at the cursor position
|
|
517
|
+
action = menu.exec_(self.trace_table.viewport().mapToGlobal(position))
|
|
518
|
+
|
|
519
|
+
if action == color_action:
|
|
520
|
+
if item:
|
|
521
|
+
color = QtWidgets.QColorDialog.getColor()
|
|
522
|
+
if color.isValid():
|
|
523
|
+
item.setBackground(color)
|
|
524
|
+
linetrace=int(self.trace_table.item(self.trace_table.currentRow(),0).text())
|
|
525
|
+
self.parent.plotted_lines[linetrace]['linecolor'] = color.name()
|
|
526
|
+
self.trace_table.setCurrentItem(self.trace_table.item(row,0)) # Otherwise the cell stays blue since it's selected.
|
|
527
|
+
self.editor_window.update_plots(update_data=False)
|
|
528
|
+
|
|
529
|
+
elif column in [1,2,6,7]:
|
|
530
|
+
menu = QtWidgets.QMenu(self)
|
|
531
|
+
for entry in self.parent.all_parameter_names:
|
|
532
|
+
action = QtWidgets.QAction(entry, self)
|
|
533
|
+
menu.addAction(action)
|
|
534
|
+
menu.triggered[QtWidgets.QAction].connect(self.replace_table_entry)
|
|
535
|
+
menu.popup(QtGui.QCursor.pos())
|
|
536
|
+
#self.update()
|
|
537
|
+
|
|
538
|
+
elif column==3:
|
|
539
|
+
menu = QtWidgets.QMenu(self)
|
|
540
|
+
styles=['-', '--', '-.', ':','.','o','v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', 'P', 'X']
|
|
541
|
+
for entry in styles:
|
|
542
|
+
action = QtWidgets.QAction(entry, self)
|
|
543
|
+
menu.addAction(action)
|
|
544
|
+
menu.triggered[QtWidgets.QAction].connect(self.replace_table_entry)
|
|
545
|
+
menu.popup(QtGui.QCursor.pos())
|
|
546
|
+
#self.update()
|
|
547
|
+
|
|
548
|
+
elif column in [0,8,9,10]:
|
|
549
|
+
menu = QtWidgets.QMenu(self)
|
|
550
|
+
check_all_action = menu.addAction("Check all")
|
|
551
|
+
uncheck_all_action = menu.addAction("Uncheck all")
|
|
552
|
+
|
|
553
|
+
if column==0:
|
|
554
|
+
check_all_action = menu.addAction("Check all")
|
|
555
|
+
uncheck_all_action = menu.addAction("Uncheck all")
|
|
556
|
+
|
|
557
|
+
elif column==8:
|
|
558
|
+
check_all_action = menu.addAction("Show all fits")
|
|
559
|
+
uncheck_all_action = menu.addAction("Hide all fits")
|
|
560
|
+
|
|
561
|
+
elif column==9:
|
|
562
|
+
check_all_action = menu.addAction("Show all fit components")
|
|
563
|
+
uncheck_all_action = menu.addAction("Hide all fit components")
|
|
564
|
+
elif column==10:
|
|
565
|
+
check_all_action = menu.addAction("Show all fit errors")
|
|
566
|
+
uncheck_all_action = menu.addAction("Hide all fit errors")
|
|
567
|
+
|
|
568
|
+
action = menu.exec_(self.trace_table.viewport().mapToGlobal(position))
|
|
569
|
+
self.trace_table.itemChanged.disconnect(self.trace_table_edited)
|
|
570
|
+
if action == check_all_action:
|
|
571
|
+
self.change_all_checkstate(column, QtCore.Qt.Checked)
|
|
572
|
+
elif action == uncheck_all_action:
|
|
573
|
+
self.change_all_checkstate(column, QtCore.Qt.Unchecked)
|
|
574
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
575
|
+
self.editor_window.update_plots(update_data=False)
|
|
576
|
+
|
|
577
|
+
def change_all_checkstate(self,column,checkstate):
|
|
578
|
+
for row in range(self.trace_table.rowCount()):
|
|
579
|
+
item = self.trace_table.item(row, column)
|
|
580
|
+
item.setCheckState(checkstate)
|
|
581
|
+
linetrace=int(self.trace_table.item(row,0).text())
|
|
582
|
+
if column==0:
|
|
583
|
+
self.parent.plotted_lines[linetrace]['checkstate'] = item.checkState()
|
|
584
|
+
elif column==8:
|
|
585
|
+
self.parent.plotted_lines[linetrace]['fit']['fit_checkstate'] = item.checkState()
|
|
586
|
+
elif column==9:
|
|
587
|
+
self.parent.plotted_lines[linetrace]['fit']['fit_components_checkstate'] = item.checkState()
|
|
588
|
+
elif column==10:
|
|
589
|
+
self.parent.plotted_lines[linetrace]['fit']['fit_uncertainty_checkstate'] = item.checkState()
|
|
590
|
+
|
|
591
|
+
def replace_table_entry(self, signal):
|
|
592
|
+
item = self.trace_table.currentItem()
|
|
593
|
+
item.setText(signal.text())
|
|
594
|
+
|
|
595
|
+
def limits_edited(self):
|
|
596
|
+
try:
|
|
597
|
+
if hasattr(self, 'minline'):
|
|
598
|
+
self.minline.remove()
|
|
599
|
+
del self.minline
|
|
600
|
+
if hasattr(self, 'maxline'):
|
|
601
|
+
self.maxline.remove()
|
|
602
|
+
del self.maxline
|
|
603
|
+
except:
|
|
604
|
+
pass
|
|
605
|
+
xmin=self.xmin_box.text()
|
|
606
|
+
xmax=self.xmax_box.text()
|
|
607
|
+
if xmin != '':
|
|
608
|
+
xmin=float(xmin)
|
|
609
|
+
self.minline=self.parent.axes.axvline(xmin, 0,0.1, color='blue', linestyle='--')
|
|
610
|
+
if xmax != '':
|
|
611
|
+
xmax=float(xmax)
|
|
612
|
+
self.maxline=self.parent.axes.axvline(xmax, 0,0.1, color='red', linestyle='--')
|
|
613
|
+
self.parent.canvas.draw()
|
|
614
|
+
|
|
615
|
+
def reset_limits(self):
|
|
616
|
+
try:
|
|
617
|
+
if hasattr(self, 'minline'):
|
|
618
|
+
self.minline.remove()
|
|
619
|
+
del self.minline
|
|
620
|
+
if hasattr(self, 'maxline'):
|
|
621
|
+
self.maxline.remove()
|
|
622
|
+
del self.maxline
|
|
623
|
+
except:
|
|
624
|
+
pass
|
|
625
|
+
self.xmin_box.clear()
|
|
626
|
+
self.xmax_box.clear()
|
|
627
|
+
|
|
628
|
+
self.parent.canvas.draw()
|
|
629
|
+
|
|
630
|
+
def update(self,clearplot=True):
|
|
631
|
+
if self.running:
|
|
632
|
+
self.draw_plot(clearplot)
|
|
633
|
+
fit_lines = self.get_checked_items(traces_or_fits='fits')
|
|
634
|
+
if len(fit_lines) > 0:
|
|
635
|
+
for line in fit_lines:
|
|
636
|
+
if 'fit' in self.parent.plotted_lines[line].keys():
|
|
637
|
+
self.draw_fits(line)
|
|
638
|
+
|
|
639
|
+
if self.xmin_box.text() != '' or self.xmax_box.text() != '':
|
|
640
|
+
self.limits_edited()
|
|
641
|
+
|
|
642
|
+
self.parent.canvas.draw()
|
|
643
|
+
|
|
644
|
+
def fit_class_changed(self):
|
|
645
|
+
self.fit_box.clear()
|
|
646
|
+
self.fit_box.addItems(fits.get_names(fitclass=self.fit_class_box.currentText()))
|
|
647
|
+
self.fit_box.setCurrentIndex(0)
|
|
648
|
+
self.fit_type_changed()
|
|
649
|
+
|
|
650
|
+
def fit_type_changed(self):
|
|
651
|
+
fit_function=fits.functions[self.fit_class_box.currentText()][self.fit_box.currentText()]
|
|
652
|
+
|
|
653
|
+
if 'default_inputs' in fit_function.keys():
|
|
654
|
+
self.input_edit.setText(fit_function['default_inputs'])
|
|
655
|
+
else:
|
|
656
|
+
self.input_edit.setText('')
|
|
657
|
+
if 'default_guess' in fit_function.keys():
|
|
658
|
+
self.guess_edit.setText(fit_function['default_guess'])
|
|
659
|
+
else:
|
|
660
|
+
self.guess_edit.setText('')
|
|
661
|
+
if 'inputs' in fit_function.keys():
|
|
662
|
+
self.input_label.setText(f'Input info: {fit_function['inputs']}')
|
|
663
|
+
else:
|
|
664
|
+
self.input_label.setText(f'Input info:')
|
|
665
|
+
if 'parameters' in fit_function.keys():
|
|
666
|
+
self.guess_checkbox.setText(f'Initial guess: {fit_function['parameters']}')
|
|
667
|
+
else:
|
|
668
|
+
self.guess_checkbox.setText(f'Initial guess:')
|
|
669
|
+
self.output_window.setText('Information about selected fit type:\n'+
|
|
670
|
+
fit_function['description'])
|
|
671
|
+
|
|
672
|
+
def collect_fit_data(self,x,y):
|
|
673
|
+
# If diagonal or circular, setting limits doesn't work; however, one can easily change the range during linetrace definition anyway
|
|
674
|
+
|
|
675
|
+
if self.xmin_box.text() != '':
|
|
676
|
+
xmin = float(self.xmin_box.text())
|
|
677
|
+
min_ind=(np.abs(x - xmin)).argmin()
|
|
678
|
+
else:
|
|
679
|
+
min_ind = 0
|
|
680
|
+
if self.xmax_box.text() != '':
|
|
681
|
+
xmax = float(self.xmax_box.text())
|
|
682
|
+
max_ind=(np.abs(x - xmax)).argmin()
|
|
683
|
+
else:
|
|
684
|
+
max_ind = len(x)-1
|
|
685
|
+
# Need to check if indices in 'wrong' order; i.e. x data descending.
|
|
686
|
+
if min_ind > max_ind:
|
|
687
|
+
x_forfit=x[max_ind:min_ind]
|
|
688
|
+
y_forfit=y[max_ind:min_ind]
|
|
689
|
+
else:
|
|
690
|
+
x_forfit=x[min_ind:max_ind]
|
|
691
|
+
y_forfit=y[min_ind:max_ind]
|
|
692
|
+
|
|
693
|
+
# if self.x_forfit[-1]<self.x_forfit[0]:
|
|
694
|
+
# x_forfit=x_forfit[::-1]
|
|
695
|
+
# y_forfit=y_forfit[::-1]
|
|
696
|
+
# self.fit_flipped=True
|
|
697
|
+
# else:
|
|
698
|
+
# self.fit_flipped=False
|
|
699
|
+
|
|
700
|
+
return x_forfit, y_forfit
|
|
701
|
+
|
|
702
|
+
def collect_fit_inputs(self,function_class,function_name):
|
|
703
|
+
if function_name in ['Expression','Statistics']:
|
|
704
|
+
inputinfo=self.input_edit.text()
|
|
705
|
+
elif 'inputs' in fits.functions[function_class][function_name].keys():
|
|
706
|
+
try:
|
|
707
|
+
inputinfo = [float(par) for par in self.input_edit.text().split(',')]
|
|
708
|
+
except Exception as e:
|
|
709
|
+
self.output_window.setText(f'Could not parse inputs: {e}\n'
|
|
710
|
+
'Review information below:\n'
|
|
711
|
+
f'{fits.functions[function_class][function_name]['description']}')
|
|
712
|
+
inputinfo=None
|
|
713
|
+
else:
|
|
714
|
+
inputinfo=None
|
|
715
|
+
return inputinfo
|
|
716
|
+
|
|
717
|
+
def collect_init_guess(self,function_class, function_name):
|
|
718
|
+
# Collect parameters/initial guess
|
|
719
|
+
if self.guess_checkbox.checkState():
|
|
720
|
+
try:
|
|
721
|
+
p0 = self.guess_edit.text().split(',')
|
|
722
|
+
except Exception as e:
|
|
723
|
+
self.output_window.setText(f'Could not parse Initial guess: {e}\n'
|
|
724
|
+
'Review information below:\n'
|
|
725
|
+
f'{fits.functions[function_class][function_name]['description']}')
|
|
726
|
+
p0 = None
|
|
727
|
+
else:
|
|
728
|
+
p0 = None
|
|
729
|
+
return p0
|
|
730
|
+
|
|
731
|
+
def start_fitting(self,line='manual',multilinefit=False):
|
|
732
|
+
if line=='manual':
|
|
733
|
+
current_row = self.trace_table.currentRow()
|
|
734
|
+
line = int(self.trace_table.item(current_row,0).text())
|
|
735
|
+
else: # We are being passed the line from fit_checked
|
|
736
|
+
# Still need to find the 'current row' to put a checkbox there later
|
|
737
|
+
labels=[int(self.trace_table.item(row,0).text()) for row in range(self.trace_table.rowCount())]
|
|
738
|
+
current_row=labels.index(line)
|
|
739
|
+
x,y=self.get_line_data(line)
|
|
740
|
+
x_forfit, y_forfit = self.collect_fit_data(x,y)
|
|
741
|
+
function_class = self.fit_class_box.currentText()
|
|
742
|
+
function_name = self.fit_box.currentText()
|
|
743
|
+
inputinfo=self.collect_fit_inputs(function_class,function_name)
|
|
744
|
+
p0=self.collect_init_guess(function_class,function_name)
|
|
745
|
+
|
|
746
|
+
# Try to do the fit.
|
|
747
|
+
if function_name != 'Statistics':
|
|
748
|
+
if 'stats' in self.parent.plotted_lines[line].keys():
|
|
749
|
+
self.parent.plotted_lines[line].pop('stats')
|
|
750
|
+
try:
|
|
751
|
+
fit_result = fits.fit_data(function_class=function_class, function_name=function_name,
|
|
752
|
+
xdata=x_forfit,ydata=y_forfit, p0=p0, inputinfo=inputinfo)
|
|
753
|
+
y_fit = fit_result.best_fit
|
|
754
|
+
|
|
755
|
+
self.parent.plotted_lines[line]['fit'] = {'fit_result': fit_result,
|
|
756
|
+
'xdata': x_forfit,
|
|
757
|
+
'ydata': y_forfit,
|
|
758
|
+
'fitted_y': y_fit,
|
|
759
|
+
'fit_checkstate': QtCore.Qt.Checked,
|
|
760
|
+
'fit_components_checkstate': QtCore.Qt.Unchecked,
|
|
761
|
+
'fit_uncertainty_checkstate': QtCore.Qt.Checked}
|
|
762
|
+
|
|
763
|
+
# Add a checkbox to the table now a fit exists.
|
|
764
|
+
self.trace_table.itemChanged.disconnect(self.trace_table_edited)
|
|
765
|
+
plot_fit_item = QtWidgets.QTableWidgetItem('')
|
|
766
|
+
plot_fit_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
767
|
+
QtCore.Qt.ItemIsEnabled |
|
|
768
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
769
|
+
plot_fit_item.setCheckState(QtCore.Qt.Checked)
|
|
770
|
+
self.trace_table.setItem(current_row,8,plot_fit_item)
|
|
771
|
+
|
|
772
|
+
plot_components_item = QtWidgets.QTableWidgetItem('')
|
|
773
|
+
plot_components_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
774
|
+
QtCore.Qt.ItemIsEnabled |
|
|
775
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
776
|
+
plot_components_item.setCheckState(QtCore.Qt.Unchecked)
|
|
777
|
+
self.trace_table.setItem(current_row,9,plot_components_item)
|
|
778
|
+
|
|
779
|
+
plot_uncertainty_item = QtWidgets.QTableWidgetItem('')
|
|
780
|
+
plot_uncertainty_item.setFlags(QtCore.Qt.ItemIsSelectable |
|
|
781
|
+
QtCore.Qt.ItemIsEnabled |
|
|
782
|
+
QtCore.Qt.ItemIsUserCheckable)
|
|
783
|
+
plot_uncertainty_item.setCheckState(QtCore.Qt.Checked)
|
|
784
|
+
self.trace_table.setItem(current_row,10,plot_uncertainty_item)
|
|
785
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
786
|
+
|
|
787
|
+
except Exception as e:
|
|
788
|
+
self.output_window.setText(f'Curve could not be fitted: {e}')
|
|
789
|
+
|
|
790
|
+
else:
|
|
791
|
+
if 'fit' in self.parent.plotted_lines[line].keys():
|
|
792
|
+
self.clear_fit(line)
|
|
793
|
+
self.parent.plotted_lines[line]['stats'] = fits.fit_data(function_class=function_class,
|
|
794
|
+
function_name=function_name,
|
|
795
|
+
xdata=x_forfit,ydata=y_forfit, p0=p0, inputinfo=inputinfo)
|
|
796
|
+
if 'autocorrelation' in self.parent.plotted_lines[line]['stats'].keys():
|
|
797
|
+
self.parent.add_array_to_data_dict(self.parent.plotted_lines[line]['stats']['xdata'],'x_for_autocorr')
|
|
798
|
+
self.parent.add_array_to_data_dict(self.parent.plotted_lines[line]['stats']['autocorrelation'],'autocorrelation')
|
|
799
|
+
if 'autocorrelation_norm' in self.parent.plotted_lines[line]['stats'].keys():
|
|
800
|
+
self.parent.add_array_to_data_dict(self.parent.plotted_lines[line]['stats']['xdata'],'x_for_autocorr')
|
|
801
|
+
self.parent.add_array_to_data_dict(self.parent.plotted_lines[line]['stats']['autocorrelation_norm'],'autocorrelation_norm')
|
|
802
|
+
if 'percentile' in self.parent.plotted_lines[line]['stats'].keys():
|
|
803
|
+
self.parent.add_array_to_data_dict(self.parent.plotted_lines[line]['stats']['percentile'],'percentile')
|
|
804
|
+
self.parent.add_array_to_data_dict(self.parent.plotted_lines[line]['stats']['percentiles'],'percentiles')
|
|
805
|
+
|
|
806
|
+
#self.parent.prepare_data_for_plot(reload_data=True,reload_from_file=False,linefrompopup=line)
|
|
807
|
+
|
|
808
|
+
if not multilinefit:
|
|
809
|
+
self.print_parameters(line)
|
|
810
|
+
self.editor_window.update_plots(update_data=False)
|
|
811
|
+
|
|
812
|
+
def fit_checked(self):
|
|
813
|
+
# Fit all checked items in the table.
|
|
814
|
+
fit_lines = self.get_checked_items(traces_or_fits='traces')
|
|
815
|
+
for line in fit_lines:
|
|
816
|
+
self.start_fitting(line,multilinefit=True)
|
|
817
|
+
self.print_parameters(line)
|
|
818
|
+
self.editor_window.update_plots(update_data=False)
|
|
819
|
+
|
|
820
|
+
def print_parameters(self,line):
|
|
821
|
+
self.output_window.clear()
|
|
822
|
+
if 'stats' in self.parent.plotted_lines[line].keys():
|
|
823
|
+
text='Statistics:\n'
|
|
824
|
+
for key in self.parent.plotted_lines[line]['stats'].keys():
|
|
825
|
+
text+=f'{key}: {self.parent.plotted_lines[line]['stats'][key]}\n'
|
|
826
|
+
self.output_window.setText(text)
|
|
827
|
+
else:
|
|
828
|
+
try:
|
|
829
|
+
self.output_window.setText(self.parent.plotted_lines[line]['fit']['fit_result'].fit_report())
|
|
830
|
+
|
|
831
|
+
except Exception as e:
|
|
832
|
+
self.output_window.setText('Could not print fit parameters:', e)
|
|
833
|
+
|
|
834
|
+
def get_line_data(self,line):
|
|
835
|
+
# Returns the processed x,y data for a particular entry in the plotted lines dictionary
|
|
836
|
+
self.parent.prepare_data_for_plot(reload_data=True,reload_from_file=False,
|
|
837
|
+
linefrompopup=line,plot_type=self.parent.plot_type)
|
|
838
|
+
x=self.parent.plotted_lines[line]['processed_data'][0]
|
|
839
|
+
y=self.parent.plotted_lines[line]['processed_data'][1]
|
|
840
|
+
return (x,y)
|
|
841
|
+
|
|
842
|
+
def plot_Yerr(self,x,y,error,line):
|
|
843
|
+
if self.parent.plot_type == 'Histogram':
|
|
844
|
+
self.parent.axes.errorbar(x, y,
|
|
845
|
+
yerr=error,
|
|
846
|
+
fmt='none',
|
|
847
|
+
ecolor=self.parent.plotted_lines[line]['linecolor'],
|
|
848
|
+
elinewidth=self.parent.plotted_lines[line]['linewidth'],
|
|
849
|
+
capsize=4)
|
|
850
|
+
else:
|
|
851
|
+
self.parent.axes.fill_between(x, y+error, y-error,
|
|
852
|
+
alpha=0.2, color=self.parent.plotted_lines[line]['linecolor'])
|
|
853
|
+
|
|
854
|
+
def plot_Xerr(self,x,y,error,line):
|
|
855
|
+
if self.parent.plot_type == 'Histogram':
|
|
856
|
+
self.parent.axes.errorbar(x, y,
|
|
857
|
+
xerr=error,
|
|
858
|
+
fmt='none',
|
|
859
|
+
ecolor=self.parent.plotted_lines[line]['linecolor'],
|
|
860
|
+
elinewidth=self.parent.plotted_lines[line]['linewidth'],
|
|
861
|
+
capsize=4)
|
|
862
|
+
else:
|
|
863
|
+
self.parent.axes.fill_betweenx(y, x+error, x-error,
|
|
864
|
+
alpha=0.2, color=self.parent.plotted_lines[line]['linecolor'])
|
|
865
|
+
|
|
866
|
+
def process_uncertainties(self,line,x,y):
|
|
867
|
+
for axiserr in ['Xerr','Yerr']:
|
|
868
|
+
if self.parent.plotted_lines[line][axiserr] not in [0,'0']:
|
|
869
|
+
try: # if a single number
|
|
870
|
+
error = float(self.parent.plotted_lines[line][axiserr])
|
|
871
|
+
except ValueError: # if name of array or has %, value error is thrown
|
|
872
|
+
if '%' in self.parent.plotted_lines[line][axiserr]:
|
|
873
|
+
# For percentage errors, make the absolute value array
|
|
874
|
+
if axiserr=='Yerr':
|
|
875
|
+
error = np.abs(y) * float(self.parent.plotted_lines[line][axiserr].replace('%','')) / 100
|
|
876
|
+
else:
|
|
877
|
+
error = np.abs(x) * float(self.parent.plotted_lines[line][axiserr].replace('%','')) / 100
|
|
878
|
+
else:
|
|
879
|
+
# Get the error data from the loaded data
|
|
880
|
+
errorname = self.parent.plotted_lines[line][axiserr]
|
|
881
|
+
error = copy.deepcopy(self.parent.data_dict[errorname])
|
|
882
|
+
# Only apply multiply or divide filters (and only if they are checked of course)
|
|
883
|
+
if 'filters' in self.parent.plotted_lines[line].keys():
|
|
884
|
+
for filt in self.parent.plotted_lines[line]['filters']:
|
|
885
|
+
if filt.checkstate and filt.name in ['Multiply','Divide'] and ((filt.method == 'X' and axiserr=='Xerr')
|
|
886
|
+
or (filt.method == 'Y' and axiserr=='Yerr')):
|
|
887
|
+
if filt.name == 'Multiply':
|
|
888
|
+
error = error * float(filt.settings[0])
|
|
889
|
+
elif filt.name == 'Divide':
|
|
890
|
+
error = error / float(filt.settings[0])
|
|
891
|
+
|
|
892
|
+
if axiserr=='Yerr':
|
|
893
|
+
self.plot_Yerr(x,y,error,line)
|
|
894
|
+
else:
|
|
895
|
+
self.plot_Xerr(x,y,error,line)
|
|
896
|
+
|
|
897
|
+
def draw_plot(self,clearplot=True):
|
|
898
|
+
if clearplot:
|
|
899
|
+
self.parent.axes.clear()
|
|
900
|
+
lines = self.get_checked_items()
|
|
901
|
+
if len(lines) > 0:
|
|
902
|
+
for line in lines:
|
|
903
|
+
x,y= self.get_line_data(line)
|
|
904
|
+
if len(x)!=len(y):
|
|
905
|
+
pass # This can happen for combined datasets with good reason: the user may haves e.g. _just_ chosen a new x axis
|
|
906
|
+
# that doesn't match the y axis, and are just about to choose an appropriate y axis.
|
|
907
|
+
# Instead of crashing the program and throwing an error, just skip plotting until they choose something sensible.
|
|
908
|
+
else:
|
|
909
|
+
if self.parent.plot_type == 'Histogram':
|
|
910
|
+
self.parent.settings['ylabel'] = 'Counts'
|
|
911
|
+
if 'default_ylabel' in self.parent.settings.keys():
|
|
912
|
+
self.parent.settings['xlabel'] = self.parent.settings['default_ylabel']
|
|
913
|
+
drawstyle='steps-mid'
|
|
914
|
+
else:
|
|
915
|
+
drawstyle='default'
|
|
916
|
+
if 'FFT' in self.editor_window.plot_type_box.currentText():
|
|
917
|
+
self.parent.settings['ylabel'] = 'Amplitude (a.u.)'
|
|
918
|
+
self.parent.settings['xlabel'] = 'Frequency'
|
|
919
|
+
#self.parent.image =
|
|
920
|
+
self.parent.axes.plot(x, y,
|
|
921
|
+
self.parent.plotted_lines[line]['linestyle'],
|
|
922
|
+
linewidth=self.parent.plotted_lines[line]['linewidth'],
|
|
923
|
+
markersize=self.parent.plotted_lines[line]['linewidth'],
|
|
924
|
+
color=self.parent.plotted_lines[line]['linecolor'],
|
|
925
|
+
drawstyle=drawstyle)
|
|
926
|
+
|
|
927
|
+
if self.parent.plotted_lines[line]['Xerr'] not in [0,'0'] or self.parent.plotted_lines[line]['Yerr'] not in [0,'0']:
|
|
928
|
+
self.process_uncertainties(line,x,y)
|
|
929
|
+
self.parent.apply_plot_settings()
|
|
930
|
+
#self.parent.apply_axlim_settings()
|
|
931
|
+
self.parent.apply_axscale_settings()
|
|
932
|
+
self.editor_window.figure.tight_layout()
|
|
933
|
+
|
|
934
|
+
def draw_fits(self,line):
|
|
935
|
+
try:
|
|
936
|
+
fit_result=self.parent.plotted_lines[line]['fit']['fit_result']
|
|
937
|
+
x_forfit=self.parent.plotted_lines[line]['fit']['xdata']
|
|
938
|
+
y_fit=fit_result.best_fit
|
|
939
|
+
self.parent.axes.plot(x_forfit, y_fit, 'k--',
|
|
940
|
+
linewidth=self.parent.plotted_lines[line]['linewidth'])
|
|
941
|
+
if self.parent.plotted_lines[line]['fit']['fit_uncertainty_checkstate'] == QtCore.Qt.Checked:
|
|
942
|
+
uncertainty = fit_result.eval_uncertainty()
|
|
943
|
+
self.parent.axes.fill_between(x_forfit, y_fit+uncertainty, y_fit-uncertainty,
|
|
944
|
+
alpha=0.2, color='grey', linewidth=0)
|
|
945
|
+
if self.parent.plotted_lines[line]['fit']['fit_components_checkstate'] == QtCore.Qt.Checked:
|
|
946
|
+
fit_components=fit_result.eval_components()
|
|
947
|
+
if self.colormap_box.currentText() == 'viridis':
|
|
948
|
+
selected_colormap = cm.get_cmap('plasma')
|
|
949
|
+
elif self.colormap_box.currentText() == 'plasma':
|
|
950
|
+
selected_colormap = cm.get_cmap('viridis')
|
|
951
|
+
line_colors = selected_colormap(np.linspace(0.1,0.9,len(fit_components.keys())))
|
|
952
|
+
for i,key in enumerate(fit_components.keys()):
|
|
953
|
+
self.parent.axes.plot(x_forfit, fit_components[key], '--', color=line_colors[i],alpha=0.75, linewidth=self.parent.plotted_lines[line]['linewidth'])
|
|
954
|
+
except Exception as e:
|
|
955
|
+
self.output_window.setText(f'Could not plot fit: {e}')
|
|
956
|
+
|
|
957
|
+
def save_fit_result(self):
|
|
958
|
+
current_row = self.trace_table.currentRow()
|
|
959
|
+
line = int(self.trace_table.item(current_row,0).text())
|
|
960
|
+
|
|
961
|
+
# Fits get saved in the lmfit format.
|
|
962
|
+
if 'fit' in self.parent.plotted_lines[line].keys():
|
|
963
|
+
fit_result = self.parent.plotted_lines[line]['fit']['fit_result']
|
|
964
|
+
formats = 'lmfit Model Result (*.sav)'
|
|
965
|
+
filename, extension = QtWidgets.QFileDialog.getSaveFileName(
|
|
966
|
+
self, 'Save Fit Result','', formats)
|
|
967
|
+
save_modelresult(fit_result,filename)
|
|
968
|
+
|
|
969
|
+
#Stats can simply be saved in a json
|
|
970
|
+
elif 'stats' in self.parent.plotted_lines[line].keys():
|
|
971
|
+
formats = 'JSON (*.json)'
|
|
972
|
+
filename, extension = QtWidgets.QFileDialog.getSaveFileName(
|
|
973
|
+
self, 'Save Statistics','', formats)
|
|
974
|
+
export_dict={'data_name':self.parent.label,
|
|
975
|
+
'X_param':self.parent.plotted_lines[line]['X data'],
|
|
976
|
+
'Y_param':self.parent.plotted_lines[line]['Y data']}
|
|
977
|
+
for key in self.parent.plotted_lines[line]['stats'].keys():
|
|
978
|
+
if isinstance(self.parent.plotted_lines[line]['stats'][key],np.ndarray):
|
|
979
|
+
export_dict[key] = self.parent.plotted_lines[line]['stats'][key].tolist()
|
|
980
|
+
else:
|
|
981
|
+
export_dict[key] = self.parent.plotted_lines[line]['stats'][key]
|
|
982
|
+
try:
|
|
983
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
|
984
|
+
jsondump(export_dict, f, ensure_ascii=False,indent=4)
|
|
985
|
+
except Exception as e:
|
|
986
|
+
print('Could not save statistics:', e)
|
|
987
|
+
|
|
988
|
+
def save_all_fits(self):
|
|
989
|
+
current_row = self.trace_table.currentRow()
|
|
990
|
+
line = int(self.trace_table.item(current_row,0).text())
|
|
991
|
+
# Can save _either_ fits or stats, and decide which to do based on whether the current line has a fit or stats.
|
|
992
|
+
if 'fit' in self.parent.plotted_lines[line].keys():
|
|
993
|
+
fit_lines = self.get_checked_items(traces_or_fits='fits')
|
|
994
|
+
formats = 'lmfit Model Result (*.sav)'
|
|
995
|
+
filename, extension = QtWidgets.QFileDialog.getSaveFileName(
|
|
996
|
+
self, 'Save Fit Result: Select base name','', formats)
|
|
997
|
+
for line in fit_lines:
|
|
998
|
+
fit_result = self.parent.plotted_lines[line]['fit']['fit_result']
|
|
999
|
+
save_modelresult(fit_result,filename.replace('.sav',f'_{line}.sav'))
|
|
1000
|
+
|
|
1001
|
+
elif 'stats' in self.parent.plotted_lines[line].keys():
|
|
1002
|
+
# All the stats can go into a single json.
|
|
1003
|
+
stat_lines=[]
|
|
1004
|
+
for line in self.parent.plotted_lines.keys():
|
|
1005
|
+
if 'stats' in self.parent.plotted_lines[line].keys():
|
|
1006
|
+
stat_lines.append(line)
|
|
1007
|
+
formats = 'JSON (*.json)'
|
|
1008
|
+
filename, extension = QtWidgets.QFileDialog.getSaveFileName(
|
|
1009
|
+
self, 'Save Statistics','', formats)
|
|
1010
|
+
export_dict={'data_name':self.parent.label,
|
|
1011
|
+
'linetrace_stats':{}}
|
|
1012
|
+
for line in stat_lines:
|
|
1013
|
+
export_dict['linetrace_stats'][line]={}
|
|
1014
|
+
export_dict['linetrace_stats'][line]['X_param']=self.parent.plotted_lines[line]['X data']
|
|
1015
|
+
export_dict['linetrace_stats'][line]['Y_param']=self.parent.plotted_lines[line]['Y data']
|
|
1016
|
+
for key in self.parent.plotted_lines[line]['stats'].keys():
|
|
1017
|
+
if isinstance(self.parent.plotted_lines[line]['stats'][key],np.ndarray):
|
|
1018
|
+
export_dict['linetrace_stats'][line][key] = self.parent.plotted_lines[line]['stats'][key].tolist()
|
|
1019
|
+
else:
|
|
1020
|
+
export_dict['linetrace_stats'][line][key] = self.parent.plotted_lines[line]['stats'][key]
|
|
1021
|
+
try:
|
|
1022
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
|
1023
|
+
jsondump(export_dict, f, ensure_ascii=False,indent=4)
|
|
1024
|
+
except Exception as e:
|
|
1025
|
+
print('Could not save statistics:', e)
|
|
1026
|
+
else:
|
|
1027
|
+
print('First select a linecut with either a fit or statistics. '
|
|
1028
|
+
'Either the fits or stats for all lines will be saved, based on that.')
|
|
1029
|
+
|
|
1030
|
+
def clear_fit(self,line='manual'):
|
|
1031
|
+
self.trace_table.itemChanged.disconnect(self.trace_table_edited)
|
|
1032
|
+
if line=='manual':
|
|
1033
|
+
manual=True
|
|
1034
|
+
row = self.trace_table.currentRow()
|
|
1035
|
+
line = int(self.trace_table.item(row,0).text())
|
|
1036
|
+
else:
|
|
1037
|
+
manual=False
|
|
1038
|
+
for row in range(self.trace_table.rowCount()):
|
|
1039
|
+
if int(self.trace_table.item(row,0).text())==line:
|
|
1040
|
+
break
|
|
1041
|
+
|
|
1042
|
+
if 'fit' in self.parent.plotted_lines[line].keys():
|
|
1043
|
+
self.parent.plotted_lines[line].pop('fit')
|
|
1044
|
+
self.trace_table.setItem(row,8,QtWidgets.QTableWidgetItem(''))
|
|
1045
|
+
self.trace_table.setItem(row,9,QtWidgets.QTableWidgetItem(''))
|
|
1046
|
+
self.trace_table.setItem(row,10,QtWidgets.QTableWidgetItem(''))
|
|
1047
|
+
if manual:
|
|
1048
|
+
self.editor_window.update_plots(update_data=False)
|
|
1049
|
+
|
|
1050
|
+
# should never be both, but use 'if' just in case
|
|
1051
|
+
if 'stats' in self.parent.plotted_lines[line].keys():
|
|
1052
|
+
self.parent.plotted_lines[line].pop('stats')
|
|
1053
|
+
|
|
1054
|
+
if manual:
|
|
1055
|
+
fit_function=fits.functions[self.fit_class_box.currentText()][self.fit_box.currentText()]
|
|
1056
|
+
self.output_window.setText('Information about selected fit type:\n'+
|
|
1057
|
+
fit_function['description'])
|
|
1058
|
+
|
|
1059
|
+
self.trace_table.itemChanged.connect(self.trace_table_edited)
|
|
1060
|
+
|
|
1061
|
+
def clear_all_fits(self):
|
|
1062
|
+
try:
|
|
1063
|
+
for line in self.parent.plotted_lines.keys():
|
|
1064
|
+
self.clear_fit(line)
|
|
1065
|
+
except Exception as e:
|
|
1066
|
+
print('Could not clear all fits:', e)
|
|
1067
|
+
|
|
1068
|
+
fit_function=fits.functions[self.fit_class_box.currentText()][self.fit_box.currentText()]
|
|
1069
|
+
self.output_window.setText('Information about selected fit type:\n'+fit_function['description'])
|
|
1070
|
+
self.editor_window.update_plots(update_data=False)
|
|
1071
|
+
|
|
1072
|
+
def save_fit_preset(self):
|
|
1073
|
+
preset_dict={}
|
|
1074
|
+
preset_dict['xlims']=[self.xmin_box.text(),self.xmax_box.text()]
|
|
1075
|
+
preset_dict['function_class']=self.fit_class_box.currentText()
|
|
1076
|
+
preset_dict['function_name']=self.fit_box.currentText()
|
|
1077
|
+
preset_dict['inputinfo']=self.input_edit.text()
|
|
1078
|
+
preset_dict['initial_guess']=self.guess_edit.text()
|
|
1079
|
+
if self.guess_checkbox.isChecked():
|
|
1080
|
+
preset_dict['intial_checkbox']=True
|
|
1081
|
+
|
|
1082
|
+
formats = 'JSON (*.json)'
|
|
1083
|
+
filename, extension = QtWidgets.QFileDialog.getSaveFileName(
|
|
1084
|
+
self, 'Save Figure As', '', formats)
|
|
1085
|
+
if filename:
|
|
1086
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
|
1087
|
+
jsondump(preset_dict, f, ensure_ascii=False)
|
|
1088
|
+
|
|
1089
|
+
def load_fit_preset(self):
|
|
1090
|
+
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
|
|
1091
|
+
self, 'Open Fitting Preset', '', '*.json')
|
|
1092
|
+
if filename:
|
|
1093
|
+
with open(filename) as f:
|
|
1094
|
+
preset_dict=jsonload(f)
|
|
1095
|
+
self.xmin_box.setText(preset_dict['xlims'][0])
|
|
1096
|
+
self.xmax_box.setText(preset_dict['xlims'][1])
|
|
1097
|
+
self.fit_class_box.setCurrentText(preset_dict['function_class'])
|
|
1098
|
+
self.fit_box.setCurrentText(preset_dict['function_name'])
|
|
1099
|
+
self.input_edit.setText(preset_dict['inputinfo'])
|
|
1100
|
+
self.guess_edit.setText(preset_dict['initial_guess'])
|
|
1101
|
+
if preset_dict['intial_checkbox']:
|
|
1102
|
+
self.guess_checkbox.setCheckState(QtCore.Qt.Checked)
|
|
1103
|
+
else:
|
|
1104
|
+
self.guess_checkbox.setCheckState(QtCore.Qt.UnChecked)
|
|
1105
|
+
self.editor_window.update_plots(update_data=False)
|
|
1106
|
+
|
|
1107
|
+
def closeEvent(self, event):
|
|
1108
|
+
self.running = False
|