teareduce 0.6.5__py3-none-any.whl → 0.6.6__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.
- teareduce/cleanest/__main__.py +4 -10
- teareduce/cleanest/cosmicraycleanerapp.py +642 -233
- teareduce/cleanest/definitions.py +63 -5
- teareduce/cleanest/interpolate.py +1 -1
- teareduce/cleanest/interpolationeditor.py +23 -9
- teareduce/cleanest/lacosmicpad.py +23 -4
- teareduce/cleanest/parametereditor.py +386 -5
- teareduce/cleanest/reviewcosmicray.py +109 -18
- teareduce/sdistortion.py +1 -1
- teareduce/statsummary.py +1 -1
- teareduce/version.py +1 -1
- teareduce/wavecal.py +1 -1
- {teareduce-0.6.5.dist-info → teareduce-0.6.6.dist-info}/METADATA +5 -1
- {teareduce-0.6.5.dist-info → teareduce-0.6.6.dist-info}/RECORD +18 -18
- {teareduce-0.6.5.dist-info → teareduce-0.6.6.dist-info}/WHEEL +0 -0
- {teareduce-0.6.5.dist-info → teareduce-0.6.6.dist-info}/entry_points.txt +0 -0
- {teareduce-0.6.5.dist-info → teareduce-0.6.6.dist-info}/licenses/LICENSE.txt +0 -0
- {teareduce-0.6.5.dist-info → teareduce-0.6.6.dist-info}/top_level.txt +0 -0
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
"""Parameter editor dialog for L.A.Cosmic parameters."""
|
|
11
11
|
|
|
12
|
-
from astropy.io import fits
|
|
13
12
|
from pathlib import Path
|
|
14
13
|
import tkinter as tk
|
|
15
14
|
from tkinter import ttk
|
|
@@ -19,9 +18,10 @@ from tkinter import messagebox
|
|
|
19
18
|
from .askextension import ask_extension_input_image
|
|
20
19
|
from .centerchildparent import center_on_parent
|
|
21
20
|
from .definitions import lacosmic_default_dict
|
|
21
|
+
from .definitions import pycosmic_default_dict
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
class
|
|
24
|
+
class ParameterEditorLACosmic:
|
|
25
25
|
"""A dialog to edit L.A.Cosmic parameters."""
|
|
26
26
|
|
|
27
27
|
def __init__(
|
|
@@ -76,6 +76,10 @@ class ParameterEditor:
|
|
|
76
76
|
-------
|
|
77
77
|
create_widgets()
|
|
78
78
|
Create the widgets for the dialog.
|
|
79
|
+
define_inbkg()
|
|
80
|
+
Define the input background image.
|
|
81
|
+
define_invar()
|
|
82
|
+
Define the input variance image.
|
|
79
83
|
on_ok()
|
|
80
84
|
Validate and save the updated values.
|
|
81
85
|
on_cancel()
|
|
@@ -84,6 +88,8 @@ class ParameterEditor:
|
|
|
84
88
|
Reset all fields to original values.
|
|
85
89
|
get_result()
|
|
86
90
|
Return the updated dictionary.
|
|
91
|
+
update_colour_param_run1_run2()
|
|
92
|
+
Update the color of run2 entries if they differ from run1.
|
|
87
93
|
|
|
88
94
|
Attributes
|
|
89
95
|
----------
|
|
@@ -167,7 +173,7 @@ class ParameterEditor:
|
|
|
167
173
|
row += 1
|
|
168
174
|
|
|
169
175
|
# Note: here we are using entry_vars to trace changes in the entries
|
|
170
|
-
# so that we can update the color of run2 entries if they differ from run1
|
|
176
|
+
# so that we can update the color of run2 entries if they differ from run1
|
|
171
177
|
self.entry_vars = {}
|
|
172
178
|
row_subtable = 0
|
|
173
179
|
coloff = 0
|
|
@@ -215,7 +221,7 @@ class ParameterEditor:
|
|
|
215
221
|
entry = tk.Entry(main_frame, textvariable=self.entry_vars[key2], width=10)
|
|
216
222
|
entry.insert(0, str(self.param_dict[key2]["value"]))
|
|
217
223
|
entry.grid(row=row, column=2 + coloff, padx=10, pady=5)
|
|
218
|
-
self.entries[
|
|
224
|
+
self.entries[key2] = entry # dictionary to hold entry widgets
|
|
219
225
|
# Type label
|
|
220
226
|
infotext = info["type"].__name__
|
|
221
227
|
if infotext == "int":
|
|
@@ -266,7 +272,11 @@ class ParameterEditor:
|
|
|
266
272
|
# Vertical separator between splitted table
|
|
267
273
|
separatorv1 = ttk.Separator(main_frame, orient="vertical")
|
|
268
274
|
separatorv1.grid(
|
|
269
|
-
row=row - max_num_params_in_columns
|
|
275
|
+
row=row - max_num_params_in_columns - 1,
|
|
276
|
+
column=4,
|
|
277
|
+
rowspan=max_num_params_in_columns + 1,
|
|
278
|
+
sticky="ns",
|
|
279
|
+
padx=10,
|
|
270
280
|
)
|
|
271
281
|
|
|
272
282
|
# Separator
|
|
@@ -480,6 +490,14 @@ class ParameterEditor:
|
|
|
480
490
|
|
|
481
491
|
# Additional validation for region limits
|
|
482
492
|
try:
|
|
493
|
+
if updated_dict["xmin"]["value"] < 1 or updated_dict["xmin"]["value"] > self.imgshape[1]:
|
|
494
|
+
raise ValueError(f"xmin must be in the range [1, {self.imgshape[1]}]")
|
|
495
|
+
if updated_dict["xmax"]["value"] < 1 or updated_dict["xmax"]["value"] > self.imgshape[1]:
|
|
496
|
+
raise ValueError(f"xmax must be in the range [1, {self.imgshape[1]}]")
|
|
497
|
+
if updated_dict["ymin"]["value"] < 1 or updated_dict["ymin"]["value"] > self.imgshape[0]:
|
|
498
|
+
raise ValueError(f"ymin must be in the range [1, {self.imgshape[0]}]")
|
|
499
|
+
if updated_dict["ymax"]["value"] < 1 or updated_dict["ymax"]["value"] > self.imgshape[0]:
|
|
500
|
+
raise ValueError(f"ymax must be in the range [1, {self.imgshape[0]}]")
|
|
483
501
|
if updated_dict["xmax"]["value"] <= updated_dict["xmin"]["value"]:
|
|
484
502
|
raise ValueError("xmax must be greater than xmin")
|
|
485
503
|
if updated_dict["ymax"]["value"] <= updated_dict["ymin"]["value"]:
|
|
@@ -555,3 +573,366 @@ class ParameterEditor:
|
|
|
555
573
|
self.entries["run1_" + parname].selection_clear()
|
|
556
574
|
if "run2_" + parname in self.entries:
|
|
557
575
|
self.entries["run2_" + parname].selection_clear()
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class ParameterEditorPyCosmic:
|
|
579
|
+
"""A dialog to edit PyCosmic parameters."""
|
|
580
|
+
|
|
581
|
+
def __init__(
|
|
582
|
+
self,
|
|
583
|
+
root,
|
|
584
|
+
param_dict,
|
|
585
|
+
window_title,
|
|
586
|
+
xmin,
|
|
587
|
+
xmax,
|
|
588
|
+
ymin,
|
|
589
|
+
ymax,
|
|
590
|
+
imgshape,
|
|
591
|
+
):
|
|
592
|
+
"""Initialize the parameter editor dialog.
|
|
593
|
+
|
|
594
|
+
Parameters
|
|
595
|
+
----------
|
|
596
|
+
root : tk.Tk
|
|
597
|
+
The root Tkinter window.
|
|
598
|
+
param_dict : dict
|
|
599
|
+
Dictionary with L.A.Cosmic parameters.
|
|
600
|
+
window_title : str
|
|
601
|
+
Title of the dialog window.
|
|
602
|
+
xmin : int
|
|
603
|
+
Minimum x-coordinate of the region to be examined.
|
|
604
|
+
From 1 to NAXIS1.
|
|
605
|
+
xmax : int
|
|
606
|
+
Maximum x-coordinate of the region to be examined.
|
|
607
|
+
From 1 to NAXIS1.
|
|
608
|
+
ymin : int
|
|
609
|
+
Minimum y-coordinate of the region to be examined.
|
|
610
|
+
From 1 to NAXIS2.
|
|
611
|
+
ymax : int
|
|
612
|
+
Maximum y-coordinate of the region to be examined.
|
|
613
|
+
From 1 to NAXIS2.
|
|
614
|
+
imgshape : tuple
|
|
615
|
+
Shape of the image (height, width).
|
|
616
|
+
|
|
617
|
+
Methods
|
|
618
|
+
-------
|
|
619
|
+
create_widgets()
|
|
620
|
+
Create the widgets for the dialog.
|
|
621
|
+
on_ok()
|
|
622
|
+
Validate and save the updated values.
|
|
623
|
+
on_cancel()
|
|
624
|
+
Close the dialog without saving.
|
|
625
|
+
get_result()
|
|
626
|
+
Return the updated dictionary.
|
|
627
|
+
|
|
628
|
+
Attributes
|
|
629
|
+
----------
|
|
630
|
+
root : tk.Tk
|
|
631
|
+
The root Tkinter window.
|
|
632
|
+
param_dict : dict
|
|
633
|
+
Dictionary with L.A.Cosmic parameters.
|
|
634
|
+
imgshape : tuple
|
|
635
|
+
Shape of the image (height, width).
|
|
636
|
+
entries : dict
|
|
637
|
+
Dictionary to hold entry widgets.
|
|
638
|
+
result_dict : dict or None
|
|
639
|
+
The updated dictionary of parameters or None if cancelled.
|
|
640
|
+
"""
|
|
641
|
+
self.root = root
|
|
642
|
+
self.root.title(window_title)
|
|
643
|
+
self.param_dict = param_dict
|
|
644
|
+
# Set default region values
|
|
645
|
+
self.param_dict["xmin"]["value"] = xmin
|
|
646
|
+
self.param_dict["xmax"]["value"] = xmax
|
|
647
|
+
self.param_dict["ymin"]["value"] = ymin
|
|
648
|
+
self.param_dict["ymax"]["value"] = ymax
|
|
649
|
+
self.imgshape = imgshape
|
|
650
|
+
self.entries = {"run1": {}, "run2": {}} # dictionary to hold entry widgets
|
|
651
|
+
self.result_dict = {}
|
|
652
|
+
|
|
653
|
+
# Create the form
|
|
654
|
+
self.create_widgets()
|
|
655
|
+
center_on_parent(child=self.root, parent=self.root.master)
|
|
656
|
+
|
|
657
|
+
def create_widgets(self):
|
|
658
|
+
"""Create the widgets for the dialog."""
|
|
659
|
+
# Define different styles for different conditions
|
|
660
|
+
style = ttk.Style()
|
|
661
|
+
style.configure("Normal.TCombobox", foreground="black", background="white")
|
|
662
|
+
style.configure("Changed.TCombobox", foreground="red", background="white")
|
|
663
|
+
|
|
664
|
+
# Main frame
|
|
665
|
+
main_frame = tk.Frame(self.root, padx=10, pady=10)
|
|
666
|
+
main_frame.pack()
|
|
667
|
+
|
|
668
|
+
row = 0
|
|
669
|
+
|
|
670
|
+
# Subtitle for PyCosmic parameters
|
|
671
|
+
default_font = tk.font.nametofont("TkDefaultFont")
|
|
672
|
+
bold_font = default_font.copy()
|
|
673
|
+
bold_font.configure(weight="bold", size=default_font.cget("size") + 2)
|
|
674
|
+
subtitle_label = tk.Label(main_frame, text="PyCosmic Parameters", font=bold_font)
|
|
675
|
+
subtitle_label.grid(row=row, column=0, columnspan=9, pady=(0, 10))
|
|
676
|
+
row += 1
|
|
677
|
+
|
|
678
|
+
# Count number of parameters for run1 and run2
|
|
679
|
+
nparams_run1 = sum(1 for key in self.param_dict.keys() if key.startswith("run1_"))
|
|
680
|
+
nparams_run2 = sum(1 for key in self.param_dict.keys() if key.startswith("run2_"))
|
|
681
|
+
if nparams_run1 != nparams_run2:
|
|
682
|
+
raise ValueError("Number of parameters for run1 and run2 do not match.")
|
|
683
|
+
else:
|
|
684
|
+
nparams_total = nparams_run1
|
|
685
|
+
max_num_params_in_columns = nparams_total // 2 + nparams_total % 2
|
|
686
|
+
|
|
687
|
+
# Create labels and entry fields for each parameter.
|
|
688
|
+
bold_font_subheader = default_font.copy()
|
|
689
|
+
bold_font_subheader.configure(weight="bold", size=default_font.cget("size") + 1)
|
|
690
|
+
for subtable in range(2):
|
|
691
|
+
if subtable == 0:
|
|
692
|
+
coloff = 0
|
|
693
|
+
else:
|
|
694
|
+
coloff = 5
|
|
695
|
+
label = tk.Label(main_frame, text="Parameter", font=bold_font_subheader, anchor="w", fg="gray")
|
|
696
|
+
label.grid(row=row, column=0 + coloff, sticky="e", pady=0)
|
|
697
|
+
label = tk.Label(main_frame, text="Run 1", font=bold_font_subheader, anchor="w", fg="gray", width=10)
|
|
698
|
+
label.grid(row=row, column=1 + coloff, sticky="w", padx=10, pady=0)
|
|
699
|
+
label = tk.Label(main_frame, text="Run 2", font=bold_font_subheader, anchor="w", fg="gray", width=10)
|
|
700
|
+
label.grid(row=row, column=2 + coloff, sticky="w", padx=10, pady=0)
|
|
701
|
+
label = tk.Label(main_frame, text="Type", font=bold_font_subheader, anchor="w", fg="gray", width=10)
|
|
702
|
+
label.grid(row=row, column=3 + coloff, sticky="w", pady=0)
|
|
703
|
+
row += 1
|
|
704
|
+
|
|
705
|
+
# Note: here we are using entry_vars to trace changes in the entries
|
|
706
|
+
# so that we can update the color of run2 entries if they differ from run1
|
|
707
|
+
self.entry_vars = {}
|
|
708
|
+
row_subtable = 0
|
|
709
|
+
coloff = 0
|
|
710
|
+
for key, info in self.param_dict.items():
|
|
711
|
+
if not key.startswith("run1_"):
|
|
712
|
+
continue
|
|
713
|
+
# Parameter name label
|
|
714
|
+
label = tk.Label(main_frame, text=f"{key[5:]}:", anchor="e", width=15)
|
|
715
|
+
label.grid(row=row, column=coloff, sticky="w", pady=5)
|
|
716
|
+
# Entry field for run1
|
|
717
|
+
self.entry_vars[key] = tk.StringVar()
|
|
718
|
+
self.entry_vars[key].trace_add("write", lambda *args: self.update_colour_param_run1_run2())
|
|
719
|
+
entry = tk.Entry(main_frame, textvariable=self.entry_vars[key], width=10)
|
|
720
|
+
entry.insert(0, str(info["value"]))
|
|
721
|
+
entry.grid(row=row, column=1 + coloff, padx=10, pady=5)
|
|
722
|
+
self.entries[key] = entry # dictionary to hold entry widgets
|
|
723
|
+
# Entry field for run2
|
|
724
|
+
key2 = "run2_" + key[5:]
|
|
725
|
+
self.entry_vars[key2] = tk.StringVar()
|
|
726
|
+
self.entry_vars[key2].trace_add("write", lambda *args: self.update_colour_param_run1_run2())
|
|
727
|
+
entry = tk.Entry(main_frame, textvariable=self.entry_vars[key2], width=10)
|
|
728
|
+
entry.insert(0, str(self.param_dict[key2]["value"]))
|
|
729
|
+
entry.grid(row=row, column=2 + coloff, padx=10, pady=5)
|
|
730
|
+
self.entries[key2] = entry # dictionary to
|
|
731
|
+
# Type label
|
|
732
|
+
infotext = info["type"].__name__
|
|
733
|
+
if infotext == "int":
|
|
734
|
+
if "intmode" in info:
|
|
735
|
+
if info["intmode"] == "odd":
|
|
736
|
+
infotext += ", odd"
|
|
737
|
+
elif info["intmode"] == "even":
|
|
738
|
+
infotext += ", even"
|
|
739
|
+
type_label = tk.Label(main_frame, text=f"({infotext})", fg="gray", anchor="w", width=10)
|
|
740
|
+
type_label.grid(row=row, column=3 + coloff, sticky="w", pady=5)
|
|
741
|
+
row_subtable += 1
|
|
742
|
+
if row_subtable == max_num_params_in_columns:
|
|
743
|
+
coloff = 5
|
|
744
|
+
row -= max_num_params_in_columns
|
|
745
|
+
row += 1
|
|
746
|
+
|
|
747
|
+
# Adjust row if odd number of parameters
|
|
748
|
+
if nparams_total % 2 != 0:
|
|
749
|
+
row += nparams_total % 2
|
|
750
|
+
|
|
751
|
+
# Vertical separator
|
|
752
|
+
separatorv1 = ttk.Separator(main_frame, orient="vertical")
|
|
753
|
+
separatorv1.grid(
|
|
754
|
+
row=row - max_num_params_in_columns - 1,
|
|
755
|
+
column=4,
|
|
756
|
+
rowspan=max_num_params_in_columns + 1,
|
|
757
|
+
sticky="ns",
|
|
758
|
+
padx=10,
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# Separator
|
|
762
|
+
separator1 = ttk.Separator(main_frame, orient="horizontal")
|
|
763
|
+
separator1.grid(row=row, column=0, columnspan=9, sticky="ew", pady=(10, 10))
|
|
764
|
+
row += 1
|
|
765
|
+
|
|
766
|
+
# Subtitle for region to be examined
|
|
767
|
+
subtitle_label = tk.Label(main_frame, text="Region to be Examined", font=bold_font)
|
|
768
|
+
subtitle_label.grid(row=row, column=0, columnspan=9, pady=(0, 10))
|
|
769
|
+
row += 1
|
|
770
|
+
|
|
771
|
+
# Region to be examined label and entries
|
|
772
|
+
for key, info in self.param_dict.items():
|
|
773
|
+
if key.lower() in ["xmin", "xmax", "ymin", "ymax"]:
|
|
774
|
+
# Parameter name label
|
|
775
|
+
label = tk.Label(main_frame, text=f"{key}:", anchor="e", width=15)
|
|
776
|
+
if key.lower() in ["xmin", "xmax"]:
|
|
777
|
+
coloff = 0
|
|
778
|
+
else:
|
|
779
|
+
coloff = 5
|
|
780
|
+
label.grid(row=row, column=coloff, sticky="w", pady=5)
|
|
781
|
+
# Entry field
|
|
782
|
+
entry = tk.Entry(main_frame, width=10)
|
|
783
|
+
entry.insert(0, str(info["value"]))
|
|
784
|
+
entry.grid(row=row, column=coloff + 1, padx=10, pady=5)
|
|
785
|
+
self.entries[key] = entry # dictionary to hold entry widgets
|
|
786
|
+
# Type label
|
|
787
|
+
dumtext = f"({info['type'].__name__})"
|
|
788
|
+
if key.lower() in ["xmin", "xmax"]:
|
|
789
|
+
dumtext += f" --> [1, {self.imgshape[1]}]"
|
|
790
|
+
else:
|
|
791
|
+
dumtext += f" --> [1, {self.imgshape[0]}]"
|
|
792
|
+
type_label = tk.Label(main_frame, text=dumtext, fg="gray", anchor="w", width=15)
|
|
793
|
+
type_label.grid(row=row, column=coloff + 2, sticky="w", pady=5)
|
|
794
|
+
if key.lower() == "xmax":
|
|
795
|
+
row -= 1
|
|
796
|
+
else:
|
|
797
|
+
row += 1
|
|
798
|
+
|
|
799
|
+
# Vertical separator
|
|
800
|
+
separatorv2 = ttk.Separator(main_frame, orient="vertical")
|
|
801
|
+
separatorv2.grid(row=row - 2, column=4, rowspan=2, sticky="ns", padx=10)
|
|
802
|
+
|
|
803
|
+
# Separator
|
|
804
|
+
separator2 = ttk.Separator(main_frame, orient="horizontal")
|
|
805
|
+
separator2.grid(row=row, column=0, columnspan=9, sticky="ew", pady=(10, 10))
|
|
806
|
+
row += 1
|
|
807
|
+
|
|
808
|
+
# Button frame
|
|
809
|
+
button_frame = tk.Frame(main_frame)
|
|
810
|
+
button_frame.grid(row=row, column=0, columnspan=9, pady=(5, 0))
|
|
811
|
+
|
|
812
|
+
# OK button
|
|
813
|
+
self.ok_button = ttk.Button(button_frame, text="OK", takefocus=True, command=self.on_ok)
|
|
814
|
+
self.ok_button.pack(side="left", padx=5)
|
|
815
|
+
|
|
816
|
+
# Cancel button
|
|
817
|
+
self.cancel_button = ttk.Button(button_frame, text="Cancel", command=self.on_cancel)
|
|
818
|
+
self.cancel_button.pack(side="left", padx=5)
|
|
819
|
+
|
|
820
|
+
# Reset button
|
|
821
|
+
self.reset_button = ttk.Button(button_frame, text="Reset", command=self.on_reset)
|
|
822
|
+
self.reset_button.pack(side="left", padx=5)
|
|
823
|
+
|
|
824
|
+
# Set focus to OK button
|
|
825
|
+
self.ok_button.focus_set()
|
|
826
|
+
|
|
827
|
+
def on_ok(self):
|
|
828
|
+
"""Validate and save the updated values"""
|
|
829
|
+
try:
|
|
830
|
+
updated_dict = {}
|
|
831
|
+
for key, info in self.param_dict.items():
|
|
832
|
+
if key in ["nruns"]:
|
|
833
|
+
continue
|
|
834
|
+
entry_value = self.entries[key].get()
|
|
835
|
+
value_type = info["type"]
|
|
836
|
+
|
|
837
|
+
# Convert string to appropriate type
|
|
838
|
+
if value_type == bool:
|
|
839
|
+
# Handle boolean conversion
|
|
840
|
+
if entry_value.lower() in ["true", "1", "yes"]:
|
|
841
|
+
converted_value = True
|
|
842
|
+
elif entry_value.lower() in ["false", "0", "no"]:
|
|
843
|
+
converted_value = False
|
|
844
|
+
else:
|
|
845
|
+
raise ValueError(f"Invalid boolean value for {key}")
|
|
846
|
+
elif value_type == str:
|
|
847
|
+
converted_value = entry_value
|
|
848
|
+
if "valid_values" in info and entry_value not in info["valid_values"]:
|
|
849
|
+
raise ValueError(f"Invalid value for {key}. Valid values are: {info['valid_values']}")
|
|
850
|
+
else:
|
|
851
|
+
converted_value = value_type(entry_value)
|
|
852
|
+
if "positive" in info and info["positive"] and converted_value < 0:
|
|
853
|
+
raise ValueError(f"Value for {key} must be positive")
|
|
854
|
+
if "intmode" in info:
|
|
855
|
+
if info["intmode"] == "odd" and converted_value % 2 == 0:
|
|
856
|
+
raise ValueError(f"Value for {key} must be an odd integer")
|
|
857
|
+
elif info["intmode"] == "even" and converted_value % 2 != 0:
|
|
858
|
+
raise ValueError(f"Value for {key} must be an even integer")
|
|
859
|
+
|
|
860
|
+
# Duplicate the parameter info and update only the value
|
|
861
|
+
# (preserving other metadata)
|
|
862
|
+
updated_dict[key] = self.param_dict[key].copy()
|
|
863
|
+
updated_dict[key]["value"] = converted_value
|
|
864
|
+
|
|
865
|
+
# Check whether any run1 and run2 parameters differ
|
|
866
|
+
nruns = 1
|
|
867
|
+
for key in self.param_dict.keys():
|
|
868
|
+
if key.startswith("run1_"):
|
|
869
|
+
parname = key[5:]
|
|
870
|
+
key2 = "run2_" + parname
|
|
871
|
+
if updated_dict[key]["value"] != updated_dict[key2]["value"]:
|
|
872
|
+
nruns = 2
|
|
873
|
+
print(
|
|
874
|
+
f"Parameter '{parname}' differs between run1 and run2: "
|
|
875
|
+
f"{updated_dict[key]['value']} (run1) vs {updated_dict[key2]['value']} (run2)"
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
# Additional validation for region limits
|
|
879
|
+
try:
|
|
880
|
+
if updated_dict["xmin"]["value"] < 1 or updated_dict["xmin"]["value"] > self.imgshape[1]:
|
|
881
|
+
raise ValueError(f"xmin must be in the range [1, {self.imgshape[1]}]")
|
|
882
|
+
if updated_dict["xmax"]["value"] < 1 or updated_dict["xmax"]["value"] > self.imgshape[1]:
|
|
883
|
+
raise ValueError(f"xmax must be in the range [1, {self.imgshape[1]}]")
|
|
884
|
+
if updated_dict["ymin"]["value"] < 1 or updated_dict["ymin"]["value"] > self.imgshape[0]:
|
|
885
|
+
raise ValueError(f"ymin must be in the range [1, {self.imgshape[0]}]")
|
|
886
|
+
if updated_dict["ymax"]["value"] < 1 or updated_dict["ymax"]["value"] > self.imgshape[0]:
|
|
887
|
+
raise ValueError(f"ymax must be in the range [1, {self.imgshape[0]}]")
|
|
888
|
+
if updated_dict["xmax"]["value"] <= updated_dict["xmin"]["value"]:
|
|
889
|
+
raise ValueError("xmax must be greater than xmin")
|
|
890
|
+
if updated_dict["ymax"]["value"] <= updated_dict["ymin"]["value"]:
|
|
891
|
+
raise ValueError("ymax must be greater than ymin")
|
|
892
|
+
self.result_dict = updated_dict
|
|
893
|
+
self.result_dict["nruns"] = {"value": nruns, "type": int, "positive": True}
|
|
894
|
+
if nruns not in [1, 2]:
|
|
895
|
+
raise ValueError("nruns must be 1 or 2")
|
|
896
|
+
self.root.destroy()
|
|
897
|
+
except ValueError as e:
|
|
898
|
+
messagebox.showerror(
|
|
899
|
+
"Invalid Inputs", "Error in region limits:\n" f"{str(e)}\n\nPlease check your inputs."
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
except ValueError as e:
|
|
903
|
+
messagebox.showerror(
|
|
904
|
+
"Invalid Inputs", f"Error converting value for {key}:\n{str(e)}\n\n" "Please check your inputs."
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
def on_cancel(self):
|
|
908
|
+
"""Close without saving"""
|
|
909
|
+
self.result_dict = None
|
|
910
|
+
self.root.destroy()
|
|
911
|
+
|
|
912
|
+
def on_reset(self):
|
|
913
|
+
"""Reset all fields to original values"""
|
|
914
|
+
self.param_dict = pycosmic_default_dict.copy()
|
|
915
|
+
self.param_dict["xmin"]["value"] = 1
|
|
916
|
+
self.param_dict["xmax"]["value"] = self.imgshape[1]
|
|
917
|
+
self.param_dict["ymin"]["value"] = 1
|
|
918
|
+
self.param_dict["ymax"]["value"] = self.imgshape[0]
|
|
919
|
+
for key, info in self.param_dict.items():
|
|
920
|
+
parname = key[5:]
|
|
921
|
+
self.entries[key].delete(0, tk.END)
|
|
922
|
+
self.entries[key].insert(0, str(info["value"]))
|
|
923
|
+
|
|
924
|
+
def get_result(self):
|
|
925
|
+
"""Return the updated dictionary"""
|
|
926
|
+
return self.result_dict
|
|
927
|
+
|
|
928
|
+
def update_colour_param_run1_run2(self):
|
|
929
|
+
"""Update the foreground color of run1 and run2 entries."""
|
|
930
|
+
# Highlight run2 parameter if different from run1
|
|
931
|
+
for key in self.param_dict.keys():
|
|
932
|
+
if key.startswith("run1_"):
|
|
933
|
+
parname = key[5:]
|
|
934
|
+
if key in self.entries and "run2_" + parname in self.entries:
|
|
935
|
+
if self.entries[key].get() != self.entries["run2_" + parname].get():
|
|
936
|
+
self.entries["run2_" + parname].config(fg="red")
|
|
937
|
+
else:
|
|
938
|
+
self.entries["run2_" + parname].config(fg="black")
|