teareduce 0.5.9__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,15 +28,19 @@ import numpy as np
28
28
  from rich import print
29
29
 
30
30
  from .centerchildparent import center_on_parent
31
+ from .definitions import DEFAULT_TK_WINDOW_SIZE_X
32
+ from .definitions import DEFAULT_TK_WINDOW_SIZE_Y
31
33
  from .definitions import MAX_PIXEL_DISTANCE_TO_CR
32
34
  from .imagedisplay import ImageDisplay
33
35
  from .interpolation_a import interpolation_a
34
36
  from .interpolation_x import interpolation_x
35
37
  from .interpolation_y import interpolation_y
38
+ from .trackedbutton import TrackedTkButton
36
39
 
37
40
  from ..imshow import imshow
38
41
  from ..sliceregion import SliceRegion2D
39
42
  from ..zscale import zscale
43
+ from ..version import VERSION
40
44
 
41
45
  import matplotlib
42
46
 
@@ -49,6 +53,8 @@ class ReviewCosmicRay(ImageDisplay):
49
53
  def __init__(
50
54
  self,
51
55
  root,
56
+ root_width,
57
+ root_height,
52
58
  data,
53
59
  auxdata,
54
60
  cleandata_lacosmic,
@@ -59,6 +65,10 @@ class ReviewCosmicRay(ImageDisplay):
59
65
  last_dilation=None,
60
66
  last_npoints=None,
61
67
  last_degree=None,
68
+ last_maskfill_size=None,
69
+ last_maskfill_operator=None,
70
+ last_maskfill_smooth=None,
71
+ last_maskfill_verbose=None,
62
72
  ):
63
73
  """Initialize the review window.
64
74
 
@@ -66,6 +76,10 @@ class ReviewCosmicRay(ImageDisplay):
66
76
  ----------
67
77
  root : tk.Toplevel
68
78
  The parent Tkinter root window.
79
+ root_width : int
80
+ The width of the root window. The review window is scaled accordingly.
81
+ root_height : int
82
+ The height of the root window. The review window is scaled accordingly.
69
83
  data : 2D numpy array
70
84
  The original image data.
71
85
  auxdata : 2D numpy array or None
@@ -90,6 +104,14 @@ class ReviewCosmicRay(ImageDisplay):
90
104
  The last used number of points parameter for interpolation.
91
105
  last_degree : int or None, optional
92
106
  The last used degree parameter for interpolation.
107
+ last_maskfill_size : int or None, optional
108
+ The last used maskfill size parameter.
109
+ last_maskfill_operator : str or None, optional
110
+ The last used maskfill operator parameter.
111
+ last_maskfill_smooth : bool or None, optional
112
+ The last used maskfill smooth parameter.
113
+ last_maskfill_verbose : bool or None, optional
114
+ The last used maskfill verbose parameter.
93
115
 
94
116
  Methods
95
117
  -------
@@ -126,6 +148,10 @@ class ReviewCosmicRay(ImageDisplay):
126
148
  ----------
127
149
  root : tk.Toplevel
128
150
  The parent Tkinter root window.
151
+ factor_width : float
152
+ The scaling factor for the width of the review window.
153
+ factor_height : float
154
+ The scaling factor for the height of the review window.
129
155
  data : 2D numpy array
130
156
  The original image data.
131
157
  auxdata : 2D numpy array or None
@@ -146,19 +172,33 @@ class ReviewCosmicRay(ImageDisplay):
146
172
  Degree parameter for interpolation.
147
173
  npoints : int
148
174
  Number of points parameter for interpolation.
175
+ maskfill_size : int
176
+ The last used maskfill size parameter.
177
+ maskfill_operator : str
178
+ The last used maskfill operator parameter.
179
+ maskfill_smooth : bool
180
+ The last used maskfill smooth parameter.
181
+ maskfill_verbose : bool
182
+ The last used maskfill verbose parameter.
149
183
  last_dilation : int or None
150
184
  The last used dilation parameter employed after L.A.Cosmic
151
185
  detection.
152
186
  """
153
187
  self.root = root
154
- self.root.title("Review Cosmic Rays")
188
+ self.root.title(f"Review Cosmic Rays (TEA version {VERSION})")
189
+ self.factor_width = root_width / DEFAULT_TK_WINDOW_SIZE_X
190
+ self.factor_height = root_height / DEFAULT_TK_WINDOW_SIZE_Y
155
191
  self.auxdata = auxdata
156
192
  if self.auxdata is not None:
157
193
  # self.root.geometry("1000x700+100+100") # This does not work in Fedora
158
- self.root.minsize(1000, 700)
194
+ window_width = int(1000 * self.factor_width + 0.5)
195
+ window_height = int(700 * self.factor_height + 0.5)
196
+ self.root.minsize(window_width, window_height)
159
197
  else:
160
- # self.root.geometry("800x700+100+100") # This does not work in Fedora
161
- self.root.minsize(900, 700)
198
+ # self.root.geometry("900x700+100+100") # This does not work in Fedora
199
+ window_width = int(900 * self.factor_width + 0.5)
200
+ window_height = int(700 * self.factor_height + 0.5)
201
+ self.root.minsize(window_width, window_height)
162
202
  self.root.update_idletasks()
163
203
  self.root.geometry("+100+100")
164
204
  self.data = data
@@ -171,6 +211,10 @@ class ReviewCosmicRay(ImageDisplay):
171
211
  self.first_plot = True
172
212
  self.degree = last_degree if last_degree is not None else 1
173
213
  self.npoints = last_npoints if last_npoints is not None else 2
214
+ self.maskfill_size = last_maskfill_size if last_maskfill_size is not None else 3
215
+ self.maskfill_operator = last_maskfill_operator if last_maskfill_operator is not None else "median"
216
+ self.maskfill_smooth = last_maskfill_smooth if last_maskfill_smooth is not None else True
217
+ self.maskfill_verbose = last_maskfill_verbose if last_maskfill_verbose is not None else False
174
218
  self.last_dilation = last_dilation
175
219
  # Make a copy of the original labels to allow pixel re-marking
176
220
  self.cr_labels_original = self.cr_labels.copy()
@@ -187,29 +231,74 @@ class ReviewCosmicRay(ImageDisplay):
187
231
 
188
232
  def create_widgets(self):
189
233
  """Create the GUI widgets for the review window."""
234
+ # Define instance of TrackedTkButton, that facilitates to show help information
235
+ # for each button displayed in the current application window.
236
+ tkbutton = TrackedTkButton(self.root)
237
+
190
238
  # Row 1 of buttons
191
239
  self.button_frame1 = tk.Frame(self.root)
192
240
  self.button_frame1.pack(pady=5)
193
- self.ndeg_label = tk.Button(
194
- self.button_frame1, text=f"Npoints={self.npoints}, Degree={self.degree}", command=self.set_ndeg
241
+ self.ndeg_label = tkbutton.new(
242
+ self.button_frame1,
243
+ text=f"Npoints={self.npoints}, Degree={self.degree}",
244
+ command=self.set_ndeg,
245
+ help_text="Set the Npoints and Degree parameters for interpolation.",
246
+ alttext="Npoints=?, Degree=?",
195
247
  )
196
248
  self.ndeg_label.pack(side=tk.LEFT, padx=5)
197
- self.remove_crosses_button = tk.Button(self.button_frame1, text="remove all x's", command=self.remove_crosses)
249
+ self.maskfill_button = tkbutton.new(
250
+ self.button_frame1,
251
+ text="Maskfill params.",
252
+ command=self.set_maskfill_params,
253
+ help_text="Set the parameters for the maskfill method.",
254
+ )
255
+ self.maskfill_button.pack(side=tk.LEFT, padx=5)
256
+ self.remove_crosses_button = tkbutton.new(
257
+ self.button_frame1,
258
+ text="remove all x's",
259
+ command=self.remove_crosses,
260
+ help_text="Remove all cross marks from the image.",
261
+ )
198
262
  self.remove_crosses_button.pack(side=tk.LEFT, padx=5)
199
- self.restore_cr_button = tk.Button(self.button_frame1, text="[r]estore CR data", command=self.restore_cr)
263
+ self.restore_cr_button = tkbutton.new(
264
+ self.button_frame1,
265
+ text="restore CR",
266
+ command=self.restore_cr,
267
+ help_text="Restore current cosmic ray pixels to their original values.",
268
+ )
200
269
  self.restore_cr_button.pack(side=tk.LEFT, padx=5)
201
270
  self.restore_cr_button.config(state=tk.DISABLED)
202
- self.next_button = tk.Button(self.button_frame1, text="[c]ontinue", command=self.continue_cr)
271
+ self.next_button = tkbutton.new(
272
+ self.button_frame1,
273
+ text="[c]ontinue",
274
+ command=self.continue_cr,
275
+ help_text="Continue to the next cosmic ray.",
276
+ )
203
277
  self.next_button.pack(side=tk.LEFT, padx=5)
204
- self.exit_button = tk.Button(self.button_frame1, text="[e]xit review", command=self.exit_review)
278
+ self.exit_button = tkbutton.new(
279
+ self.button_frame1,
280
+ text="[e]xit review",
281
+ command=self.exit_review,
282
+ help_text="Exit the cosmic ray review process.",
283
+ )
205
284
  self.exit_button.pack(side=tk.LEFT, padx=5)
206
285
 
207
286
  # Row 2 of buttons
208
287
  self.button_frame2 = tk.Frame(self.root)
209
288
  self.button_frame2.pack(pady=5)
210
- self.interp_x_button = tk.Button(self.button_frame2, text="[x] interp.", command=self.interp_x)
289
+ self.interp_x_button = tkbutton.new(
290
+ self.button_frame2,
291
+ text="[x] interp.",
292
+ command=self.interp_x,
293
+ help_text="Perform X-interpolation for the current cosmic ray.",
294
+ )
211
295
  self.interp_x_button.pack(side=tk.LEFT, padx=5)
212
- self.interp_y_button = tk.Button(self.button_frame2, text="[y] interp.", command=self.interp_y)
296
+ self.interp_y_button = tkbutton.new(
297
+ self.button_frame2,
298
+ text="[y] interp.",
299
+ command=self.interp_y,
300
+ help_text="Perform Y-interpolation for the current cosmic ray.",
301
+ )
213
302
  self.interp_y_button.pack(side=tk.LEFT, padx=5)
214
303
  # it is important to use lambda here to pass the method argument correctly
215
304
  # (avoiding the execution of the function at button creation time, which would happen
@@ -218,23 +307,51 @@ class ReviewCosmicRay(ImageDisplay):
218
307
  # the function is trying to deactivate the buttons before they are created, which
219
308
  # would lead to an error; in addition, since I have two buttons calling the same function
220
309
  # with different arguments, using lambda allows to differentiate them)
221
- self.interp_s_button = tk.Button(
222
- self.button_frame2, text="[s]urface interp.", command=lambda: self.interp_a("surface")
310
+ self.interp_s_button = tkbutton.new(
311
+ self.button_frame2,
312
+ text="[s]urface interp.",
313
+ command=lambda: self.interp_a("surface"),
314
+ help_text="Perform surface interpolation for the current cosmic ray.",
223
315
  )
224
316
  self.interp_s_button.pack(side=tk.LEFT, padx=5)
225
- self.interp_d_button = tk.Button(self.button_frame2, text="me[d]ian", command=lambda: self.interp_a("median"))
317
+ self.interp_d_button = tkbutton.new(
318
+ self.button_frame2,
319
+ text="me[d]ian",
320
+ command=lambda: self.interp_a("median"),
321
+ help_text="Perform median interpolation for the current cosmic ray.",
322
+ )
226
323
  self.interp_d_button.pack(side=tk.LEFT, padx=5)
227
- self.interp_m_button = tk.Button(self.button_frame2, text="[m]ean", command=lambda: self.interp_a("mean"))
324
+ self.interp_m_button = tkbutton.new(
325
+ self.button_frame2,
326
+ text="[m]ean",
327
+ command=lambda: self.interp_a("mean"),
328
+ help_text="Perform mean interpolation for the current cosmic ray.",
329
+ )
228
330
  self.interp_m_button.pack(side=tk.LEFT, padx=5)
229
- self.interp_l_button = tk.Button(self.button_frame2, text="[l]acosmic", command=self.use_lacosmic)
331
+ self.interp_l_button = tkbutton.new(
332
+ self.button_frame2,
333
+ text="[l]acosmic",
334
+ command=self.use_lacosmic,
335
+ help_text="Use L.A.Cosmic interpolation for the current cosmic ray.",
336
+ )
230
337
  self.interp_l_button.pack(side=tk.LEFT, padx=5)
231
338
  if self.last_dilation is not None and self.last_dilation > 0:
232
339
  self.interp_l_button.config(state=tk.DISABLED)
233
340
  if self.cleandata_lacosmic is None:
234
341
  self.interp_l_button.config(state=tk.DISABLED)
235
- self.interp_maskfill_button = tk.Button(self.button_frame2, text="mas[k]fill", command=self.use_maskfill)
342
+ self.interp_maskfill_button = tkbutton.new(
343
+ self.button_frame2,
344
+ text="mask[f]ill",
345
+ command=self.use_maskfill,
346
+ help_text="Perform maskfill interpolation for the current cosmic ray.",
347
+ )
236
348
  self.interp_maskfill_button.pack(side=tk.LEFT, padx=5)
237
- self.interp_aux_button = tk.Button(self.button_frame2, text="[a]ux. data", command=self.use_auxdata)
349
+ self.interp_aux_button = tkbutton.new(
350
+ self.button_frame2,
351
+ text="[a]ux. data",
352
+ command=self.use_auxdata,
353
+ help_text="Use auxiliary data for interpolation of the current cosmic ray.",
354
+ )
238
355
  self.interp_aux_button.pack(side=tk.LEFT, padx=5)
239
356
  if self.auxdata is None:
240
357
  self.interp_aux_button.config(state=tk.DISABLED)
@@ -243,20 +360,53 @@ class ReviewCosmicRay(ImageDisplay):
243
360
  self.button_frame3 = tk.Frame(self.root)
244
361
  self.button_frame3.pack(pady=5)
245
362
  vmin, vmax = zscale(self.data)
246
- self.vmin_button = tk.Button(self.button_frame3, text=f"vmin: {vmin:.2f}", command=self.set_vmin)
363
+ self.vmin_button = tkbutton.new(
364
+ self.button_frame3,
365
+ text=f"vmin: {vmin:.2f}",
366
+ command=self.set_vmin,
367
+ help_text="Set the minimum value for the display scale.",
368
+ alttext="vmin: ??",
369
+ )
247
370
  self.vmin_button.pack(side=tk.LEFT, padx=5)
248
- self.vmax_button = tk.Button(self.button_frame3, text=f"vmax: {vmax:.2f}", command=self.set_vmax)
371
+ self.vmax_button = tkbutton.new(
372
+ self.button_frame3,
373
+ text=f"vmax: {vmax:.2f}",
374
+ command=self.set_vmax,
375
+ help_text="Set the maximum value for the display scale.",
376
+ alttext="vmax: ??",
377
+ )
249
378
  self.vmax_button.pack(side=tk.LEFT, padx=5)
250
- self.set_minmax_button = tk.Button(self.button_frame3, text="minmax [,]", command=self.set_minmax)
379
+ self.set_minmax_button = tkbutton.new(
380
+ self.button_frame3,
381
+ text="minmax [,]",
382
+ command=self.set_minmax,
383
+ help_text="Set the display scale to the minimum and maximum data values.",
384
+ )
251
385
  self.set_minmax_button.pack(side=tk.LEFT, padx=5)
252
- self.set_zscale_button = tk.Button(self.button_frame3, text="zscale [/]", command=self.set_zscale)
386
+ self.set_zscale_button = tkbutton.new(
387
+ self.button_frame3,
388
+ text="zscale [/]",
389
+ command=self.set_zscale,
390
+ help_text="Set the display scale using zscale.",
391
+ )
253
392
  self.set_zscale_button.pack(side=tk.LEFT, padx=5)
393
+ self.help_button = tkbutton.new(
394
+ self.button_frame3,
395
+ text="Help",
396
+ command=tkbutton.show_help,
397
+ help_text="Show help information for all buttons.",
398
+ )
399
+ self.help_button.pack(side=tk.LEFT, padx=5)
254
400
 
255
401
  # Figure
256
402
  if self.auxdata is not None:
257
- self.fig, (self.ax_aux, self.ax) = plt.subplots(ncols=2, figsize=(11, 5.5), constrained_layout=True)
403
+ self.fig, (self.ax, self.ax_aux) = plt.subplots(
404
+ ncols=2, figsize=(11 * self.factor_width, 5.5 * self.factor_height), constrained_layout=True
405
+ )
258
406
  else:
259
- self.fig, self.ax = plt.subplots(figsize=(9, 5.5), constrained_layout=True)
407
+ self.fig, self.ax = plt.subplots(
408
+ figsize=(9 * self.factor_width, 5.5 * self.factor_height), constrained_layout=True
409
+ )
260
410
  self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
261
411
  self.canvas.get_tk_widget().pack(padx=5, pady=5)
262
412
  # The next two instructions prevent a segmentation fault when pressing "q"
@@ -376,6 +526,61 @@ class ReviewCosmicRay(ImageDisplay):
376
526
  self.npoints = new_npoints
377
527
  self.ndeg_label.config(text=f"Npoints={self.npoints}, Degree={self.degree}")
378
528
 
529
+ def set_maskfill_params(self):
530
+ """Set the maskfill parameters."""
531
+ new_size = simpledialog.askinteger(
532
+ "Set Maskfill Size", "Enter Maskfill Size (odd integer >=1):", initialvalue=self.maskfill_size, minvalue=1
533
+ )
534
+ if new_size is None:
535
+ return
536
+ if new_size % 2 == 0:
537
+ messagebox.showerror("Input Error", "Maskfill size must be an odd integer.")
538
+ return
539
+ new_operator = simpledialog.askstring(
540
+ "Set Maskfill Operator",
541
+ "Enter Maskfill Operator ('median' or 'mean'):",
542
+ initialvalue=self.maskfill_operator,
543
+ )
544
+ if new_operator is None:
545
+ return
546
+ if new_operator not in ["median", "mean"]:
547
+ messagebox.showerror("Input Error", "Maskfill operator must be 'median' or 'mean'.")
548
+ return
549
+ smooth_str = simpledialog.askstring(
550
+ "Set Maskfill Smooth",
551
+ "Enter Maskfill Smooth ('True' or 'False'):",
552
+ initialvalue=str(self.maskfill_smooth),
553
+ )
554
+ if smooth_str is None:
555
+ return
556
+ smooth_str = smooth_str.strip().lower()
557
+ if smooth_str == "true":
558
+ new_smooth = True
559
+ elif smooth_str == "false":
560
+ new_smooth = False
561
+ else:
562
+ messagebox.showerror("Input Error", "Maskfill Smooth must be 'True' or 'False'.")
563
+ return
564
+ verbose_str = simpledialog.askstring(
565
+ "Set Maskfill Verbose",
566
+ "Enter Maskfill Verbose ('True' or 'False'):",
567
+ initialvalue=str(self.maskfill_verbose),
568
+ )
569
+ if verbose_str is None:
570
+ return
571
+ verbose_str = verbose_str.strip().lower()
572
+ if verbose_str == "true":
573
+ new_verbose = True
574
+ elif verbose_str == "false":
575
+ new_verbose = False
576
+ else:
577
+ messagebox.showerror("Input Error", "Maskfill Verbose must be 'True' or 'False'.")
578
+ return
579
+ self.maskfill_size = new_size
580
+ self.maskfill_operator = new_operator
581
+ self.maskfill_smooth = new_smooth
582
+ self.maskfill_verbose = new_verbose
583
+
379
584
  def set_buttons_after_cleaning_cr(self):
380
585
  """Set the state of buttons after cleaning a cosmic ray."""
381
586
  self.disable_interpolation_buttons()
@@ -388,6 +593,7 @@ class ReviewCosmicRay(ImageDisplay):
388
593
  messagebox.showerror("Input Error", "2*Npoints must be greater than Degree for x interpolation.")
389
594
  return
390
595
  print(f"X-interpolation of cosmic ray {self.cr_index}")
596
+ print(f"Interpolation parameters: Npoints={self.npoints}, Degree={self.degree}")
391
597
  interpolation_performed, xfit_all, yfit_all = interpolation_x(
392
598
  data=self.data,
393
599
  mask_fixed=self.mask_fixed,
@@ -410,6 +616,7 @@ class ReviewCosmicRay(ImageDisplay):
410
616
  messagebox.showerror("Input Error", "2*Npoints must be greater than Degree for y interpolation.")
411
617
  return
412
618
  print(f"Y-interpolation of cosmic ray {self.cr_index}")
619
+ print(f"Interpolation parameters: Npoints={self.npoints}, Degree={self.degree}")
413
620
  interpolation_performed, xfit_all, yfit_all = interpolation_y(
414
621
  data=self.data,
415
622
  mask_fixed=self.mask_fixed,
@@ -435,6 +642,7 @@ class ReviewCosmicRay(ImageDisplay):
435
642
  The interpolation method to use ('surface', 'median' or 'mean').
436
643
  """
437
644
  print(f"{method} interpolation of cosmic ray {self.cr_index}")
645
+ print(f'Interpolation parameters: Npoints={self.npoints}, method="{method}"')
438
646
  interpolation_performed, xfit_all, yfit_all = interpolation_a(
439
647
  data=self.data,
440
648
  mask_fixed=self.mask_fixed,
@@ -468,6 +676,12 @@ class ReviewCosmicRay(ImageDisplay):
468
676
  def use_maskfill(self):
469
677
  """Use maskfill cleaned data to clean a cosmic ray."""
470
678
  print(f"Maskfill interpolation of cosmic ray {self.cr_index}")
679
+ print(
680
+ f"Maskfill parameters: size={self.maskfill_size}, "
681
+ f"operator={self.maskfill_operator}, "
682
+ f"smooth={self.maskfill_smooth}, "
683
+ f"verbose={self.maskfill_verbose}"
684
+ )
471
685
  ycr_list, xcr_list = np.where(self.cr_labels == self.cr_index)
472
686
  mask = np.zeros(self.data.shape, dtype=bool)
473
687
  for iy, ix in zip(ycr_list, xcr_list):
@@ -475,9 +689,10 @@ class ReviewCosmicRay(ImageDisplay):
475
689
  smoothed_output, _ = maskfill(
476
690
  input_image=self.data,
477
691
  mask=mask,
478
- size=3,
479
- operator="median",
480
- smooth=True,
692
+ size=self.maskfill_size,
693
+ operator=self.maskfill_operator,
694
+ smooth=self.maskfill_smooth,
695
+ verbose=self.maskfill_verbose,
481
696
  )
482
697
  for iy, ix in zip(ycr_list, xcr_list):
483
698
  self.data[iy, ix] = smoothed_output[iy, ix]
@@ -628,8 +843,8 @@ class ReviewCosmicRay(ImageDisplay):
628
843
  self.interp_d_button.config(state=tk.NORMAL)
629
844
  self.interp_m_button.config(state=tk.NORMAL)
630
845
  if self.cleandata_lacosmic is not None:
631
- if self.last_dilation is None or self.last_dilation == 0:
632
- self.interp_l_button.config(state=tk.NORMAL)
846
+ if self.last_dilation is None or self.last_dilation == 0:
847
+ self.interp_l_button.config(state=tk.NORMAL)
633
848
  self.interp_maskfill_button.config(state=tk.NORMAL)
634
849
  if self.auxdata is not None:
635
850
  self.interp_aux_button.config(state=tk.NORMAL)
@@ -0,0 +1,73 @@
1
+ #
2
+ # Copyright 2025 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0+
7
+ # License-Filename: LICENSE.txt
8
+ #
9
+
10
+ """Tracked button widget for the cleanest module."""
11
+
12
+ import tkinter as tk
13
+ from rich.console import Console
14
+
15
+ console = Console()
16
+
17
+
18
+ class TrackedTkButton:
19
+ """Class to create tracked Tkinter buttons with help text."""
20
+
21
+ def __init__(self, root):
22
+ """Initialize the TrackedTkButton instance.
23
+
24
+ Initializes an empty list to store button information.
25
+
26
+ Parameters
27
+ ----------
28
+ root : tk.Tk or tk.Frame
29
+ The parent widget where the help information about
30
+ the actions associated to each button will be displayed.
31
+ """
32
+ self.root = root
33
+ self.buttons_info = []
34
+
35
+ def new(self, parent, text, command, help_text, alttext=None, **kwargs):
36
+ """Create a Tkinter button with tracking information.
37
+
38
+ Parameters
39
+ ----------
40
+ parent : tk.Widget
41
+ The parent widget where the button will be placed.
42
+ text : str
43
+ The text to display on the button.
44
+ command : callable
45
+ The function to call when the button is pressed.
46
+ help_text : str
47
+ The help text associated with the button.
48
+ alttext : str, optional
49
+ Alternative text for the button.
50
+ **kwargs : dict
51
+ Additional keyword arguments to pass to the Tkinter Button constructor.
52
+
53
+ Returns
54
+ -------
55
+ button : tk.Button
56
+ The created Tkinter button.
57
+ """
58
+ button = tk.Button(parent, text=text, command=command, **kwargs)
59
+ self.buttons_info.append({"button": button, "text": text, "help_text": help_text, "alttext": alttext})
60
+ return button
61
+
62
+ def show_help(self):
63
+ """Display help information for all tracked buttons."""
64
+ console.rule("[bold red]Button Help Information[/bold red]")
65
+ for info in self.buttons_info:
66
+ if info["alttext"] is not None:
67
+ text = info["alttext"]
68
+ else:
69
+ text = info["text"]
70
+ # replace '[' by '\[' to avoid formatting issues
71
+ text = text.replace("[", "\\[")
72
+ console.print(f"[bold blue]{text}[/bold blue]: {info['help_text']}")
73
+ console.rule()
teareduce/version.py CHANGED
@@ -9,7 +9,7 @@
9
9
  #
10
10
  """Module to define the version of the teareduce package."""
11
11
 
12
- VERSION = '0.5.9'
12
+ VERSION = '0.6.0'
13
13
 
14
14
 
15
15
  def main():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teareduce
3
- Version: 0.5.9
3
+ Version: 0.6.0
4
4
  Summary: Utilities for astronomical data reduction
5
5
  Author-email: Nicolás Cardiel <cardiel@ucm.es>
6
6
  License: GPL-3.0-or-later
@@ -15,37 +15,40 @@ teareduce/sdistortion.py,sha256=5ZsZn4vD5Sw2aoqO8-NIOH7H89Zmh7ZDkow6YbAotHU,5916
15
15
  teareduce/simulateccdexposure.py,sha256=cdbpca6GVuM3d7R1LGzlIZZvjTq_jzrlkk_Cli7aouQ,24636
16
16
  teareduce/sliceregion.py,sha256=Jdf8XvmGaY_vaY1cneTaRtSOYPxpUsJm9cXJDDMa0YM,18626
17
17
  teareduce/statsummary.py,sha256=VTNAnBV8z6suqiyB2Lhw3YjUUOjlmwUPX3enkOKRF54,5422
18
- teareduce/version.py,sha256=Tab2zphBGQslnQsUawX1hJaJWEF6qYbHq13JXc12qms,419
18
+ teareduce/version.py,sha256=E1uQr8Mx5jXWQL_pejqYQN5pSGxyQT__LkXZ7yPdegY,419
19
19
  teareduce/wavecal.py,sha256=2MewWz5Y3ms41c305UtWOM_LaLNdk1mugDXCtzf-fSw,68586
20
20
  teareduce/write_array_to_fits.py,sha256=kWDrEH9coJ1yIu56oQJpWtDqJL4c8HGmssE9jle4e94,617
21
21
  teareduce/zscale.py,sha256=SDgmcDD2N5GvDn46JADCgTQJPBF_N_jSKMHoeTz9Nsw,1152
22
- teareduce/cleanest/__init__.py,sha256=NEJ7JETeZFaKl8yW9gjLHIwfkI3F3B0TWQeCY3ESSrk,548
23
- teareduce/cleanest/__main__.py,sha256=muijsFmB4kd0E5fn5ZhRHUH2bIPSRgykEUq3yg0K05c,5957
22
+ teareduce/cleanest/__init__.py,sha256=1rcfu97uqj3R-OZbU9HHLYaTzWkS-poRFBBy8nuH7p4,547
23
+ teareduce/cleanest/__main__.py,sha256=KhFJSekko0mz_pVz3gxNnEmqvv4nuRBdhaXB2Nt4aL0,6145
24
+ teareduce/cleanest/askextension.py,sha256=Q6mGLA6ajUcDU4gsVA4P78GsPei0c7iZK1f7SfK9iVo,3014
24
25
  teareduce/cleanest/centerchildparent.py,sha256=wHxOvNrrQ-KBLZAbtQ9bJAxYhGOzqYBF4LgdIQk7UF8,1285
25
- teareduce/cleanest/cosmicraycleanerapp.py,sha256=5z3tjfhoOQox3_NHi1oH2SudgPM_EjqMtz9nw6BKyqk,50882
26
- teareduce/cleanest/definitions.py,sha256=L7VXT0SAjv96zgZ_VhMMV1vcf-oxdwefvlmPclBA-ug,2558
26
+ teareduce/cleanest/cosmicraycleanerapp.py,sha256=MJPdHmEpID4ykGu3o89dLUD0ebhjw3ZlxFUBQDJ_HcY,61768
27
+ teareduce/cleanest/definitions.py,sha256=Ryreer1ztkk4W3muWGI_SSfm3ZAhyAOQJbZLtdfTJsc,4692
27
28
  teareduce/cleanest/dilatemask.py,sha256=I5tHAv5VEO6V0Wed8Ar20uLt4F9P-tgjmLL5BAaFvgM,1276
28
29
  teareduce/cleanest/find_closest_true.py,sha256=mWdIvhipzAXDRKfePDrP7f0lP4U48cckpHiKwiB4jHI,1320
30
+ teareduce/cleanest/gausskernel2d_elliptical.py,sha256=f6AT0ZHmLCd83NAIyyJ8ODlihMeeTkmvNnUSK3vjP9I,1606
29
31
  teareduce/cleanest/imagedisplay.py,sha256=820Vn-Q0bJyHicOBsxDmfAZxuGOFepEEsm0LTxlPJjc,5848
30
- teareduce/cleanest/interpolate.py,sha256=qlz4SHw89ahBRx1VHBMkd0IEpFkd96P6FVfsw_LoQDA,5194
32
+ teareduce/cleanest/interpolate.py,sha256=upk6OxIIfoV1PetogwI8PUmdwM6TiKvHu9XNUwG1Dac,6380
31
33
  teareduce/cleanest/interpolation_a.py,sha256=zE4VIrC41vapf0Vx9qmh1oacw2qkJwcuMnV3JpSDW8Y,4007
32
34
  teareduce/cleanest/interpolation_x.py,sha256=D5hKbobT6lJk18XktP3PXhzEN12zqed6U18yfQ-reLQ,3744
33
35
  teareduce/cleanest/interpolation_y.py,sha256=O6yw5nKKlTdV6qsP1Ku6CwGhXB6o3j0_YQirXyILi8c,3759
34
- teareduce/cleanest/interpolationeditor.py,sha256=J5vrioLTwUSMbUeY0ezqNqm3CvLJgKFe7gvZ44Z62sc,13257
35
- teareduce/cleanest/lacosmicpad.py,sha256=K_SU2YBds9I3eN0vJLkrbTLcdRpB2bMRf90_3Rk5ZIk,2345
36
+ teareduce/cleanest/interpolationeditor.py,sha256=ZcqQl_iG-0eH-1PdtnJppRI9zKiSK6S7OCQz7XUvAno,19888
37
+ teareduce/cleanest/lacosmicpad.py,sha256=aZp-2NZJKRnu1NmjC_SHnL0EVy0jDXWULgo0Pe7tq3U,6817
36
38
  teareduce/cleanest/mergemasks.py,sha256=1Vq6Wqn6DClxSAvHwy6QrJGszA8nkmoMHITyprSykHI,2072
37
39
  teareduce/cleanest/modalprogressbar.py,sha256=uwd-p92PvOVJnbXd-B8DRcBZ--keKpr4ZN9PLeqm1Ws,6449
38
- teareduce/cleanest/parametereditor.py,sha256=_0baFbkIUcPyF-teFIRMt1MpA4IzDZc7VHNh1h0qN4A,14229
39
- teareduce/cleanest/reviewcosmicray.py,sha256=QQaGMl_P9uWYBmDlPFIw7vEg_TI5DUJUhzBMdIShHC0,28297
40
+ teareduce/cleanest/parametereditor.py,sha256=V8twAQzR0vjd68PB92PfdQBmTRDR04xx2mb4SLSV68Y,24530
41
+ teareduce/cleanest/reviewcosmicray.py,sha256=8jUaJCHVvZqlQ57A9eCDNIfBCKQ0IqFADq8C1H7T7ko,36518
42
+ teareduce/cleanest/trackedbutton.py,sha256=r6Mzhb24c3HaJdvGGGFLn7cF9n4TgsKYc_yfcYIyB4g,2348
40
43
  teareduce/cookbook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
44
  teareduce/cookbook/get_cookbook_file.py,sha256=vde-iNii2lm1QII8GmLRsFsKNxkdsd7njCBE-8Z7io0,1088
42
45
  teareduce/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
46
  teareduce/tests/test_cleanest.py,sha256=6dRqkw1RQMKsFrC8cEweMvTD6wXhiDv3P4PS57-HEqI,5598
44
47
  teareduce/tests/test_sliceregion.py,sha256=S7Zoh2eEBFIEbfsXgWBEMCf7pottjw2oLhqlZJQkAwg,3785
45
48
  teareduce/tests/test_version.py,sha256=mKLnbXyvVNc1pATq5PxR8qeoFMPAFL_GilFV6IHLOi0,172
46
- teareduce-0.5.9.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
47
- teareduce-0.5.9.dist-info/METADATA,sha256=lk-aBMRThZ2tRIPajrP6yKD40TIow1WgzJ56ymTcOjU,3640
48
- teareduce-0.5.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
- teareduce-0.5.9.dist-info/entry_points.txt,sha256=6yBvig5jTL2ugqz5SF767AiszzrHKGRASsX1II84kqA,66
50
- teareduce-0.5.9.dist-info/top_level.txt,sha256=7OkwtX9zNRkGJ7ACgjk4ESgC74qUYcS5O2qcO0v-Si4,10
51
- teareduce-0.5.9.dist-info/RECORD,,
49
+ teareduce-0.6.0.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
50
+ teareduce-0.6.0.dist-info/METADATA,sha256=ZL_ioffCU8ICr6DsuohcYHR3tvJLWkG1gJePxH61Pgk,3640
51
+ teareduce-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
+ teareduce-0.6.0.dist-info/entry_points.txt,sha256=6yBvig5jTL2ugqz5SF767AiszzrHKGRASsX1II84kqA,66
53
+ teareduce-0.6.0.dist-info/top_level.txt,sha256=7OkwtX9zNRkGJ7ACgjk4ESgC74qUYcS5O2qcO0v-Si4,10
54
+ teareduce-0.6.0.dist-info/RECORD,,