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.
Files changed (82) hide show
  1. qcodespp/__init__.py +77 -0
  2. qcodespp/actions.py +237 -0
  3. qcodespp/data/__init__.py +0 -0
  4. qcodespp/data/data_array.py +555 -0
  5. qcodespp/data/data_set.py +926 -0
  6. qcodespp/data/format.py +338 -0
  7. qcodespp/data/gnuplot_format.py +425 -0
  8. qcodespp/data/hdf5_format.py +537 -0
  9. qcodespp/data/hdf5_format_hickle.py +63 -0
  10. qcodespp/data/io.py +241 -0
  11. qcodespp/data/location.py +171 -0
  12. qcodespp/instrument_drivers/Keysight/Keysight_34450A.py +9 -0
  13. qcodespp/instrument_drivers/Keysight/__init__.py +0 -0
  14. qcodespp/instrument_drivers/Keysight/private/Keysight_344xxA.py +456 -0
  15. qcodespp/instrument_drivers/Keysight/private/__init__.py +0 -0
  16. qcodespp/instrument_drivers/Lakeshore/Model_340.py +483 -0
  17. qcodespp/instrument_drivers/Lakeshore/__init__.py +0 -0
  18. qcodespp/instrument_drivers/QDevil/159fit_params_high_latest.json +386 -0
  19. qcodespp/instrument_drivers/QDevil/159fit_params_low_latest.json +386 -0
  20. qcodespp/instrument_drivers/QDevil/QDAC2.py +3229 -0
  21. qcodespp/instrument_drivers/QDevil/QDAC2_Array.py +300 -0
  22. qcodespp/instrument_drivers/QDevil/QSwitch_elab.py +652 -0
  23. qcodespp/instrument_drivers/QDevil/__init__.py +0 -0
  24. qcodespp/instrument_drivers/ZI/ZIHF2LI.py +415 -0
  25. qcodespp/instrument_drivers/ZI/ZIMFLI.py +764 -0
  26. qcodespp/instrument_drivers/ZI/ZIMFLI_sync.py +108 -0
  27. qcodespp/instrument_drivers/ZI/ZIUHFLI.py +2179 -0
  28. qcodespp/instrument_drivers/ZI/__init__.py +1 -0
  29. qcodespp/instrument_drivers/__init__.py +0 -0
  30. qcodespp/instrument_drivers/cryogenic/CryogenicSMS120C.py +599 -0
  31. qcodespp/instrument_drivers/cryogenic/__init__.py +0 -0
  32. qcodespp/instrument_drivers/instrument_mocks.py +379 -0
  33. qcodespp/instrument_drivers/multiplexer_instrument.py +287 -0
  34. qcodespp/instrument_drivers/national_instruments/PXI_4461.py +451 -0
  35. qcodespp/instrument_drivers/national_instruments/PXI_4462.py +290 -0
  36. qcodespp/instrument_drivers/national_instruments/PXI_6259.py +436 -0
  37. qcodespp/instrument_drivers/national_instruments/PXI_6733.py +230 -0
  38. qcodespp/instrument_drivers/national_instruments/PXI_6733_e.py +243 -0
  39. qcodespp/instrument_drivers/national_instruments/__init__.py +0 -0
  40. qcodespp/instrument_drivers/oxford/__init__.py +0 -0
  41. qcodespp/instrument_drivers/oxford/mercuryiPS_serial.py +658 -0
  42. qcodespp/instrument_drivers/oxford/triton.py +434 -0
  43. qcodespp/instrument_drivers/tektronix/Keithley_2000_Scan.py +65 -0
  44. qcodespp/instrument_drivers/tektronix/Keithley_2182a.py +190 -0
  45. qcodespp/instrument_drivers/tektronix/Keithley_2400.py +158 -0
  46. qcodespp/instrument_drivers/tektronix/Keithley_2450.py +242 -0
  47. qcodespp/instrument_drivers/tektronix/Keithley_2700.py +397 -0
  48. qcodespp/instrument_drivers/tektronix/Keithley_6500.py +161 -0
  49. qcodespp/instrument_drivers/tektronix/__init__.py +0 -0
  50. qcodespp/instrument_drivers/yokogawa/GS200.py +513 -0
  51. qcodespp/instrument_drivers/yokogawa/__init__.py +0 -0
  52. qcodespp/loops.py +1342 -0
  53. qcodespp/measure.py +310 -0
  54. qcodespp/parameters.py +311 -0
  55. qcodespp/plotting/RemotePlot.py +469 -0
  56. qcodespp/plotting/RemotePlotClient.py +367 -0
  57. qcodespp/plotting/RemoteQtPlotWidgets.py +871 -0
  58. qcodespp/plotting/__init__.py +0 -0
  59. qcodespp/plotting/analysis_tools.py +195 -0
  60. qcodespp/plotting/colors.py +135 -0
  61. qcodespp/plotting/offline/__init__.py +0 -0
  62. qcodespp/plotting/offline/datatypes.py +999 -0
  63. qcodespp/plotting/offline/design.py +906 -0
  64. qcodespp/plotting/offline/design.ui +1578 -0
  65. qcodespp/plotting/offline/editor.py +2966 -0
  66. qcodespp/plotting/offline/filters.py +561 -0
  67. qcodespp/plotting/offline/fits.py +1171 -0
  68. qcodespp/plotting/offline/helpers.py +288 -0
  69. qcodespp/plotting/offline/iconGadget.png +0 -0
  70. qcodespp/plotting/offline/main.py +55 -0
  71. qcodespp/plotting/offline/popupwindows.py +1848 -0
  72. qcodespp/plotting/offline/qcodes_pp_extension.py +328 -0
  73. qcodespp/plotting/offline/sidebars.py +1108 -0
  74. qcodespp/station.py +290 -0
  75. qcodespp/utils/__init__.py +0 -0
  76. qcodespp/utils/helpers.py +618 -0
  77. qcodespp/utils/visa_helpers.py +61 -0
  78. qcodespp/version.py +12 -0
  79. qcodespp-0.1.0.dist-info/METADATA +91 -0
  80. qcodespp-0.1.0.dist-info/RECORD +82 -0
  81. qcodespp-0.1.0.dist-info/WHEEL +4 -0
  82. 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