teareduce 0.5.9__py3-none-any.whl → 0.6.1__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.
@@ -9,10 +9,14 @@
9
9
 
10
10
  """Parameter editor dialog for L.A.Cosmic parameters."""
11
11
 
12
+ from astropy.io import fits
13
+ from pathlib import Path
12
14
  import tkinter as tk
13
15
  from tkinter import ttk
16
+ from tkinter import filedialog
14
17
  from tkinter import messagebox
15
18
 
19
+ from .askextension import ask_extension_input_image
16
20
  from .centerchildparent import center_on_parent
17
21
  from .definitions import lacosmic_default_dict
18
22
 
@@ -20,7 +24,21 @@ from .definitions import lacosmic_default_dict
20
24
  class ParameterEditor:
21
25
  """A dialog to edit L.A.Cosmic parameters."""
22
26
 
23
- def __init__(self, root, param_dict, window_title, xmin, xmax, ymin, ymax, imgshape):
27
+ def __init__(
28
+ self,
29
+ root,
30
+ param_dict,
31
+ window_title,
32
+ xmin,
33
+ xmax,
34
+ ymin,
35
+ ymax,
36
+ imgshape,
37
+ inbkg=None,
38
+ extnum_inbkg=None,
39
+ invar=None,
40
+ extnum_invar=None,
41
+ ):
24
42
  """Initialize the parameter editor dialog.
25
43
 
26
44
  Parameters
@@ -45,6 +63,14 @@ class ParameterEditor:
45
63
  From 1 to NAXIS2.
46
64
  imgshape : tuple
47
65
  Shape of the image (height, width).
66
+ inbkg : str or None
67
+ Path to the input background image FITS file.
68
+ extnum_inbkg : int or None
69
+ FITS extension number for the input background image.
70
+ invar : str or None
71
+ Path to the input variance image FITS file.
72
+ extnum_invar : int or None
73
+ FITS extension number for the input variance image.
48
74
 
49
75
  Methods
50
76
  -------
@@ -82,7 +108,11 @@ class ParameterEditor:
82
108
  self.param_dict["ymax"]["value"] = ymax
83
109
  self.imgshape = imgshape
84
110
  self.entries = {"run1": {}, "run2": {}} # dictionary to hold entry widgets
85
- self.result_dict = None
111
+ self.inbkg = inbkg
112
+ self.extnum_inbkg = extnum_inbkg
113
+ self.invar = invar
114
+ self.extnum_invar = extnum_invar
115
+ self.result_dict = {}
86
116
 
87
117
  # Create the form
88
118
  self.create_widgets()
@@ -90,6 +120,11 @@ class ParameterEditor:
90
120
 
91
121
  def create_widgets(self):
92
122
  """Create the widgets for the dialog."""
123
+ # Define different styles for different conditions
124
+ style = ttk.Style()
125
+ style.configure("Normal.TCombobox", foreground="black", background="white")
126
+ style.configure("Changed.TCombobox", foreground="red", background="white")
127
+
93
128
  # Main frame
94
129
  main_frame = tk.Frame(self.root, padx=10, pady=10)
95
130
  main_frame.pack()
@@ -101,59 +136,147 @@ class ParameterEditor:
101
136
  bold_font = default_font.copy()
102
137
  bold_font.configure(weight="bold", size=default_font.cget("size") + 2)
103
138
  subtitle_label = tk.Label(main_frame, text="L.A.Cosmic Parameters", font=bold_font)
104
- subtitle_label.grid(row=row, column=0, columnspan=4, pady=(0, 10))
139
+ subtitle_label.grid(row=row, column=0, columnspan=9, pady=(0, 10))
105
140
  row += 1
106
141
 
142
+ # Count number of parameters for run1 and run2
143
+ nparams_run1 = sum(1 for key in self.param_dict.keys() if key.startswith("run1_"))
144
+ nparams_run2 = sum(1 for key in self.param_dict.keys() if key.startswith("run2_"))
145
+ if nparams_run1 != nparams_run2:
146
+ raise ValueError("Number of parameters for run1 and run2 do not match.")
147
+ nparams_input_images = len(["inbkg", "invar"])
148
+ nparams_total = nparams_input_images + nparams_run1
149
+ max_num_params_in_columns = nparams_total // 2 + nparams_total % 2
150
+
107
151
  # Create labels and entry fields for each parameter.
108
152
  bold_font_subheader = default_font.copy()
109
153
  bold_font_subheader.configure(weight="bold", size=default_font.cget("size") + 1)
110
- label = tk.Label(main_frame, text="Parameter", font=bold_font_subheader, anchor="w", fg="gray")
111
- label.grid(row=row, column=0, sticky="e", pady=0)
112
- label = tk.Label(main_frame, text="Run 1", font=bold_font_subheader, anchor="w", fg="gray", width=10)
113
- label.grid(row=row, column=1, sticky="w", padx=10, pady=0)
114
- label = tk.Label(main_frame, text="Run 2", font=bold_font_subheader, anchor="w", fg="gray", width=10)
115
- label.grid(row=row, column=2, sticky="w", padx=10, pady=0)
116
- label = tk.Label(main_frame, text="Type", font=bold_font_subheader, anchor="w", fg="gray", width=10)
117
- label.grid(row=row, column=3, sticky="w", pady=0)
154
+ for subtable in range(2):
155
+ if subtable == 0:
156
+ coloff = 0
157
+ else:
158
+ coloff = 5
159
+ label = tk.Label(main_frame, text="Parameter", font=bold_font_subheader, anchor="w", fg="gray")
160
+ label.grid(row=row, column=0 + coloff, sticky="e", pady=0)
161
+ label = tk.Label(main_frame, text="Run 1", font=bold_font_subheader, anchor="w", fg="gray", width=10)
162
+ label.grid(row=row, column=1 + coloff, sticky="w", padx=10, pady=0)
163
+ label = tk.Label(main_frame, text="Run 2", font=bold_font_subheader, anchor="w", fg="gray", width=10)
164
+ label.grid(row=row, column=2 + coloff, sticky="w", padx=10, pady=0)
165
+ label = tk.Label(main_frame, text="Type", font=bold_font_subheader, anchor="w", fg="gray", width=10)
166
+ label.grid(row=row, column=3 + coloff, sticky="w", pady=0)
118
167
  row += 1
168
+
119
169
  # Note: here we are using entry_vars to trace changes in the entries
120
170
  # so that we can update the color of run2 entries if they differ from run1.
121
171
  self.entry_vars = {}
172
+ row_subtable = 0
173
+ coloff = 0
122
174
  for key, info in self.param_dict.items():
123
175
  if not key.startswith("run1_"):
124
176
  continue
125
177
  # Parameter name label
126
178
  label = tk.Label(main_frame, text=f"{key[5:]}:", anchor="e", width=15)
127
- label.grid(row=row, column=0, sticky="w", pady=5)
179
+ label.grid(row=row, column=coloff, sticky="w", pady=5)
128
180
  # Entry field for run1
129
181
  self.entry_vars[key] = tk.StringVar()
130
- self.entry_vars[key].trace_add("write", lambda *args: self.update_colour_param_run1_run2())
131
- entry = tk.Entry(main_frame, textvariable=self.entry_vars[key], width=10)
132
- entry.insert(0, str(info["value"]))
133
- entry.grid(row=row, column=1, padx=10, pady=5)
182
+ if key[5:] in ["cleantype", "fsmode", "psfmodel"]:
183
+ entry = ttk.Combobox(
184
+ main_frame,
185
+ textvariable=self.entry_vars[key],
186
+ width=8,
187
+ state="readonly",
188
+ values=info["valid_values"],
189
+ style="Normal.TCombobox",
190
+ )
191
+ self.entry_vars[key].set(str(info["value"]))
192
+ entry.bind("<<ComboboxSelected>>", lambda e: self.update_colour_param_run1_run2())
193
+ else:
194
+ self.entry_vars[key].trace_add("write", lambda *args: self.update_colour_param_run1_run2())
195
+ entry = tk.Entry(main_frame, textvariable=self.entry_vars[key], width=10)
196
+ entry.insert(0, str(info["value"]))
197
+ entry.grid(row=row, column=1 + coloff, padx=10, pady=5)
134
198
  self.entries[key] = entry # dictionary to hold entry widgets
135
199
  # Entry field for run2
136
200
  key2 = "run2_" + key[5:]
137
201
  self.entry_vars[key2] = tk.StringVar()
138
- self.entry_vars[key2].trace_add("write", lambda *args: self.update_colour_param_run1_run2())
139
- entry = tk.Entry(main_frame, textvariable=self.entry_vars[key2], width=10)
140
- entry.insert(0, str(self.param_dict[key2]["value"]))
141
- entry.grid(row=row, column=2, padx=10, pady=5)
202
+ if key[5:] in ["cleantype", "fsmode", "psfmodel"]:
203
+ entry = ttk.Combobox(
204
+ main_frame,
205
+ textvariable=self.entry_vars[key2],
206
+ width=8,
207
+ state="readonly",
208
+ values=info["valid_values"],
209
+ style="Normal.TCombobox",
210
+ )
211
+ self.entry_vars[key2].set(str(self.param_dict[key2]["value"]))
212
+ entry.bind("<<ComboboxSelected>>", lambda e: self.update_colour_param_run1_run2())
213
+ else:
214
+ self.entry_vars[key2].trace_add("write", lambda *args: self.update_colour_param_run1_run2())
215
+ entry = tk.Entry(main_frame, textvariable=self.entry_vars[key2], width=10)
216
+ entry.insert(0, str(self.param_dict[key2]["value"]))
217
+ entry.grid(row=row, column=2 + coloff, padx=10, pady=5)
142
218
  self.entries["run2_" + key[5:]] = entry # dictionary to hold entry widgets
143
219
  # Type label
144
- type_label = tk.Label(main_frame, text=f"({info['type'].__name__})", fg="gray", anchor="w", width=10)
145
- type_label.grid(row=row, column=3, sticky="w", pady=5)
220
+ infotext = info["type"].__name__
221
+ if infotext == "int":
222
+ if "intmode" in info:
223
+ if info["intmode"] == "odd":
224
+ infotext += ", odd"
225
+ elif info["intmode"] == "even":
226
+ infotext += ", even"
227
+ type_label = tk.Label(main_frame, text=f"({infotext})", fg="gray", anchor="w", width=10)
228
+ type_label.grid(row=row, column=3 + coloff, sticky="w", pady=5)
229
+ row_subtable += 1
230
+ if row_subtable == max_num_params_in_columns:
231
+ coloff = 5
232
+ row -= max_num_params_in_columns
146
233
  row += 1
147
- # self.update_colour_param_run1_run2()
234
+
235
+ # Auxiliary images
236
+ label = tk.Label(main_frame, text="inbkg:", anchor="e", width=15)
237
+ label.grid(row=row, column=coloff, sticky="w", pady=5)
238
+ if self.inbkg is None:
239
+ self.filename_inbkg = tk.StringVar(value="None")
240
+ else:
241
+ self.filename_inbkg = tk.StringVar(value=str(Path(self.inbkg).name + f"[{self.extnum_inbkg}]"))
242
+ file_inbkg_label = tk.Label(
243
+ main_frame, textvariable=self.filename_inbkg, fg="blue", bg="white", cursor="hand2", anchor="w", width=40
244
+ )
245
+ file_inbkg_label.grid(row=row, column=coloff + 1, columnspan=4, sticky="w", padx=10, pady=5)
246
+ file_inbkg_label.bind("<Button-1>", lambda e: self.define_inbkg())
247
+ row += 1
248
+
249
+ label = tk.Label(main_frame, text="invar:", anchor="e", width=15)
250
+ label.grid(row=row, column=coloff, sticky="w", pady=5)
251
+ if self.invar is None:
252
+ self.filename_invar = tk.StringVar(value="None")
253
+ else:
254
+ self.filename_invar = tk.StringVar(value=str(Path(self.invar).name + f"[{self.extnum_invar}]"))
255
+ file_invar_label = tk.Label(
256
+ main_frame, textvariable=self.filename_invar, fg="blue", bg="white", cursor="hand2", anchor="w", width=40
257
+ )
258
+ file_invar_label.grid(row=row, column=coloff + 1, columnspan=4, sticky="w", padx=10, pady=5)
259
+ file_invar_label.bind("<Button-1>", lambda e: self.define_invar())
260
+ row += 1
261
+
262
+ # Adjust row if odd number of parameters
263
+ if nparams_total % 2 != 0:
264
+ row += nparams_total % 2
265
+
266
+ # Vertical separator between splitted table
267
+ separatorv1 = ttk.Separator(main_frame, orient="vertical")
268
+ separatorv1.grid(
269
+ row=row - max_num_params_in_columns, column=4, rowspan=max_num_params_in_columns, sticky="ns", padx=10
270
+ )
148
271
 
149
272
  # Separator
150
273
  separator1 = ttk.Separator(main_frame, orient="horizontal")
151
- separator1.grid(row=row, column=0, columnspan=4, sticky="ew", pady=(10, 10))
274
+ separator1.grid(row=row, column=0, columnspan=9, sticky="ew", pady=(10, 10))
152
275
  row += 1
153
276
 
154
277
  # Subtitle for additional parameters
155
278
  subtitle_label = tk.Label(main_frame, text="Additional Parameters", font=bold_font)
156
- subtitle_label.grid(row=row, column=0, columnspan=4, pady=(0, 10))
279
+ subtitle_label.grid(row=row, column=0, columnspan=9, pady=(0, 10))
157
280
  row += 1
158
281
 
159
282
  # Dilation label and entry
@@ -167,28 +290,31 @@ class ParameterEditor:
167
290
  main_frame, text=f"({self.param_dict['dilation']['type'].__name__})", fg="gray", anchor="w", width=10
168
291
  )
169
292
  type_label.grid(row=row, column=2, sticky="w", pady=5)
170
- row += 1
171
293
 
172
294
  label = tk.Label(main_frame, text="Border Padding:", anchor="e", width=15)
173
- label.grid(row=row, column=0, sticky="w", pady=5)
295
+ label.grid(row=row, column=5, sticky="w", pady=5)
174
296
  entry = tk.Entry(main_frame, width=10)
175
297
  entry.insert(0, str(self.param_dict["borderpadd"]["value"]))
176
- entry.grid(row=row, column=1, padx=10, pady=5)
298
+ entry.grid(row=row, column=6, padx=10, pady=5)
177
299
  self.entries["borderpadd"] = entry
178
300
  type_label = tk.Label(
179
301
  main_frame, text=f"({self.param_dict['borderpadd']['type'].__name__})", fg="gray", anchor="w", width=10
180
302
  )
181
- type_label.grid(row=row, column=2, sticky="w", pady=5)
303
+ type_label.grid(row=row, column=7, sticky="w", pady=5)
182
304
  row += 1
183
305
 
306
+ # Vertical separator
307
+ separatorv2 = ttk.Separator(main_frame, orient="vertical")
308
+ separatorv2.grid(row=row - 1, column=4, rowspan=1, sticky="ns", padx=10)
309
+
184
310
  # Separator
185
311
  separator2 = ttk.Separator(main_frame, orient="horizontal")
186
- separator2.grid(row=row, column=0, columnspan=4, sticky="ew", pady=(10, 10))
312
+ separator2.grid(row=row, column=0, columnspan=9, sticky="ew", pady=(10, 10))
187
313
  row += 1
188
314
 
189
315
  # Subtitle for region to be examined
190
316
  subtitle_label = tk.Label(main_frame, text="Region to be Examined", font=bold_font)
191
- subtitle_label.grid(row=row, column=0, columnspan=4, pady=(0, 10))
317
+ subtitle_label.grid(row=row, column=0, columnspan=9, pady=(0, 10))
192
318
  row += 1
193
319
 
194
320
  # Region to be examined label and entries
@@ -196,11 +322,15 @@ class ParameterEditor:
196
322
  if key.lower() in ["xmin", "xmax", "ymin", "ymax"]:
197
323
  # Parameter name label
198
324
  label = tk.Label(main_frame, text=f"{key}:", anchor="e", width=15)
199
- label.grid(row=row, column=0, sticky="w", pady=5)
325
+ if key.lower() in ["xmin", "xmax"]:
326
+ coloff = 0
327
+ else:
328
+ coloff = 5
329
+ label.grid(row=row, column=coloff, sticky="w", pady=5)
200
330
  # Entry field
201
331
  entry = tk.Entry(main_frame, width=10)
202
332
  entry.insert(0, str(info["value"]))
203
- entry.grid(row=row, column=1, padx=10, pady=5)
333
+ entry.grid(row=row, column=coloff + 1, padx=10, pady=5)
204
334
  self.entries[key] = entry # dictionary to hold entry widgets
205
335
  # Type label
206
336
  dumtext = f"({info['type'].__name__})"
@@ -209,37 +339,100 @@ class ParameterEditor:
209
339
  else:
210
340
  dumtext += f" --> [1, {self.imgshape[0]}]"
211
341
  type_label = tk.Label(main_frame, text=dumtext, fg="gray", anchor="w", width=15)
212
- type_label.grid(row=row, column=2, sticky="w", pady=5)
213
- row += 1
342
+ type_label.grid(row=row, column=coloff + 2, sticky="w", pady=5)
343
+ if key.lower() == "xmax":
344
+ row -= 1
345
+ else:
346
+ row += 1
347
+
348
+ # Vertical separator
349
+ separatorv3 = ttk.Separator(main_frame, orient="vertical")
350
+ separatorv3.grid(row=row - 2, column=4, rowspan=2, sticky="ns", padx=10)
214
351
 
215
352
  # Separator
216
353
  separator3 = ttk.Separator(main_frame, orient="horizontal")
217
- separator3.grid(row=row, column=0, columnspan=4, sticky="ew", pady=(10, 10))
354
+ separator3.grid(row=row, column=0, columnspan=9, sticky="ew", pady=(10, 10))
218
355
  row += 1
219
356
 
220
357
  # Button frame
221
358
  button_frame = tk.Frame(main_frame)
222
- button_frame.grid(row=row, column=0, columnspan=4, pady=(5, 0))
359
+ button_frame.grid(row=row, column=0, columnspan=9, pady=(5, 0))
223
360
 
224
361
  # OK button
225
- ok_button = tk.Button(button_frame, text="OK", width=5, command=self.on_ok)
226
- ok_button.pack(side="left", padx=5)
362
+ self.ok_button = ttk.Button(button_frame, text="OK", takefocus=True, command=self.on_ok)
363
+ self.ok_button.pack(side="left", padx=5)
227
364
 
228
365
  # Cancel button
229
- cancel_button = tk.Button(button_frame, text="Cancel", width=5, command=self.on_cancel)
230
- cancel_button.pack(side="left", padx=5)
366
+ self.cancel_button = ttk.Button(button_frame, text="Cancel", command=self.on_cancel)
367
+ self.cancel_button.pack(side="left", padx=5)
231
368
 
232
369
  # Reset button
233
- reset_button = tk.Button(button_frame, text="Reset", width=5, command=self.on_reset)
234
- reset_button.pack(side="left", padx=5)
370
+ self.reset_button = ttk.Button(button_frame, text="Reset", command=self.on_reset)
371
+ self.reset_button.pack(side="left", padx=5)
372
+
373
+ # Set focus to OK button
374
+ self.ok_button.focus_set()
375
+
376
+ def define_inbkg(self):
377
+ """Define the input background image."""
378
+ self.inbkg = filedialog.askopenfilename(
379
+ parent=self.root,
380
+ title="Select FITS file to be used as input background image",
381
+ filetypes=[("FITS files", "*.fits *.fit *.fts"), ("All files", "*.*")],
382
+ )
383
+ if not self.inbkg:
384
+ self.inbkg = None
385
+ if isinstance(self.inbkg, str):
386
+ self.inbkg = self.inbkg.strip()
387
+
388
+ if self.inbkg in ["", None]:
389
+ self.inbkg = None
390
+ self.filename_inbkg.set("None")
391
+ self.extnum_inbkg = None
392
+ return
393
+ else:
394
+ self.extnum_inbkg = ask_extension_input_image(self.inbkg, self.imgshape)
395
+ if self.extnum_inbkg is None:
396
+ self.inbkg = None
397
+ else:
398
+ self.filename_inbkg.set(str(Path(self.inbkg).name + f"[{self.extnum_inbkg}]"))
399
+
400
+ def define_invar(self):
401
+ """Define the input variance image."""
402
+ self.invar = filedialog.askopenfilename(
403
+ parent=self.root,
404
+ title="Select FITS file to be used as input variance image",
405
+ filetypes=[("FITS files", "*.fits *.fit *.fts"), ("All files", "*.*")],
406
+ )
407
+ if not self.invar:
408
+ self.invar = None
409
+ if isinstance(self.invar, str):
410
+ self.invar = self.invar.strip()
411
+
412
+ if self.invar in ["", None]:
413
+ self.invar = None
414
+ self.filename_invar.set("None")
415
+ self.extnum_invar = None
416
+ return
417
+ else:
418
+ self.extnum_invar = ask_extension_input_image(self.invar, self.imgshape)
419
+ if self.extnum_invar is None:
420
+ self.invar = None
421
+ else:
422
+ self.filename_invar.set(str(Path(self.invar).name + f"[{self.extnum_invar}]"))
235
423
 
236
424
  def on_ok(self):
237
425
  """Validate and save the updated values"""
238
426
  try:
239
- updated_dict = {}
427
+ updated_dict = {
428
+ "inbkg": {"value": self.inbkg, "type": str},
429
+ "extnum_inbkg": {"value": self.extnum_inbkg, "type": int},
430
+ "invar": {"value": self.invar, "type": str},
431
+ "extnum_invar": {"value": self.extnum_invar, "type": int},
432
+ }
240
433
 
241
434
  for key, info in self.param_dict.items():
242
- if key == "nruns":
435
+ if key in ["nruns", "inbkg", "extnum_inbkg", "invar", "extnum_invar"]:
243
436
  continue
244
437
  entry_value = self.entries[key].get()
245
438
  value_type = info["type"]
@@ -253,12 +446,24 @@ class ParameterEditor:
253
446
  converted_value = False
254
447
  else:
255
448
  raise ValueError(f"Invalid boolean value for {key}")
449
+ elif value_type == str:
450
+ converted_value = entry_value
451
+ if "valid_values" in info and entry_value not in info["valid_values"]:
452
+ raise ValueError(f"Invalid value for {key}. Valid values are: {info['valid_values']}")
256
453
  else:
257
454
  converted_value = value_type(entry_value)
258
455
  if "positive" in info and info["positive"] and converted_value < 0:
259
456
  raise ValueError(f"Value for {key} must be positive")
457
+ if "intmode" in info:
458
+ if info["intmode"] == "odd" and converted_value % 2 == 0:
459
+ raise ValueError(f"Value for {key} must be an odd integer")
460
+ elif info["intmode"] == "even" and converted_value % 2 != 0:
461
+ raise ValueError(f"Value for {key} must be an even integer")
260
462
 
261
- updated_dict[key] = {"value": converted_value, "type": value_type}
463
+ # Duplicate the parameter info and update only the value
464
+ # (preserving other metadata)
465
+ updated_dict[key] = self.param_dict[key].copy()
466
+ updated_dict[key]["value"] = converted_value
262
467
 
263
468
  # Check whether any run1 and run2 parameters differ
264
469
  nruns = 1
@@ -306,11 +511,21 @@ class ParameterEditor:
306
511
  self.param_dict["xmax"]["value"] = self.imgshape[1]
307
512
  self.param_dict["ymin"]["value"] = 1
308
513
  self.param_dict["ymax"]["value"] = self.imgshape[0]
514
+ self.inbkg = None
515
+ self.extnum_inbkg = None
516
+ self.filename_inbkg.set("None")
517
+ self.invar = None
518
+ self.extnum_invar = None
519
+ self.filename_invar.set("None")
309
520
  for key, info in self.param_dict.items():
310
- if key == "nruns":
521
+ if key in ["nruns", "inbkg", "extnum_inbkg", "invar", "extnum_invar"]:
311
522
  continue
312
- self.entries[key].delete(0, tk.END)
313
- self.entries[key].insert(0, str(info["value"]))
523
+ parname = key[5:]
524
+ if parname in ["cleantype", "fsmode", "psfmodel"]:
525
+ self.entries[key].set(str(info["value"]))
526
+ else:
527
+ self.entries[key].delete(0, tk.END)
528
+ self.entries[key].insert(0, str(info["value"]))
314
529
 
315
530
  def get_result(self):
316
531
  """Return the updated dictionary"""
@@ -324,6 +539,19 @@ class ParameterEditor:
324
539
  parname = key[5:]
325
540
  if key in self.entries and "run2_" + parname in self.entries:
326
541
  if self.entries[key].get() != self.entries["run2_" + parname].get():
327
- self.entries["run2_" + parname].config(fg="red")
542
+ if parname in ["cleantype", "fsmode", "psfmodel"]:
543
+ self.entries["run2_" + parname].configure(style="Changed.TCombobox")
544
+ else:
545
+ self.entries["run2_" + parname].config(fg="red")
328
546
  else:
329
- self.entries["run2_" + parname].config(fg="black")
547
+ if parname in ["cleantype", "fsmode", "psfmodel"]:
548
+ self.entries["run2_" + parname].configure(style="Normal.TCombobox")
549
+ else:
550
+ self.entries["run2_" + parname].config(fg="black")
551
+ # Remove the highlight after choosing an option from the dropdown
552
+ # (to see the color change immediately)
553
+ if parname in ["cleantype", "fsmode", "psfmodel"]:
554
+ if "run_1_" + parname in self.entries:
555
+ self.entries["run1_" + parname].selection_clear()
556
+ if "run2_" + parname in self.entries:
557
+ self.entries["run2_" + parname].selection_clear()