llwp 2.1.0__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.0/llwp.egg-info → llwp-2.1.2}/PKG-INFO +1 -1
- {llwp-2.1.0 → llwp-2.1.2}/llwp/LLWP.py +352 -207
- {llwp-2.1.0 → llwp-2.1.2/llwp.egg-info}/PKG-INFO +1 -1
- {llwp-2.1.0 → llwp-2.1.2}/pyproject.toml +4 -1
- {llwp-2.1.0 → llwp-2.1.2}/LICENSE +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/MANIFEST.in +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/README.md +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp/ASAP.svg +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp/ASAPMac.png +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp/LLWP.svg +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp/LLWPMac.png +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp/__init__.py +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp/__main__.py +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp.egg-info/SOURCES.txt +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp.egg-info/dependency_links.txt +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp.egg-info/entry_points.txt +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp.egg-info/requires.txt +0 -0
- {llwp-2.1.0 → llwp-2.1.2}/llwp.egg-info/top_level.txt +0 -0
- {llwp-2.1.0 → 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):
|
|
@@ -357,6 +361,7 @@ class Config(dict):
|
|
|
357
361
|
"flag_showseriesarrows": (True, bool),
|
|
358
362
|
"flag_keeponlylastassignment": (False, bool),
|
|
359
363
|
"flag_autoreloadfiles": (True, bool),
|
|
364
|
+
"flag_reloadlinafterdelete": (True, bool),
|
|
360
365
|
"flag_lincustomfreqformat": ("", str),
|
|
361
366
|
"flag_allowdocking": (True, bool),
|
|
362
367
|
"flag_docksalwaysontop": (True, bool),
|
|
@@ -682,7 +687,11 @@ class PlotWidget(QWidget):
|
|
|
682
687
|
np.zeros(shape=(0, 2, 2)), colors=config["color_cat"], capstyle="round"
|
|
683
688
|
)
|
|
684
689
|
self.lin_coll = self.ax.scatter(
|
|
685
|
-
[],
|
|
690
|
+
[],
|
|
691
|
+
[],
|
|
692
|
+
color=config["color_ref"],
|
|
693
|
+
marker=config["plot_linmarker"],
|
|
694
|
+
zorder=100,
|
|
686
695
|
)
|
|
687
696
|
self.ax.add_collection(self.exp_coll, autolim=False)
|
|
688
697
|
self.ax.add_collection(self.cat_coll, autolim=False)
|
|
@@ -702,6 +711,7 @@ class PlotWidget(QWidget):
|
|
|
702
711
|
|
|
703
712
|
self.update_plot()
|
|
704
713
|
|
|
714
|
+
@lock_d(matplotlib_lock)
|
|
705
715
|
def update_plot(self):
|
|
706
716
|
scaling = config["plot_yscale"]
|
|
707
717
|
|
|
@@ -877,18 +887,15 @@ class PlotWidget(QWidget):
|
|
|
877
887
|
"s": lambda: self.move_plot("out"),
|
|
878
888
|
"a": lambda: self.move_plot("left"),
|
|
879
889
|
"d": lambda: self.move_plot("right"),
|
|
880
|
-
|
|
881
890
|
"Shift+w": lambda: self.move_plot("sin"),
|
|
882
891
|
"Shift+s": lambda: self.move_plot("sout"),
|
|
883
892
|
"Shift+a": lambda: self.move_plot("sleft"),
|
|
884
893
|
"Shift+d": lambda: self.move_plot("sright"),
|
|
885
|
-
|
|
886
894
|
# We cannot use shortcuts that are also used in the menu bar here, as these are global shortcuts on MacOS, as the MenuBar is always active
|
|
887
895
|
"Alt+w": lambda: self.change_index(0, +1),
|
|
888
896
|
"Alt+s": lambda: self.change_index(0, -1),
|
|
889
897
|
"Alt+a": lambda: self.change_index(1, -1),
|
|
890
898
|
"Alt+d": lambda: self.change_index(1, +1),
|
|
891
|
-
|
|
892
899
|
"Alt+Up": lambda: self.change_index(0, +1),
|
|
893
900
|
"Alt+Down": lambda: self.change_index(0, -1),
|
|
894
901
|
"Alt+Left": lambda: self.change_index(1, -1),
|
|
@@ -910,16 +917,17 @@ class PlotWidget(QWidget):
|
|
|
910
917
|
|
|
911
918
|
|
|
912
919
|
drawplot_decorator = DynamicDecorator()
|
|
913
|
-
matplotlib_lock = threading.RLock()
|
|
914
920
|
|
|
915
921
|
|
|
916
922
|
##
|
|
917
923
|
## Customized PyQt classes
|
|
918
924
|
##
|
|
919
925
|
class QThread(QThread):
|
|
926
|
+
# Maps each function to the sequence number of its most recently created thread
|
|
920
927
|
active_runnables = {}
|
|
921
928
|
lock = threading.RLock()
|
|
922
929
|
threads = set()
|
|
930
|
+
_sequence = AtomicCounter()
|
|
923
931
|
|
|
924
932
|
@classmethod
|
|
925
933
|
def threaded_d(cls, func):
|
|
@@ -930,26 +938,37 @@ class QThread(QThread):
|
|
|
930
938
|
|
|
931
939
|
return wrapper
|
|
932
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
|
+
|
|
933
951
|
def __init__(self, function, *args, **kwargs):
|
|
934
952
|
super().__init__()
|
|
935
953
|
self.function, self.args, self.kwargs = function, args, kwargs
|
|
936
|
-
self.
|
|
937
|
-
|
|
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)
|
|
938
962
|
|
|
939
963
|
def earlyreturn(self):
|
|
940
|
-
if self.active_runnables
|
|
964
|
+
if self.active_runnables.get(self.function) != self.sequence:
|
|
941
965
|
raise EarlyReturnError
|
|
942
966
|
|
|
943
967
|
def run(self):
|
|
944
968
|
try:
|
|
945
|
-
with self.lock:
|
|
946
|
-
self.thread_id = threading.current_thread().ident
|
|
947
|
-
self.active_runnables[self.function] = self.thread_id
|
|
948
969
|
self.function(*self.args, **self.kwargs, thread=self)
|
|
949
970
|
except EarlyReturnError:
|
|
950
971
|
pass
|
|
951
|
-
except Exception:
|
|
952
|
-
raise
|
|
953
972
|
|
|
954
973
|
|
|
955
974
|
class NTimesBehavior(QObject):
|
|
@@ -968,7 +987,7 @@ class NTimesBehavior(QObject):
|
|
|
968
987
|
event.button() == Qt.MouseButton.LeftButton
|
|
969
988
|
and event.modifiers() & Qt.KeyboardModifier.AltModifier
|
|
970
989
|
):
|
|
971
|
-
i, ok = QInputDialog.getInt(self.widget, "
|
|
990
|
+
i, ok = QInputDialog.getInt(self.widget, "Repeat how many times", "N: ")
|
|
972
991
|
if not ok:
|
|
973
992
|
return True
|
|
974
993
|
|
|
@@ -1138,6 +1157,21 @@ class Figure(Figure):
|
|
|
1138
1157
|
|
|
1139
1158
|
|
|
1140
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
|
+
|
|
1141
1175
|
def __init__(self, *args, **kwargs):
|
|
1142
1176
|
super().__init__(*args, **kwargs)
|
|
1143
1177
|
self.wheelEvent = lambda event: event.ignore()
|
|
@@ -2342,6 +2376,47 @@ class NewAssignments(LinFile):
|
|
|
2342
2376
|
file.write(pyckett.df_to_lin(df, custom_freq_format=custom_freq_format))
|
|
2343
2377
|
|
|
2344
2378
|
|
|
2379
|
+
def delete_assignments(assignments):
|
|
2380
|
+
new_assignments = None
|
|
2381
|
+
for lin_fname in assignments["filename"].unique():
|
|
2382
|
+
to_delete = assignments[assignments["filename"] == lin_fname]
|
|
2383
|
+
to_delete = to_delete[list(pyckett.lin_dtypes.keys())]
|
|
2384
|
+
|
|
2385
|
+
if lin_fname == "__newassignments__":
|
|
2386
|
+
new_assignments = NewAssignments.get_instance()
|
|
2387
|
+
lin = new_assignments.get_new_assignments_df().copy()
|
|
2388
|
+
else:
|
|
2389
|
+
lin = pyckett.lin_to_df(lin_fname, sort=False)
|
|
2390
|
+
|
|
2391
|
+
lin = pd.merge(lin, to_delete, how="left", indicator=True)
|
|
2392
|
+
lin = lin[lin["_merge"] == "left_only"]
|
|
2393
|
+
lin = lin.drop("_merge", axis="columns").reset_index(drop=True)
|
|
2394
|
+
|
|
2395
|
+
if lin_fname == "__newassignments__":
|
|
2396
|
+
new_assignments.new_assignments_df = lin
|
|
2397
|
+
new_assignments.load_file()
|
|
2398
|
+
|
|
2399
|
+
new_assignments_window = NewAssignmentsWindow.instance
|
|
2400
|
+
new_assignments_window.model.update()
|
|
2401
|
+
else:
|
|
2402
|
+
# Saving the *.lin file after removing the transitions
|
|
2403
|
+
# can overwrite custom formats for the x and error values
|
|
2404
|
+
with open(lin_fname, "w+") as file:
|
|
2405
|
+
custom_freq_format = config["flag_lincustomfreqformat"]
|
|
2406
|
+
file.write(
|
|
2407
|
+
pyckett.df_to_lin(lin, custom_freq_format=custom_freq_format)
|
|
2408
|
+
)
|
|
2409
|
+
|
|
2410
|
+
# Without the file watcher the file would not be updated otherwise
|
|
2411
|
+
if (
|
|
2412
|
+
config["flag_reloadlinafterdelete"]
|
|
2413
|
+
and not config["flag_autoreloadfiles"]
|
|
2414
|
+
):
|
|
2415
|
+
lin_file = LinFile.ids.get(lin_fname)
|
|
2416
|
+
if lin_file:
|
|
2417
|
+
lin_file.load_file()
|
|
2418
|
+
|
|
2419
|
+
|
|
2345
2420
|
class FileAdditionalSettingsDialog(QDialog):
|
|
2346
2421
|
open_dialogs = {}
|
|
2347
2422
|
|
|
@@ -2560,6 +2635,7 @@ class LWPAx:
|
|
|
2560
2635
|
self.indices = None
|
|
2561
2636
|
self.annotation = None
|
|
2562
2637
|
self.qns = None
|
|
2638
|
+
self.lin_df = None
|
|
2563
2639
|
|
|
2564
2640
|
with matplotlib_lock:
|
|
2565
2641
|
self.span = matplotlib.widgets.SpanSelector(
|
|
@@ -2577,7 +2653,11 @@ class LWPAx:
|
|
|
2577
2653
|
np.zeros(shape=(0, 2, 2)), colors=config["color_cat"], capstyle="round"
|
|
2578
2654
|
)
|
|
2579
2655
|
self.lin_coll = ax.scatter(
|
|
2580
|
-
[],
|
|
2656
|
+
[],
|
|
2657
|
+
[],
|
|
2658
|
+
color=config["color_ref"],
|
|
2659
|
+
marker=config["plot_linmarker"],
|
|
2660
|
+
zorder=100,
|
|
2581
2661
|
)
|
|
2582
2662
|
|
|
2583
2663
|
with matplotlib_lock:
|
|
@@ -2591,6 +2671,7 @@ class LWPAx:
|
|
|
2591
2671
|
ax.set_xticks([])
|
|
2592
2672
|
|
|
2593
2673
|
@drawplot_decorator.d
|
|
2674
|
+
@lock_d(matplotlib_lock)
|
|
2594
2675
|
def update(self):
|
|
2595
2676
|
ax = self.ax
|
|
2596
2677
|
ax.set_xlim(self.xrange)
|
|
@@ -2705,7 +2786,7 @@ class LWPAx:
|
|
|
2705
2786
|
|
|
2706
2787
|
else:
|
|
2707
2788
|
segs = np.array(((xs, xs), (ys * 0, ys))).T
|
|
2708
|
-
colors = dataframe["color"].to_numpy()
|
|
2789
|
+
colors = dataframe["color"].to_numpy().copy()
|
|
2709
2790
|
|
|
2710
2791
|
mask = xs == self.ref_position
|
|
2711
2792
|
colors[mask] = config["color_ref"]
|
|
@@ -2713,6 +2794,7 @@ class LWPAx:
|
|
|
2713
2794
|
self.cat_coll.set(segments=segs, colors=colors)
|
|
2714
2795
|
|
|
2715
2796
|
elif datatype == "lin":
|
|
2797
|
+
self.lin_df = dataframe
|
|
2716
2798
|
tuples = list(zip(xs, ys))
|
|
2717
2799
|
tuples = tuples if len(tuples) != 0 else [[None, None]]
|
|
2718
2800
|
colors = dataframe["color"].to_numpy()
|
|
@@ -2874,12 +2956,13 @@ class LWPAx:
|
|
|
2874
2956
|
|
|
2875
2957
|
def fit_data(self, xmin, xmax):
|
|
2876
2958
|
# Delete artists highlighting previous fit
|
|
2877
|
-
|
|
2878
|
-
self.__class__.fit_vline
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
self.__class__.fit_curve
|
|
2882
|
-
|
|
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
|
|
2883
2966
|
|
|
2884
2967
|
# Fit the data
|
|
2885
2968
|
xmiddle, xuncert, fit_xs, fit_ys = self.fit_peak(xmin, xmax)
|
|
@@ -2887,12 +2970,13 @@ class LWPAx:
|
|
|
2887
2970
|
QApplication.clipboard().setText(str(xmiddle))
|
|
2888
2971
|
|
|
2889
2972
|
# Highlight fit in plot
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
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
|
+
)
|
|
2896
2980
|
|
|
2897
2981
|
# Create assignment object
|
|
2898
2982
|
new_assignment = {
|
|
@@ -2941,8 +3025,8 @@ class LWPAx:
|
|
|
2941
3025
|
xmiddle, xuncert = fit_results["xmiddle"], fit_results["xuncert"]
|
|
2942
3026
|
fit_xs, fit_ys = fit_results["fit_xs"], fit_results["fit_ys"]
|
|
2943
3027
|
except Exception as E:
|
|
2944
|
-
self.
|
|
2945
|
-
self.
|
|
3028
|
+
self.fit_curve = None
|
|
3029
|
+
self.fit_line = None
|
|
2946
3030
|
notify_error.emit(
|
|
2947
3031
|
f"The fitting failed with the following error message : {str(E)}"
|
|
2948
3032
|
)
|
|
@@ -3275,7 +3359,7 @@ class LWPWidget(QGroupBox):
|
|
|
3275
3359
|
|
|
3276
3360
|
for i_col in range(n_widgets):
|
|
3277
3361
|
thread.earlyreturn()
|
|
3278
|
-
if i_col
|
|
3362
|
+
if i_col >= n_cols:
|
|
3279
3363
|
continue
|
|
3280
3364
|
|
|
3281
3365
|
refwidget = tab_widget.widget(i_col)
|
|
@@ -3341,24 +3425,28 @@ class LWPWidget(QGroupBox):
|
|
|
3341
3425
|
thread.earlyreturn()
|
|
3342
3426
|
|
|
3343
3427
|
# Set the correct values to the LWPAxes
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
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
|
|
3347
3435
|
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
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()
|
|
3362
3450
|
|
|
3363
3451
|
def get_current_ax(self):
|
|
3364
3452
|
shape = self.lwpaxes.shape
|
|
@@ -3368,6 +3456,27 @@ class LWPWidget(QGroupBox):
|
|
|
3368
3456
|
else:
|
|
3369
3457
|
return self.lwpaxes[0, 0]
|
|
3370
3458
|
|
|
3459
|
+
def get_assignments_at(self, lwpax, event):
|
|
3460
|
+
"""Return the assignments whose markers are under the cursor."""
|
|
3461
|
+
lin_df = lwpax.lin_df
|
|
3462
|
+
if lin_df is None or not len(lin_df):
|
|
3463
|
+
return pd.DataFrame()
|
|
3464
|
+
|
|
3465
|
+
ratio = self.plotcanvas.device_pixel_ratio
|
|
3466
|
+
height = self.plotcanvas.get_width_height(physical=True)[1]
|
|
3467
|
+
mouseevent = matplotlib.backend_bases.MouseEvent(
|
|
3468
|
+
"button_press_event",
|
|
3469
|
+
self.plotcanvas,
|
|
3470
|
+
event.x() * ratio,
|
|
3471
|
+
height - event.y() * ratio,
|
|
3472
|
+
)
|
|
3473
|
+
contained, info = lwpax.lin_coll.contains(mouseevent)
|
|
3474
|
+
if not contained:
|
|
3475
|
+
return pd.DataFrame()
|
|
3476
|
+
|
|
3477
|
+
assignments = lin_df.iloc[info["ind"]].copy()
|
|
3478
|
+
return assignments
|
|
3479
|
+
|
|
3371
3480
|
def contextMenuCanvas(self, event):
|
|
3372
3481
|
x, y = event.x(), event.y()
|
|
3373
3482
|
geometry = self.plotcanvas.geometry()
|
|
@@ -3389,8 +3498,20 @@ class LWPWidget(QGroupBox):
|
|
|
3389
3498
|
open_blend_action = menu.addAction("Open in Blended Lines")
|
|
3390
3499
|
fit_all_action = menu.addAction("Fit all")
|
|
3391
3500
|
|
|
3501
|
+
assignments = self.get_assignments_at(lwpax, event)
|
|
3502
|
+
delete_assignment_action = None
|
|
3503
|
+
if len(assignments):
|
|
3504
|
+
menu.addSeparator()
|
|
3505
|
+
delete_assignment_action = menu.addAction(
|
|
3506
|
+
f"Delete assignment{'s' if len(assignments) > 1 else ''}"
|
|
3507
|
+
)
|
|
3508
|
+
|
|
3392
3509
|
action = menu.exec(self.plotcanvas.mapToGlobal(event.pos()))
|
|
3393
|
-
if action
|
|
3510
|
+
if action is None:
|
|
3511
|
+
return
|
|
3512
|
+
if action == delete_assignment_action:
|
|
3513
|
+
delete_assignments(assignments)
|
|
3514
|
+
elif action == get_position_action:
|
|
3394
3515
|
QApplication.clipboard().setText(str(lwpax.ref_position))
|
|
3395
3516
|
elif action == get_qns_action:
|
|
3396
3517
|
output_string = []
|
|
@@ -3761,6 +3882,12 @@ class MainWindow(QMainWindow):
|
|
|
3761
3882
|
config.register("flag_allowdocking", self.update_docking)
|
|
3762
3883
|
config.register("flag_docksalwaysontop", self.update_docksalwaysontop)
|
|
3763
3884
|
|
|
3885
|
+
self.setDockOptions(
|
|
3886
|
+
QMainWindow.DockOption.AnimatedDocks
|
|
3887
|
+
| QMainWindow.DockOption.AllowNestedDocks
|
|
3888
|
+
| QMainWindow.DockOption.AllowTabbedDocks
|
|
3889
|
+
)
|
|
3890
|
+
|
|
3764
3891
|
try:
|
|
3765
3892
|
# Set LLWP logo as icon
|
|
3766
3893
|
possible_folders = [
|
|
@@ -6018,6 +6145,14 @@ class ReferenceSelector(QTabWidget):
|
|
|
6018
6145
|
self.values_changed.emit()
|
|
6019
6146
|
mainwindow.lwpwidget.set_data()
|
|
6020
6147
|
|
|
6148
|
+
def change_series_qns(self):
|
|
6149
|
+
i, ok = QInputDialog.getInt(
|
|
6150
|
+
self, "Set Number of Quantum Numbers", "# Quantum Numbers: "
|
|
6151
|
+
)
|
|
6152
|
+
if not ok:
|
|
6153
|
+
return
|
|
6154
|
+
config["series_qns"] = i
|
|
6155
|
+
|
|
6021
6156
|
def contextMenuEvent(self, event):
|
|
6022
6157
|
menu = QMenu(self)
|
|
6023
6158
|
get_positions_action = menu.addAction("Copy Reference Positions")
|
|
@@ -6027,8 +6162,15 @@ class ReferenceSelector(QTabWidget):
|
|
|
6027
6162
|
|
|
6028
6163
|
change_qns_templates = config["series_changeqnsactions"]
|
|
6029
6164
|
change_qns_actions = {}
|
|
6165
|
+
is_transition_active = self.state["method"] == "Transition"
|
|
6166
|
+
|
|
6167
|
+
if is_transition_active:
|
|
6168
|
+
menu.addSeparator()
|
|
6169
|
+
change_series_qns_action = menu.addAction(
|
|
6170
|
+
"Change number of quantum numbers"
|
|
6171
|
+
)
|
|
6030
6172
|
|
|
6031
|
-
if
|
|
6173
|
+
if is_transition_active and len(change_qns_templates):
|
|
6032
6174
|
menu.addSeparator()
|
|
6033
6175
|
|
|
6034
6176
|
for label in change_qns_templates.keys():
|
|
@@ -6065,6 +6207,8 @@ class ReferenceSelector(QTabWidget):
|
|
|
6065
6207
|
self.series_selector.change_qns_action(label)
|
|
6066
6208
|
elif action == list_from_cat_trend_action:
|
|
6067
6209
|
self.list_from_cat_trend()
|
|
6210
|
+
elif is_transition_active and action == change_series_qns_action:
|
|
6211
|
+
self.change_series_qns()
|
|
6068
6212
|
|
|
6069
6213
|
|
|
6070
6214
|
class SeriesSelector(QWidget):
|
|
@@ -6620,7 +6764,11 @@ class NewAssignmentsWindow(EQDockWidget):
|
|
|
6620
6764
|
# tooltip_append = "Append to file if checked or overwrite content if unchecked"
|
|
6621
6765
|
new_assignments = self.new_assignments = NewAssignments.get_instance()
|
|
6622
6766
|
|
|
6623
|
-
save_label =
|
|
6767
|
+
save_label = (
|
|
6768
|
+
"Save && Update"
|
|
6769
|
+
if (not config["onsave_skipall"]) and config["onsave_consent"]
|
|
6770
|
+
else "Save"
|
|
6771
|
+
)
|
|
6624
6772
|
widgets = self.widgets = {
|
|
6625
6773
|
"save_settings": QQ(
|
|
6626
6774
|
QToolButton,
|
|
@@ -6991,7 +7139,9 @@ class OnSaveOptionsDialog(QDialog):
|
|
|
6991
7139
|
for widget in self.widgets:
|
|
6992
7140
|
widget.setEnabled(enabled)
|
|
6993
7141
|
|
|
6994
|
-
NewAssignmentsWindow.instance.widgets["save"].setText(
|
|
7142
|
+
NewAssignmentsWindow.instance.widgets["save"].setText(
|
|
7143
|
+
"Save && Update" if enabled else "Save"
|
|
7144
|
+
)
|
|
6995
7145
|
|
|
6996
7146
|
def on_exit(self, _=None):
|
|
6997
7147
|
self.__class__.open_instance = None
|
|
@@ -7637,35 +7787,36 @@ class ResidualsWindow(EQDockWidget):
|
|
|
7637
7787
|
colors = df["color"].to_numpy()
|
|
7638
7788
|
tuples = list(zip(xs, ys))
|
|
7639
7789
|
tuples = tuples if len(tuples) != 0 else [[None, None]]
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
xmin
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
ymin
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
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
|
+
|
|
7662
7814
|
notify_info.emit("<br/>".join(message))
|
|
7663
7815
|
except Exception:
|
|
7664
7816
|
notify_warning.emit("There was an error in your Residuals window input")
|
|
7665
7817
|
finally:
|
|
7666
7818
|
self.plotting_finished.emit()
|
|
7667
7819
|
|
|
7668
|
-
|
|
7669
7820
|
def save_residuals(self):
|
|
7670
7821
|
df = self.get_residuals()
|
|
7671
7822
|
fname = QFileDialog.getSaveFileName(
|
|
@@ -7734,52 +7885,12 @@ class ResidualsWindow(EQDockWidget):
|
|
|
7734
7885
|
mainwindow.raise_()
|
|
7735
7886
|
mainwindow.activateWindow()
|
|
7736
7887
|
elif action_to_perform == "deletefromfile":
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
lin = pyckett.lin_to_df(lin_fname, sort=False)
|
|
7744
|
-
|
|
7745
|
-
transitions_to_delete = tmp_transitions.query(
|
|
7746
|
-
"filename_lin == @lin_fname"
|
|
7747
|
-
)
|
|
7748
|
-
columns = {
|
|
7749
|
-
x: x.replace("_lin", "").replace("_x", "")
|
|
7750
|
-
for x in transitions_to_delete.columns
|
|
7751
|
-
if (x.endswith("_lin") or x.endswith("_x"))
|
|
7752
|
-
}
|
|
7753
|
-
transitions_to_delete = transitions_to_delete.rename(
|
|
7754
|
-
columns=columns
|
|
7755
|
-
)
|
|
7756
|
-
transitions_to_delete = transitions_to_delete[
|
|
7757
|
-
list(pyckett.lin_dtypes.keys())
|
|
7758
|
-
]
|
|
7759
|
-
|
|
7760
|
-
lin = pd.merge(
|
|
7761
|
-
lin, transitions_to_delete, how="left", indicator=True
|
|
7762
|
-
)
|
|
7763
|
-
lin = lin[lin["_merge"] == "left_only"]
|
|
7764
|
-
lin = lin.drop("_merge", axis="columns").reset_index(drop=True)
|
|
7765
|
-
|
|
7766
|
-
if lin_fname == "__newassignments__":
|
|
7767
|
-
new_assignments.new_assignments_df = lin
|
|
7768
|
-
new_assignments.load_file()
|
|
7769
|
-
|
|
7770
|
-
new_assignments_window = NewAssignmentsWindow.instance
|
|
7771
|
-
new_assignments_window.model.update()
|
|
7772
|
-
# new_assignments_window.model.resize_columns()
|
|
7773
|
-
else:
|
|
7774
|
-
# Saving the *.lin file after removing the transitions
|
|
7775
|
-
# can overwrite custom formats for the x and error values
|
|
7776
|
-
with open(lin_fname, "w+") as file:
|
|
7777
|
-
custom_freq_format = config["flag_lincustomfreqformat"]
|
|
7778
|
-
file.write(
|
|
7779
|
-
pyckett.df_to_lin(
|
|
7780
|
-
lin, custom_freq_format=custom_freq_format
|
|
7781
|
-
)
|
|
7782
|
-
)
|
|
7888
|
+
columns = {
|
|
7889
|
+
x: x.replace("_lin", "").replace("_x", "")
|
|
7890
|
+
for x in tmp_transitions.columns
|
|
7891
|
+
if (x.endswith("_lin") or x.endswith("_x"))
|
|
7892
|
+
}
|
|
7893
|
+
delete_assignments(tmp_transitions.rename(columns=columns))
|
|
7783
7894
|
|
|
7784
7895
|
def fit_file_context_menu(self, pos):
|
|
7785
7896
|
menu = QMenu(self)
|
|
@@ -7835,7 +7946,6 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
7835
7946
|
[], [], color=config["blendedlines_color_total"]
|
|
7836
7947
|
)[0]
|
|
7837
7948
|
self.plot_parts = []
|
|
7838
|
-
self.plot_parts_lock = threading.Lock()
|
|
7839
7949
|
self.update_plot_requested.connect(super().update_plot)
|
|
7840
7950
|
|
|
7841
7951
|
def update_plot(self):
|
|
@@ -7976,7 +8086,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
7976
8086
|
x, profile, derivative, 0, *args, fixedwidth=fixedwidth
|
|
7977
8087
|
)
|
|
7978
8088
|
|
|
7979
|
-
with
|
|
8089
|
+
with matplotlib_lock:
|
|
7980
8090
|
for part in plot_widget.plot_parts:
|
|
7981
8091
|
part.remove()
|
|
7982
8092
|
plot_widget.plot_parts = []
|
|
@@ -8076,8 +8186,9 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8076
8186
|
|
|
8077
8187
|
ax = plot_widget.ax
|
|
8078
8188
|
|
|
8079
|
-
|
|
8080
|
-
|
|
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"])
|
|
8081
8192
|
|
|
8082
8193
|
opt_param = []
|
|
8083
8194
|
err_param = []
|
|
@@ -8101,7 +8212,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8101
8212
|
tmp_ys = fitfunction_no_baseline(res_xs, *tmp_params)
|
|
8102
8213
|
tmp_ys += exp_mean
|
|
8103
8214
|
|
|
8104
|
-
with
|
|
8215
|
+
with matplotlib_lock:
|
|
8105
8216
|
plot_widget.plot_parts.append(
|
|
8106
8217
|
ax.plot(
|
|
8107
8218
|
res_xs,
|
|
@@ -8114,7 +8225,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8114
8225
|
opt_param.append(tmp_params)
|
|
8115
8226
|
err_param.append(tmp_errors)
|
|
8116
8227
|
|
|
8117
|
-
with
|
|
8228
|
+
with matplotlib_lock:
|
|
8118
8229
|
plot_widget.plot_parts.append(
|
|
8119
8230
|
ax.scatter(
|
|
8120
8231
|
[x[0] for x in opt_param],
|
|
@@ -8125,7 +8236,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8125
8236
|
|
|
8126
8237
|
if polynomrank > 0 and config["blendedlines_showbaseline"]:
|
|
8127
8238
|
baseline_args = popt[-polynomrank:]
|
|
8128
|
-
with
|
|
8239
|
+
with matplotlib_lock:
|
|
8129
8240
|
plot_widget.plot_parts.append(
|
|
8130
8241
|
ax.plot(
|
|
8131
8242
|
res_xs,
|
|
@@ -8164,7 +8275,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8164
8275
|
|
|
8165
8276
|
peaks = self.peaks.copy()
|
|
8166
8277
|
fixedwidth = config["blendedlines_fixedwidth"]
|
|
8167
|
-
with
|
|
8278
|
+
with matplotlib_lock:
|
|
8168
8279
|
for part in plot_widget.plot_parts:
|
|
8169
8280
|
part.remove()
|
|
8170
8281
|
plot_widget.plot_parts = []
|
|
@@ -8337,7 +8448,7 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8337
8448
|
|
|
8338
8449
|
idx += delta_idx
|
|
8339
8450
|
|
|
8340
|
-
with
|
|
8451
|
+
with matplotlib_lock:
|
|
8341
8452
|
plot_widget.plot_parts.append(
|
|
8342
8453
|
ax.scatter(
|
|
8343
8454
|
[x[0] for x in opt_param],
|
|
@@ -8366,8 +8477,9 @@ class BlendedLinesWindow(EQDockWidget):
|
|
|
8366
8477
|
|
|
8367
8478
|
thread.earlyreturn()
|
|
8368
8479
|
|
|
8369
|
-
|
|
8370
|
-
|
|
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"])
|
|
8371
8483
|
|
|
8372
8484
|
self.fill_table_requested.emit()
|
|
8373
8485
|
self.set_indicator_text.emit("Ready")
|
|
@@ -9097,28 +9209,29 @@ class EnergyLevelsWindow(EQDockWidget):
|
|
|
9097
9209
|
colors = df["color"].to_numpy()
|
|
9098
9210
|
tuples = list(zip(xs, ys))
|
|
9099
9211
|
tuples = tuples if len(tuples) != 0 else [[None, None]]
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
xmin
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
ymin
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
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
|
+
)
|
|
9122
9235
|
except Exception:
|
|
9123
9236
|
notify_error.emit("There was an error in your Energy Levels window inputs")
|
|
9124
9237
|
raise
|
|
@@ -9197,6 +9310,7 @@ class PeakfinderWindow(EQDockWidget):
|
|
|
9197
9310
|
[], [], color=config["peakfinder_peakcolor"], marker="*"
|
|
9198
9311
|
)
|
|
9199
9312
|
|
|
9313
|
+
@lock_d(matplotlib_lock)
|
|
9200
9314
|
def update_plot(self):
|
|
9201
9315
|
xmin, xmax = self.xrange
|
|
9202
9316
|
tuples = list(filter(lambda x: xmin < x[0] < xmax, self.parent.peaks))
|
|
@@ -9221,7 +9335,7 @@ class PeakfinderWindow(EQDockWidget):
|
|
|
9221
9335
|
layout.addLayout(tmp_layout)
|
|
9222
9336
|
|
|
9223
9337
|
self.run_button = QQ(
|
|
9224
|
-
QPushButton, text="Run Peakfinder", change=lambda: self.
|
|
9338
|
+
QPushButton, text="Run Peakfinder", change=lambda: self.on_run_clicked()
|
|
9225
9339
|
)
|
|
9226
9340
|
tmp_layout.addWidget(self.run_button)
|
|
9227
9341
|
tmp_layout.addWidget(
|
|
@@ -9345,11 +9459,15 @@ class PeakfinderWindow(EQDockWidget):
|
|
|
9345
9459
|
config["peakfinder_kwargs"] = kwargs
|
|
9346
9460
|
return kwargs.copy()
|
|
9347
9461
|
|
|
9462
|
+
def on_run_clicked(self):
|
|
9463
|
+
self.run_button.setEnabled(False)
|
|
9464
|
+
kwargs = self.get_kwargs()
|
|
9465
|
+
self.find_peaks(kwargs)
|
|
9466
|
+
|
|
9348
9467
|
@QThread.threaded_d
|
|
9349
9468
|
@peak_locked
|
|
9350
|
-
def find_peaks(self, thread=None):
|
|
9469
|
+
def find_peaks(self, kwargs, thread=None):
|
|
9351
9470
|
self.peakfinding_started.emit()
|
|
9352
|
-
kwargs = self.get_kwargs()
|
|
9353
9471
|
|
|
9354
9472
|
if "frequency" in kwargs:
|
|
9355
9473
|
val = kwargs["frequency"]
|
|
@@ -10164,9 +10282,9 @@ def bin_data(dataframe, binwidth, range):
|
|
|
10164
10282
|
bin_array = ((dataframe["x"].to_numpy() - range[0]) // binwidth).astype(np.int64)
|
|
10165
10283
|
value_column = "x" if "y" not in dataframe else "y"
|
|
10166
10284
|
|
|
10167
|
-
index_ = dataframe.groupby(
|
|
10168
|
-
|
|
10169
|
-
|
|
10285
|
+
index_ = dataframe.groupby([bin_array, dataframe["filename"]], observed=True)[
|
|
10286
|
+
value_column
|
|
10287
|
+
].idxmax()
|
|
10170
10288
|
return dataframe.loc[index_.to_numpy()]
|
|
10171
10289
|
|
|
10172
10290
|
|
|
@@ -10575,7 +10693,11 @@ class ASAPAx(LWPAx):
|
|
|
10575
10693
|
np.zeros(shape=(0, 2, 2)), colors=config["color_exp"], capstyle="round"
|
|
10576
10694
|
)
|
|
10577
10695
|
self.lin_coll = self.ax.scatter(
|
|
10578
|
-
[],
|
|
10696
|
+
[],
|
|
10697
|
+
[],
|
|
10698
|
+
color=config["color_ref"],
|
|
10699
|
+
marker=config["plot_linmarker"],
|
|
10700
|
+
zorder=100,
|
|
10579
10701
|
)
|
|
10580
10702
|
|
|
10581
10703
|
with matplotlib_lock:
|
|
@@ -10617,6 +10739,7 @@ class ASAPAx(LWPAx):
|
|
|
10617
10739
|
self.exp_coll.set(segments=segs, color=color)
|
|
10618
10740
|
|
|
10619
10741
|
@drawplot_decorator.d
|
|
10742
|
+
@lock_d(matplotlib_lock)
|
|
10620
10743
|
def update(self):
|
|
10621
10744
|
ax = self.ax
|
|
10622
10745
|
|
|
@@ -10786,12 +10909,13 @@ class ASAPAx(LWPAx):
|
|
|
10786
10909
|
return
|
|
10787
10910
|
|
|
10788
10911
|
# Delete artists highlighting previous fit
|
|
10789
|
-
|
|
10790
|
-
self.__class__.fit_vline
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
self.__class__.fit_curve
|
|
10794
|
-
|
|
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
|
|
10795
10919
|
|
|
10796
10920
|
if onclick:
|
|
10797
10921
|
xmiddle = xmin
|
|
@@ -10800,14 +10924,16 @@ class ASAPAx(LWPAx):
|
|
|
10800
10924
|
else:
|
|
10801
10925
|
# Fit the data
|
|
10802
10926
|
xmiddle, xuncert, fit_xs, fit_ys = self.fit_peak(xmin, xmax)
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
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]
|
|
10806
10931
|
|
|
10807
10932
|
# Highlight fit in plot
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
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
|
+
)
|
|
10811
10937
|
|
|
10812
10938
|
# Emit signal for detail viewer
|
|
10813
10939
|
self.x_fit = xmiddle
|
|
@@ -10904,8 +11030,8 @@ class ASAPAx(LWPAx):
|
|
|
10904
11030
|
fit_xs, fit_ys = fit_results["fit_xs"], fit_results["fit_ys"]
|
|
10905
11031
|
|
|
10906
11032
|
except Exception as E:
|
|
10907
|
-
self.
|
|
10908
|
-
self.
|
|
11033
|
+
self.fit_curve = None
|
|
11034
|
+
self.fit_line = None
|
|
10909
11035
|
notify_error.emit(
|
|
10910
11036
|
f"The fitting failed with the following error message : {str(E)}"
|
|
10911
11037
|
)
|
|
@@ -11259,12 +11385,13 @@ class ASAPWidget(LWPWidget):
|
|
|
11259
11385
|
@status_d
|
|
11260
11386
|
@drawplot_decorator.d
|
|
11261
11387
|
def calc_correlation_plots(self, thread=None):
|
|
11262
|
-
|
|
11263
|
-
self._ax_class.fit_vline
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
self._ax_class.fit_curve
|
|
11267
|
-
|
|
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
|
|
11268
11395
|
|
|
11269
11396
|
if not hasattr(ASAPSettingsWindow, "instance"):
|
|
11270
11397
|
return
|
|
@@ -11294,7 +11421,7 @@ class ASAPWidget(LWPWidget):
|
|
|
11294
11421
|
|
|
11295
11422
|
for i_col in range(n_widgets):
|
|
11296
11423
|
thread.earlyreturn()
|
|
11297
|
-
if i_col
|
|
11424
|
+
if i_col >= n_cols:
|
|
11298
11425
|
continue
|
|
11299
11426
|
|
|
11300
11427
|
refwidget = tab_widget.widget(i_col)
|
|
@@ -11803,10 +11930,22 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
11803
11930
|
|
|
11804
11931
|
tmp_layout = QHBoxLayout()
|
|
11805
11932
|
tmp_layout.addWidget(QQ(QLabel, text="Width: "))
|
|
11806
|
-
tmp_layout.addWidget(
|
|
11933
|
+
tmp_layout.addWidget(
|
|
11934
|
+
QQ(
|
|
11935
|
+
QDoubleSpinBoxFullPrec,
|
|
11936
|
+
"asap_squaredwidth",
|
|
11937
|
+
width=120,
|
|
11938
|
+
range=(None, None),
|
|
11939
|
+
)
|
|
11940
|
+
)
|
|
11807
11941
|
tmp_layout.addWidget(QQ(QLabel, text="Stepsize: "))
|
|
11808
11942
|
tmp_layout.addWidget(
|
|
11809
|
-
QQ(
|
|
11943
|
+
QQ(
|
|
11944
|
+
QDoubleSpinBoxFullPrec,
|
|
11945
|
+
"asap_squaredstepsize",
|
|
11946
|
+
width=120,
|
|
11947
|
+
range=(None, None),
|
|
11948
|
+
)
|
|
11810
11949
|
)
|
|
11811
11950
|
tmp_layout.addWidget(QQ(QLabel, text="Threshold: "))
|
|
11812
11951
|
tmp_layout.addWidget(
|
|
@@ -11870,12 +12009,15 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
11870
12009
|
self.noq = config["series_qns"]
|
|
11871
12010
|
assignments = []
|
|
11872
12011
|
|
|
11873
|
-
|
|
11874
|
-
self.fig.
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
self.
|
|
11878
|
-
|
|
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
|
+
)
|
|
11879
12021
|
|
|
11880
12022
|
width = config["asap_squaredwidth"] or config["plot_width"]
|
|
11881
12023
|
stepsize = config["asap_squaredstepsize"] or config["asap_stepsize"]
|
|
@@ -11943,9 +12085,10 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
11943
12085
|
tot_ys /= ys_max
|
|
11944
12086
|
log_y_max += np.log10(ys_max)
|
|
11945
12087
|
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
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")
|
|
11949
12092
|
|
|
11950
12093
|
self.data = np.vstack((rel_xs, tot_ys)).T
|
|
11951
12094
|
self.drawplot.emit()
|
|
@@ -12016,12 +12159,13 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12016
12159
|
@status_d
|
|
12017
12160
|
def fit_data(self, xmin, xmax, thread=None):
|
|
12018
12161
|
# Delete artists highlighting previous fit
|
|
12019
|
-
|
|
12020
|
-
self.fit_vline
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
self.fit_curve
|
|
12024
|
-
|
|
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
|
|
12025
12169
|
|
|
12026
12170
|
if self.data is None:
|
|
12027
12171
|
notify_warning.emit("No cross-correlation data available.")
|
|
@@ -12054,8 +12198,8 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12054
12198
|
f"The following parameters were determined: \n{popt=}\n{perr=}"
|
|
12055
12199
|
)
|
|
12056
12200
|
except Exception as E:
|
|
12057
|
-
self.
|
|
12058
|
-
self.
|
|
12201
|
+
self.fit_curve = None
|
|
12202
|
+
self.fit_vline = None
|
|
12059
12203
|
notify_error.emit(
|
|
12060
12204
|
f"The fitting failed with the following error message : {str(E)}"
|
|
12061
12205
|
)
|
|
@@ -12065,12 +12209,13 @@ class ASAPSquaredWindow(EQDockWidget):
|
|
|
12065
12209
|
self.clipboard_requested.emit(str(xmiddle))
|
|
12066
12210
|
|
|
12067
12211
|
# Highlight fit in plot
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
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
|
+
)
|
|
12074
12219
|
self.xmiddle = xmiddle
|
|
12075
12220
|
self.xuncert = xuncert
|
|
12076
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "llwp"
|
|
7
|
-
version = "2.1.
|
|
7
|
+
version = "2.1.2"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Luis Bonah", email="bonah@ph1.uni-koeln.de" },
|
|
10
10
|
]
|
|
@@ -30,3 +30,6 @@ dev = [
|
|
|
30
30
|
[project.scripts]
|
|
31
31
|
llwp = "llwp:start_llwp"
|
|
32
32
|
asap = "llwp:start_asap"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools]
|
|
35
|
+
include-package-data = true
|
|
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
|