llwp 2.1.1__tar.gz → 2.1.2__tar.gz
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.
- {llwp-2.1.1/llwp.egg-info → llwp-2.1.2}/PKG-INFO +1 -1
- {llwp-2.1.1 → llwp-2.1.2}/llwp/LLWP.py +203 -144
- {llwp-2.1.1 → llwp-2.1.2/llwp.egg-info}/PKG-INFO +1 -1
- {llwp-2.1.1 → llwp-2.1.2}/pyproject.toml +1 -1
- {llwp-2.1.1 → llwp-2.1.2}/LICENSE +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/MANIFEST.in +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/README.md +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp/ASAP.svg +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp/ASAPMac.png +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp/LLWP.svg +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp/LLWPMac.png +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp/__init__.py +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp/__main__.py +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp.egg-info/SOURCES.txt +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp.egg-info/dependency_links.txt +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp.egg-info/entry_points.txt +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp.egg-info/requires.txt +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/llwp.egg-info/top_level.txt +0 -0
- {llwp-2.1.1 → llwp-2.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llwp
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: LLWP is a fast, efficient and easy solution for exploring and assigning spectra - relying on Loomis-Wood plots.
|
|
5
5
|
Author-email: Luis Bonah <bonah@ph1.uni-koeln.de>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -158,6 +158,10 @@ def status_d(func):
|
|
|
158
158
|
return _wrapper
|
|
159
159
|
|
|
160
160
|
|
|
161
|
+
# Guards all modifications of matplotlib artists as well as the rendering of the figures
|
|
162
|
+
matplotlib_lock = threading.RLock()
|
|
163
|
+
|
|
164
|
+
|
|
161
165
|
def lock_d(lock):
|
|
162
166
|
@wrapt.decorator
|
|
163
167
|
def _wrapper(wrapped, instance, args, kwargs):
|
|
@@ -707,6 +711,7 @@ class PlotWidget(QWidget):
|
|
|
707
711
|
|
|
708
712
|
self.update_plot()
|
|
709
713
|
|
|
714
|
+
@lock_d(matplotlib_lock)
|
|
710
715
|
def update_plot(self):
|
|
711
716
|
scaling = config["plot_yscale"]
|
|
712
717
|
|
|
@@ -912,16 +917,17 @@ class PlotWidget(QWidget):
|
|
|
912
917
|
|
|
913
918
|
|
|
914
919
|
drawplot_decorator = DynamicDecorator()
|
|
915
|
-
matplotlib_lock = threading.RLock()
|
|
916
920
|
|
|
917
921
|
|
|
918
922
|
##
|
|
919
923
|
## Customized PyQt classes
|
|
920
924
|
##
|
|
921
925
|
class QThread(QThread):
|
|
926
|
+
# Maps each function to the sequence number of its most recently created thread
|
|
922
927
|
active_runnables = {}
|
|
923
928
|
lock = threading.RLock()
|
|
924
929
|
threads = set()
|
|
930
|
+
_sequence = AtomicCounter()
|
|
925
931
|
|
|
926
932
|
@classmethod
|
|
927
933
|
def threaded_d(cls, func):
|
|
@@ -932,26 +938,37 @@ class QThread(QThread):
|
|
|
932
938
|
|
|
933
939
|
return wrapper
|
|
934
940
|
|
|
941
|
+
@classmethod
|
|
942
|
+
def _release_finished(cls):
|
|
943
|
+
# Python references are only dropped once Qt reports the thread as finished.
|
|
944
|
+
# Dropping them earlier (e.g. from the finished signal, which is emitted
|
|
945
|
+
# before the thread has fully stopped) can destroy a running QThread.
|
|
946
|
+
with cls.lock:
|
|
947
|
+
for thread in [t for t in cls.threads if t.isFinished()]:
|
|
948
|
+
thread.wait()
|
|
949
|
+
cls.threads.discard(thread)
|
|
950
|
+
|
|
935
951
|
def __init__(self, function, *args, **kwargs):
|
|
936
952
|
super().__init__()
|
|
937
953
|
self.function, self.args, self.kwargs = function, args, kwargs
|
|
938
|
-
self.
|
|
939
|
-
|
|
954
|
+
with self.lock:
|
|
955
|
+
self._release_finished()
|
|
956
|
+
# The sequence number is assigned at creation (not when the thread
|
|
957
|
+
# starts running), so the newest request always wins and the result
|
|
958
|
+
# does not depend on OS scheduling or on reused thread idents.
|
|
959
|
+
self.sequence = self._sequence.increase()
|
|
960
|
+
self.active_runnables[self.function] = self.sequence
|
|
961
|
+
self.threads.add(self)
|
|
940
962
|
|
|
941
963
|
def earlyreturn(self):
|
|
942
|
-
if self.active_runnables
|
|
964
|
+
if self.active_runnables.get(self.function) != self.sequence:
|
|
943
965
|
raise EarlyReturnError
|
|
944
966
|
|
|
945
967
|
def run(self):
|
|
946
968
|
try:
|
|
947
|
-
with self.lock:
|
|
948
|
-
self.thread_id = threading.current_thread().ident
|
|
949
|
-
self.active_runnables[self.function] = self.thread_id
|
|
950
969
|
self.function(*self.args, **self.kwargs, thread=self)
|
|
951
970
|
except EarlyReturnError:
|
|
952
971
|
pass
|
|
953
|
-
except Exception:
|
|
954
|
-
raise
|
|
955
972
|
|
|
956
973
|
|
|
957
974
|
class NTimesBehavior(QObject):
|
|
@@ -1140,6 +1157,21 @@ class Figure(Figure):
|
|
|
1140
1157
|
|
|
1141
1158
|
|
|
1142
1159
|
class FigureCanvas(FigureCanvas):
|
|
1160
|
+
# Rendering and painting run in the GUI thread, while worker threads modify the
|
|
1161
|
+
# artists of the figure. Both have to hold the matplotlib_lock, otherwise the
|
|
1162
|
+
# renderer might traverse artists that are removed at the same time.
|
|
1163
|
+
def draw(self):
|
|
1164
|
+
with matplotlib_lock:
|
|
1165
|
+
super().draw()
|
|
1166
|
+
|
|
1167
|
+
def paintEvent(self, event):
|
|
1168
|
+
with matplotlib_lock:
|
|
1169
|
+
super().paintEvent(event)
|
|
1170
|
+
|
|
1171
|
+
def blit(self, *args, **kwargs):
|
|
1172
|
+
with matplotlib_lock:
|
|
1173
|
+
super().blit(*args, **kwargs)
|
|
1174
|
+
|
|
1143
1175
|
def __init__(self, *args, **kwargs):
|
|
1144
1176
|
super().__init__(*args, **kwargs)
|
|
1145
1177
|
self.wheelEvent = lambda event: event.ignore()
|
|
@@ -2639,6 +2671,7 @@ class LWPAx:
|
|
|
2639
2671
|
ax.set_xticks([])
|
|
2640
2672
|
|
|
2641
2673
|
@drawplot_decorator.d
|
|
2674
|
+
@lock_d(matplotlib_lock)
|
|
2642
2675
|
def update(self):
|
|
2643
2676
|
ax = self.ax
|
|
2644
2677
|
ax.set_xlim(self.xrange)
|
|
@@ -2753,7 +2786,7 @@ class LWPAx:
|
|
|
2753
2786
|
|
|
2754
2787
|
else:
|
|
2755
2788
|
segs = np.array(((xs, xs), (ys * 0, ys))).T
|
|
2756
|
-
colors = dataframe["color"].to_numpy()
|
|
2789
|
+
colors = dataframe["color"].to_numpy().copy()
|
|
2757
2790
|
|
|
2758
2791
|
mask = xs == self.ref_position
|
|
2759
2792
|
colors[mask] = config["color_ref"]
|
|
@@ -2923,12 +2956,13 @@ class LWPAx:
|
|
|
2923
2956
|
|
|
2924
2957
|
def fit_data(self, xmin, xmax):
|
|
2925
2958
|
# Delete artists highlighting previous fit
|
|
2926
|
-
|
|
2927
|
-
self.__class__.fit_vline
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
self.__class__.fit_curve
|
|
2931
|
-
|
|
2959
|
+
with matplotlib_lock:
|
|
2960
|
+
if self.__class__.fit_vline is not None:
|
|
2961
|
+
self.__class__.fit_vline.remove()
|
|
2962
|
+
self.__class__.fit_vline = None
|
|
2963
|
+
if self.__class__.fit_curve is not None:
|
|
2964
|
+
self.__class__.fit_curve.remove()
|
|
2965
|
+
self.__class__.fit_curve = None
|
|
2932
2966
|
|
|
2933
2967
|
# Fit the data
|
|
2934
2968
|
xmiddle, xuncert, fit_xs, fit_ys = self.fit_peak(xmin, xmax)
|
|
@@ -2936,12 +2970,13 @@ class LWPAx:
|
|
|
2936
2970
|
QApplication.clipboard().setText(str(xmiddle))
|
|
2937
2971
|
|
|
2938
2972
|
# Highlight fit in plot
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2973
|
+
with matplotlib_lock:
|
|
2974
|
+
self.__class__.fit_curve = self.ax.plot(
|
|
2975
|
+
fit_xs, fit_ys, color=config["color_fit"], alpha=0.7, linewidth=1
|
|
2976
|
+
)[0]
|
|
2977
|
+
self.__class__.fit_vline = self.ax.axvline(
|
|
2978
|
+
x=xmiddle, color=config["color_fit"], ls="--", alpha=1, linewidth=1
|
|
2979
|
+
)
|
|
2945
2980
|
|
|
2946
2981
|
# Create assignment object
|
|
2947
2982
|
new_assignment = {
|
|
@@ -2990,8 +3025,8 @@ class LWPAx:
|
|
|
2990
3025
|
xmiddle, xuncert = fit_results["xmiddle"], fit_results["xuncert"]
|
|
2991
3026
|
fit_xs, fit_ys = fit_results["fit_xs"], fit_results["fit_ys"]
|
|
2992
3027
|
except Exception as E:
|
|
2993
|
-
self.
|
|
2994
|
-
self.
|
|
3028
|
+
self.fit_curve = None
|
|
3029
|
+
self.fit_line = None
|
|
2995
3030
|
notify_error.emit(
|
|
2996
3031
|
f"The fitting failed with the following error message : {str(E)}"
|
|
2997
3032
|
)
|
|
@@ -3324,7 +3359,7 @@ class LWPWidget(QGroupBox):
|
|
|
3324
3359
|
|
|
3325
3360
|
for i_col in range(n_widgets):
|
|
3326
3361
|
thread.earlyreturn()
|
|
3327
|
-
if i_col
|
|
3362
|
+
if i_col >= n_cols:
|
|
3328
3363
|
continue
|
|
3329
3364
|
|
|
3330
3365
|
refwidget = tab_widget.widget(i_col)
|
|
@@ -3390,24 +3425,28 @@ class LWPWidget(QGroupBox):
|
|
|
3390
3425
|
thread.earlyreturn()
|
|
3391
3426
|
|
|
3392
3427
|
# Set the correct values to the LWPAxes
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3428
|
+
# The lock makes sure that the axes are not replaced while they are updated
|
|
3429
|
+
with matplotlib_lock:
|
|
3430
|
+
if self.lwpaxes.shape != (n_rows, n_cols):
|
|
3431
|
+
notify_error.emit(
|
|
3432
|
+
"Shape of LWPAxes is out of sync with requested values."
|
|
3433
|
+
)
|
|
3434
|
+
return
|
|
3396
3435
|
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3436
|
+
for i_row in range(n_rows):
|
|
3437
|
+
for i_col in range(n_cols):
|
|
3438
|
+
ax = self.lwpaxes[i_row, i_col]
|
|
3439
|
+
ax.ref_position = positions[i_row, i_col]
|
|
3440
|
+
ax.xrange = (xmins[i_row, i_col], xmaxs[i_row, i_col])
|
|
3441
|
+
ax.qns = qns[i_row, i_col]
|
|
3442
|
+
ax.indices = {
|
|
3443
|
+
label: (
|
|
3444
|
+
min_indices[label][i_row, i_col],
|
|
3445
|
+
max_indices[label][i_row, i_col],
|
|
3446
|
+
)
|
|
3447
|
+
for label in ("exp", "cat", "lin")
|
|
3448
|
+
}
|
|
3449
|
+
ax.update()
|
|
3411
3450
|
|
|
3412
3451
|
def get_current_ax(self):
|
|
3413
3452
|
shape = self.lwpaxes.shape
|
|
@@ -7748,28 +7787,30 @@ class ResidualsWindow(EQDockWidget):
|
|
|
7748
7787
|
colors = df["color"].to_numpy()
|
|
7749
7788
|
tuples = list(zip(xs, ys))
|
|
7750
7789
|
tuples = tuples if len(tuples) != 0 else [[None, None]]
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
xmin
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
ymin
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7790
|
+
with matplotlib_lock:
|
|
7791
|
+
self.points.set_offsets(tuples)
|
|
7792
|
+
self.points.set_color(colors)
|
|
7793
|
+
if len(xs) and config["residuals_autoscale"]:
|
|
7794
|
+
xmin, xmax = np.min(xs), np.max(xs)
|
|
7795
|
+
if xmin == xmax:
|
|
7796
|
+
xmin -= 1
|
|
7797
|
+
xmax += 1
|
|
7798
|
+
self.ax.set_xlim(
|
|
7799
|
+
[
|
|
7800
|
+
xmin - 0.02 * (xmax - xmin),
|
|
7801
|
+
xmax + 0.02 * (xmax - xmin),
|
|
7802
|
+
]
|
|
7803
|
+
)
|
|
7804
|
+
ymin, ymax = np.min(ys), np.max(ys)
|
|
7805
|
+
if ymin == ymax:
|
|
7806
|
+
ymin -= 1
|
|
7807
|
+
ymax += 1
|
|
7808
|
+
y_range = [ymin, ymax]
|
|
7809
|
+
self.ax.set_ylim(
|
|
7810
|
+
y_range[0] - config["plot_ymargin"] * (y_range[1] - y_range[0]),
|
|
7811
|
+
y_range[1] + config["plot_ymargin"] * (y_range[1] - y_range[0]),
|
|
7812
|
+
)
|
|
7813
|
+
|
|
7773
7814
|
notify_info.emit("<br/>".join(message))
|
|
7774
7815
|
except Exception:
|
|
7775
7816
|
notify_warning.emit("There was an error in your Residuals window input")
|
|
@@ -7905,7 +7946,6 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
7905
7946
|
[], [], color=config["blendedlines_color_total"]
|
|
7906
7947
|
)[0]
|
|
7907
7948
|
self.plot_parts = []
|
|
7908
|
-
self.plot_parts_lock = threading.Lock()
|
|
7909
7949
|
self.update_plot_requested.connect(super().update_plot)
|
|
7910
7950
|
|
|
7911
7951
|
def update_plot(self):
|
|
@@ -8046,7 +8086,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8046
8086
|
x, profile, derivative, 0, *args, fixedwidth=fixedwidth
|
|
8047
8087
|
)
|
|
8048
8088
|
|
|
8049
|
-
with
|
|
8089
|
+
with matplotlib_lock:
|
|
8050
8090
|
for part in plot_widget.plot_parts:
|
|
8051
8091
|
part.remove()
|
|
8052
8092
|
plot_widget.plot_parts = []
|
|
@@ -8146,8 +8186,9 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8146
8186
|
|
|
8147
8187
|
ax = plot_widget.ax
|
|
8148
8188
|
|
|
8149
|
-
|
|
8150
|
-
|
|
8189
|
+
with matplotlib_lock:
|
|
8190
|
+
plot_widget.fit_line.set_data(res_xs, res_ys)
|
|
8191
|
+
plot_widget.fit_line.set_color(config["blendedlines_color_total"])
|
|
8151
8192
|
|
|
8152
8193
|
opt_param = []
|
|
8153
8194
|
err_param = []
|
|
@@ -8171,7 +8212,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8171
8212
|
tmp_ys = fitfunction_no_baseline(res_xs, *tmp_params)
|
|
8172
8213
|
tmp_ys += exp_mean
|
|
8173
8214
|
|
|
8174
|
-
with
|
|
8215
|
+
with matplotlib_lock:
|
|
8175
8216
|
plot_widget.plot_parts.append(
|
|
8176
8217
|
ax.plot(
|
|
8177
8218
|
res_xs,
|
|
@@ -8184,7 +8225,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8184
8225
|
opt_param.append(tmp_params)
|
|
8185
8226
|
err_param.append(tmp_errors)
|
|
8186
8227
|
|
|
8187
|
-
with
|
|
8228
|
+
with matplotlib_lock:
|
|
8188
8229
|
plot_widget.plot_parts.append(
|
|
8189
8230
|
ax.scatter(
|
|
8190
8231
|
[x[0] for x in opt_param],
|
|
@@ -8195,7 +8236,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8195
8236
|
|
|
8196
8237
|
if polynomrank > 0 and config["blendedlines_showbaseline"]:
|
|
8197
8238
|
baseline_args = popt[-polynomrank:]
|
|
8198
|
-
with
|
|
8239
|
+
with matplotlib_lock:
|
|
8199
8240
|
plot_widget.plot_parts.append(
|
|
8200
8241
|
ax.plot(
|
|
8201
8242
|
res_xs,
|
|
@@ -8234,7 +8275,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8234
8275
|
|
|
8235
8276
|
peaks = self.peaks.copy()
|
|
8236
8277
|
fixedwidth = config["blendedlines_fixedwidth"]
|
|
8237
|
-
with
|
|
8278
|
+
with matplotlib_lock:
|
|
8238
8279
|
for part in plot_widget.plot_parts:
|
|
8239
8280
|
part.remove()
|
|
8240
8281
|
plot_widget.plot_parts = []
|
|
@@ -8407,7 +8448,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8407
8448
|
|
|
8408
8449
|
idx += delta_idx
|
|
8409
8450
|
|
|
8410
|
-
with
|
|
8451
|
+
with matplotlib_lock:
|
|
8411
8452
|
plot_widget.plot_parts.append(
|
|
8412
8453
|
ax.scatter(
|
|
8413
8454
|
[x[0] for x in opt_param],
|
|
@@ -8436,8 +8477,9 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8436
8477
|
|
|
8437
8478
|
thread.earlyreturn()
|
|
8438
8479
|
|
|
8439
|
-
|
|
8440
|
-
|
|
8480
|
+
with matplotlib_lock:
|
|
8481
|
+
plot_widget.fit_line.set_data(res_xs, res_ys)
|
|
8482
|
+
plot_widget.fit_line.set_color(config["blendedlines_color_total"])
|
|
8441
8483
|
|
|
8442
8484
|
self.fill_table_requested.emit()
|
|
8443
8485
|
self.set_indicator_text.emit("Ready")
|
|
@@ -9167,28 +9209,29 @@ class EnergyLevelsWindow(EQDockWidget):
|
|
|
9167
9209
|
colors = df["color"].to_numpy()
|
|
9168
9210
|
tuples = list(zip(xs, ys))
|
|
9169
9211
|
tuples = tuples if len(tuples) != 0 else [[None, None]]
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
xmin
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
ymin
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9212
|
+
with matplotlib_lock:
|
|
9213
|
+
self.points.set_offsets(tuples)
|
|
9214
|
+
self.points.set_color(colors)
|
|
9215
|
+
if len(xs) and config["energylevels_autoscale"]:
|
|
9216
|
+
xmin, xmax = np.min(xs), np.max(xs)
|
|
9217
|
+
if xmin == xmax:
|
|
9218
|
+
xmin -= 1
|
|
9219
|
+
xmax += 1
|
|
9220
|
+
self.ax.set_xlim(
|
|
9221
|
+
[
|
|
9222
|
+
xmin - 0.02 * (xmax - xmin),
|
|
9223
|
+
xmax + 0.02 * (xmax - xmin),
|
|
9224
|
+
]
|
|
9225
|
+
)
|
|
9226
|
+
ymin, ymax = np.min(ys), np.max(ys)
|
|
9227
|
+
if ymin == ymax:
|
|
9228
|
+
ymin -= 1
|
|
9229
|
+
ymax += 1
|
|
9230
|
+
y_range = [ymin, ymax]
|
|
9231
|
+
self.ax.set_ylim(
|
|
9232
|
+
y_range[0] - config["plot_ymargin"] * (y_range[1] - y_range[0]),
|
|
9233
|
+
y_range[1] + config["plot_ymargin"] * (y_range[1] - y_range[0]),
|
|
9234
|
+
)
|
|
9192
9235
|
except Exception:
|
|
9193
9236
|
notify_error.emit("There was an error in your Energy Levels window inputs")
|
|
9194
9237
|
raise
|
|
@@ -9267,6 +9310,7 @@ class PeakfinderWindow(EQDockWidget):
|
|
|
9267
9310
|
[], [], color=config["peakfinder_peakcolor"], marker="*"
|
|
9268
9311
|
)
|
|
9269
9312
|
|
|
9313
|
+
@lock_d(matplotlib_lock)
|
|
9270
9314
|
def update_plot(self):
|
|
9271
9315
|
xmin, xmax = self.xrange
|
|
9272
9316
|
tuples = list(filter(lambda x: xmin < x[0] < xmax, self.parent.peaks))
|
|
@@ -9291,7 +9335,7 @@ class PeakfinderWindow(EQDockWidget):
|
|
|
9291
9335
|
layout.addLayout(tmp_layout)
|
|
9292
9336
|
|
|
9293
9337
|
self.run_button = QQ(
|
|
9294
|
-
QPushButton, text="Run Peakfinder", change=lambda: self.
|
|
9338
|
+
QPushButton, text="Run Peakfinder", change=lambda: self.on_run_clicked()
|
|
9295
9339
|
)
|
|
9296
9340
|
tmp_layout.addWidget(self.run_button)
|
|
9297
9341
|
tmp_layout.addWidget(
|
|
@@ -9415,11 +9459,15 @@ class PeakfinderWindow(EQDockWidget):
|
|
|
9415
9459
|
config["peakfinder_kwargs"] = kwargs
|
|
9416
9460
|
return kwargs.copy()
|
|
9417
9461
|
|
|
9462
|
+
def on_run_clicked(self):
|
|
9463
|
+
self.run_button.setEnabled(False)
|
|
9464
|
+
kwargs = self.get_kwargs()
|
|
9465
|
+
self.find_peaks(kwargs)
|
|
9466
|
+
|
|
9418
9467
|
@QThread.threaded_d
|
|
9419
9468
|
@peak_locked
|
|
9420
|
-
def find_peaks(self, thread=None):
|
|
9469
|
+
def find_peaks(self, kwargs, thread=None):
|
|
9421
9470
|
self.peakfinding_started.emit()
|
|
9422
|
-
kwargs = self.get_kwargs()
|
|
9423
9471
|
|
|
9424
9472
|
if "frequency" in kwargs:
|
|
9425
9473
|
val = kwargs["frequency"]
|
|
@@ -10691,6 +10739,7 @@ class ASAPAx(LWPAx):
|
|
|
10691
10739
|
self.exp_coll.set(segments=segs, color=color)
|
|
10692
10740
|
|
|
10693
10741
|
@drawplot_decorator.d
|
|
10742
|
+
@lock_d(matplotlib_lock)
|
|
10694
10743
|
def update(self):
|
|
10695
10744
|
ax = self.ax
|
|
10696
10745
|
|
|
@@ -10860,12 +10909,13 @@ class ASAPAx(LWPAx):
|
|
|
10860
10909
|
return
|
|
10861
10910
|
|
|
10862
10911
|
# Delete artists highlighting previous fit
|
|
10863
|
-
|
|
10864
|
-
self.__class__.fit_vline
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
self.__class__.fit_curve
|
|
10868
|
-
|
|
10912
|
+
with matplotlib_lock:
|
|
10913
|
+
if self.__class__.fit_vline is not None:
|
|
10914
|
+
self.__class__.fit_vline.remove()
|
|
10915
|
+
self.__class__.fit_vline = None
|
|
10916
|
+
if self.__class__.fit_curve is not None:
|
|
10917
|
+
self.__class__.fit_curve.remove()
|
|
10918
|
+
self.__class__.fit_curve = None
|
|
10869
10919
|
|
|
10870
10920
|
if onclick:
|
|
10871
10921
|
xmiddle = xmin
|
|
@@ -10874,14 +10924,16 @@ class ASAPAx(LWPAx):
|
|
|
10874
10924
|
else:
|
|
10875
10925
|
# Fit the data
|
|
10876
10926
|
xmiddle, xuncert, fit_xs, fit_ys = self.fit_peak(xmin, xmax)
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10927
|
+
with matplotlib_lock:
|
|
10928
|
+
self.__class__.fit_curve = self.ax.plot(
|
|
10929
|
+
fit_xs, fit_ys, color=config["color_fit"], alpha=0.7, linewidth=1
|
|
10930
|
+
)[0]
|
|
10880
10931
|
|
|
10881
10932
|
# Highlight fit in plot
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
10933
|
+
with matplotlib_lock:
|
|
10934
|
+
self.__class__.fit_vline = self.ax.axvline(
|
|
10935
|
+
x=xmiddle, color=config["color_fit"], ls="--", alpha=1, linewidth=1
|
|
10936
|
+
)
|
|
10885
10937
|
|
|
10886
10938
|
# Emit signal for detail viewer
|
|
10887
10939
|
self.x_fit = xmiddle
|
|
@@ -10978,8 +11030,8 @@ class ASAPAx(LWPAx):
|
|
|
10978
11030
|
fit_xs, fit_ys = fit_results["fit_xs"], fit_results["fit_ys"]
|
|
10979
11031
|
|
|
10980
11032
|
except Exception as E:
|
|
10981
|
-
self.
|
|
10982
|
-
self.
|
|
11033
|
+
self.fit_curve = None
|
|
11034
|
+
self.fit_line = None
|
|
10983
11035
|
notify_error.emit(
|
|
10984
11036
|
f"The fitting failed with the following error message : {str(E)}"
|
|
10985
11037
|
)
|
|
@@ -11333,12 +11385,13 @@ class ASAPWidget(LWPWidget):
|
|
|
11333
11385
|
@status_d
|
|
11334
11386
|
@drawplot_decorator.d
|
|
11335
11387
|
def calc_correlation_plots(self, thread=None):
|
|
11336
|
-
|
|
11337
|
-
self._ax_class.fit_vline
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
self._ax_class.fit_curve
|
|
11341
|
-
|
|
11388
|
+
with matplotlib_lock:
|
|
11389
|
+
if self._ax_class.fit_vline is not None:
|
|
11390
|
+
self._ax_class.fit_vline.remove()
|
|
11391
|
+
self._ax_class.fit_vline = None
|
|
11392
|
+
if self._ax_class.fit_curve is not None:
|
|
11393
|
+
self._ax_class.fit_curve.remove()
|
|
11394
|
+
self._ax_class.fit_curve = None
|
|
11342
11395
|
|
|
11343
11396
|
if not hasattr(ASAPSettingsWindow, "instance"):
|
|
11344
11397
|
return
|
|
@@ -11368,7 +11421,7 @@ class ASAPWidget(LWPWidget):
|
|
|
11368
11421
|
|
|
11369
11422
|
for i_col in range(n_widgets):
|
|
11370
11423
|
thread.earlyreturn()
|
|
11371
|
-
if i_col
|
|
11424
|
+
if i_col >= n_cols:
|
|
11372
11425
|
continue
|
|
11373
11426
|
|
|
11374
11427
|
refwidget = tab_widget.widget(i_col)
|
|
@@ -11956,12 +12009,15 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
11956
12009
|
self.noq = config["series_qns"]
|
|
11957
12010
|
assignments = []
|
|
11958
12011
|
|
|
11959
|
-
|
|
11960
|
-
self.fig.
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
self.
|
|
11964
|
-
|
|
12012
|
+
with matplotlib_lock:
|
|
12013
|
+
for ax in self.fig.get_axes():
|
|
12014
|
+
self.fig.delaxes(ax)
|
|
12015
|
+
self.fit_vline = None
|
|
12016
|
+
self.fit_curve = None
|
|
12017
|
+
self.ax = self.fig.subplots()
|
|
12018
|
+
self.span_selector = matplotlib.widgets.SpanSelector(
|
|
12019
|
+
self.ax, self.on_range, "horizontal", useblit=True
|
|
12020
|
+
)
|
|
11965
12021
|
|
|
11966
12022
|
width = config["asap_squaredwidth"] or config["plot_width"]
|
|
11967
12023
|
stepsize = config["asap_squaredstepsize"] or config["asap_stepsize"]
|
|
@@ -12029,9 +12085,10 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12029
12085
|
tot_ys /= ys_max
|
|
12030
12086
|
log_y_max += np.log10(ys_max)
|
|
12031
12087
|
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12088
|
+
with matplotlib_lock:
|
|
12089
|
+
self.ax.plot(rel_xs, tot_ys, color=config["color_exp"])
|
|
12090
|
+
if config["asap_squaredplotlog"]:
|
|
12091
|
+
self.ax.set_yscale("log")
|
|
12035
12092
|
|
|
12036
12093
|
self.data = np.vstack((rel_xs, tot_ys)).T
|
|
12037
12094
|
self.drawplot.emit()
|
|
@@ -12102,12 +12159,13 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12102
12159
|
@status_d
|
|
12103
12160
|
def fit_data(self, xmin, xmax, thread=None):
|
|
12104
12161
|
# Delete artists highlighting previous fit
|
|
12105
|
-
|
|
12106
|
-
self.fit_vline
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
self.fit_curve
|
|
12110
|
-
|
|
12162
|
+
with matplotlib_lock:
|
|
12163
|
+
if self.fit_vline is not None:
|
|
12164
|
+
self.fit_vline.remove()
|
|
12165
|
+
self.fit_vline = None
|
|
12166
|
+
if self.fit_curve is not None:
|
|
12167
|
+
self.fit_curve.remove()
|
|
12168
|
+
self.fit_curve = None
|
|
12111
12169
|
|
|
12112
12170
|
if self.data is None:
|
|
12113
12171
|
notify_warning.emit("No cross-correlation data available.")
|
|
@@ -12140,8 +12198,8 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12140
12198
|
f"The following parameters were determined: \n{popt=}\n{perr=}"
|
|
12141
12199
|
)
|
|
12142
12200
|
except Exception as E:
|
|
12143
|
-
self.
|
|
12144
|
-
self.
|
|
12201
|
+
self.fit_curve = None
|
|
12202
|
+
self.fit_vline = None
|
|
12145
12203
|
notify_error.emit(
|
|
12146
12204
|
f"The fitting failed with the following error message : {str(E)}"
|
|
12147
12205
|
)
|
|
@@ -12151,12 +12209,13 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12151
12209
|
self.clipboard_requested.emit(str(xmiddle))
|
|
12152
12210
|
|
|
12153
12211
|
# Highlight fit in plot
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12212
|
+
with matplotlib_lock:
|
|
12213
|
+
self.fit_curve = self.ax.plot(
|
|
12214
|
+
fit_xs, fit_ys, color=config["color_fit"], alpha=0.7, linewidth=1
|
|
12215
|
+
)[0]
|
|
12216
|
+
self.fit_vline = self.ax.axvline(
|
|
12217
|
+
x=xmiddle, color=config["color_fit"], ls="--", alpha=1, linewidth=1
|
|
12218
|
+
)
|
|
12160
12219
|
self.xmiddle = xmiddle
|
|
12161
12220
|
self.xuncert = xuncert
|
|
12162
12221
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llwp
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: LLWP is a fast, efficient and easy solution for exploring and assigning spectra - relying on Loomis-Wood plots.
|
|
5
5
|
Author-email: Luis Bonah <bonah@ph1.uni-koeln.de>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|