graxpert 3.2.0a0.dev4__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.
Files changed (58) hide show
  1. graxpert/AstroImageRepository.py +114 -0
  2. graxpert/__init__.py +1 -0
  3. graxpert/ai_model_handling.py +215 -0
  4. graxpert/app_state.py +10 -0
  5. graxpert/application/__init__.py +0 -0
  6. graxpert/application/app.py +741 -0
  7. graxpert/application/app_events.py +96 -0
  8. graxpert/application/eventbus.py +24 -0
  9. graxpert/astroimage.py +308 -0
  10. graxpert/background_extraction.py +290 -0
  11. graxpert/background_flood_selection.py +240 -0
  12. graxpert/background_grid_selection.py +64 -0
  13. graxpert/cmdline_tools.py +547 -0
  14. graxpert/commands.py +200 -0
  15. graxpert/deconvolution.py +176 -0
  16. graxpert/denoising.py +178 -0
  17. graxpert/grid_utils.py +38 -0
  18. graxpert/img/GraXpert_LOGO_Hauptvariante.png +0 -0
  19. graxpert/img/Icon.png +0 -0
  20. graxpert/img/LDN1235_original.jpg +0 -0
  21. graxpert/img/LDN1235_processed.jpg +0 -0
  22. graxpert/img/__init__.py +0 -0
  23. graxpert/img/gfx_numbers.png +0 -0
  24. graxpert/img/gfx_numbers.svg +737 -0
  25. graxpert/img/graXpert_Startbadge_Umbriel.png +0 -0
  26. graxpert/img/hourglass.png +0 -0
  27. graxpert/locales/__init__.py +0 -0
  28. graxpert/locales/de_DE/LC_MESSAGES/base.mo +0 -0
  29. graxpert/localization.py +39 -0
  30. graxpert/main.py +441 -0
  31. graxpert/mp_logging.py +97 -0
  32. graxpert/parallel_processing.py +3 -0
  33. graxpert/preferences.py +139 -0
  34. graxpert/radialbasisinterpolation.py +215 -0
  35. graxpert/resource_utils.py +35 -0
  36. graxpert/s3_secrets.py +16 -0
  37. graxpert/skyall.py +152 -0
  38. graxpert/stretch.py +151 -0
  39. graxpert/theme/__init__.py +0 -0
  40. graxpert/theme/graxpert-dark-blue.json +367 -0
  41. graxpert/ui/__init__.py +1 -0
  42. graxpert/ui/application_frame.py +115 -0
  43. graxpert/ui/canvas.py +558 -0
  44. graxpert/ui/left_menu.py +437 -0
  45. graxpert/ui/loadingframe.py +142 -0
  46. graxpert/ui/right_menu.py +315 -0
  47. graxpert/ui/statusbar.py +102 -0
  48. graxpert/ui/styling.py +23 -0
  49. graxpert/ui/tooltip.py +194 -0
  50. graxpert/ui/ui_events.py +19 -0
  51. graxpert/ui/widgets.py +297 -0
  52. graxpert/ui_scaling.py +13 -0
  53. graxpert/version.py +2 -0
  54. graxpert-3.2.0a0.dev4.dist-info/METADATA +219 -0
  55. graxpert-3.2.0a0.dev4.dist-info/RECORD +58 -0
  56. graxpert-3.2.0a0.dev4.dist-info/WHEEL +5 -0
  57. graxpert-3.2.0a0.dev4.dist-info/entry_points.txt +2 -0
  58. graxpert-3.2.0a0.dev4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,437 @@
1
+ import tkinter as tk
2
+
3
+ from customtkinter import ThemeManager
4
+
5
+ import graxpert.ui.tooltip as tooltip
6
+ from graxpert.application.app import graxpert
7
+ from graxpert.application.app_events import AppEvents
8
+ from graxpert.application.eventbus import eventbus
9
+ from graxpert.localization import _
10
+ from graxpert.ui.ui_events import UiEvents
11
+ from graxpert.ui.widgets import CollapsibleMenuFrame, GraXpertButton, GraXpertCheckbox, GraXpertOptionMenu, GraXpertScrollableFrame, ProcessingStep, ValueSlider, default_label_width, padx, pady
12
+
13
+
14
+ class LoadMenu(CollapsibleMenuFrame):
15
+ def __init__(self, parent, **kwargs):
16
+ super().__init__(parent, title=_("Loading"), show=True, number=1, **kwargs)
17
+
18
+ self.create_children()
19
+ self.setup_layout()
20
+ self.place_children()
21
+
22
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: self.hide() if not e == "LOAD" else None)
23
+
24
+ def create_children(self):
25
+ super().create_children()
26
+
27
+ # image loading
28
+ self.load_image_button = GraXpertButton(
29
+ self.sub_frame,
30
+ text=_("Load Image"),
31
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
32
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
33
+ command=self.menu_open_clicked,
34
+ )
35
+ self.tt_load = tooltip.Tooltip(self.load_image_button, text=tooltip.load_text)
36
+
37
+ def setup_layout(self):
38
+ super().setup_layout()
39
+
40
+ def place_children(self):
41
+ super().place_children()
42
+
43
+ row = -1
44
+
45
+ def next_row():
46
+ nonlocal row
47
+ row += 1
48
+ return row
49
+
50
+ # image loading
51
+ self.load_image_button.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
52
+
53
+ def toggle(self):
54
+ super().toggle()
55
+ if self.show:
56
+ eventbus.emit(UiEvents.SHOW_MENU_REQUEST, "LOAD")
57
+
58
+ def menu_open_clicked(self, event=None):
59
+ eventbus.emit(AppEvents.OPEN_FILE_DIALOG_REQUEST)
60
+
61
+
62
+ class CropMenu(CollapsibleMenuFrame):
63
+ def __init__(self, parent, **kwargs):
64
+ super().__init__(parent, title=_("Crop"), show=False, number=2, **kwargs)
65
+ self.create_children()
66
+ self.setup_layout()
67
+ self.place_children()
68
+
69
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: self.hide() if not e == "CROP" else None)
70
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: eventbus.emit(UiEvents.TURN_OFF_CROP_MODE) if not e == "CROP" else None)
71
+
72
+ def create_children(self):
73
+ super().create_children()
74
+ self.cropapply_button = GraXpertButton(
75
+ self.sub_frame,
76
+ text=_("Apply crop"),
77
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
78
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
79
+ command=lambda: eventbus.emit(UiEvents.APPLY_CROP_REQUEST),
80
+ )
81
+
82
+ def setup_layout(self):
83
+ super().setup_layout()
84
+
85
+ def place_children(self):
86
+ super().place_children()
87
+ self.cropapply_button.grid(column=1, row=0, pady=pady, sticky=tk.NSEW)
88
+
89
+ def toggle(self):
90
+ super().toggle()
91
+ if self.show:
92
+ eventbus.emit(UiEvents.SHOW_MENU_REQUEST, "CROP")
93
+ eventbus.emit(UiEvents.TURN_ON_CROP_MODE)
94
+ else:
95
+ eventbus.emit(UiEvents.TURN_OFF_CROP_MODE)
96
+
97
+
98
+ class ExtractionMenu(CollapsibleMenuFrame):
99
+ def __init__(self, parent, **kwargs):
100
+ super().__init__(parent, title=_("Background Extraction"), show=False, number=3, **kwargs)
101
+
102
+ # method selection
103
+ self.interpol_options = ["RBF", "Splines", "Kriging", "AI"]
104
+ self.interpol_type = tk.StringVar()
105
+ self.interpol_type.set(graxpert.prefs.interpol_type_option)
106
+ self.interpol_type.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.INTERPOL_TYPE_CHANGED, {"interpol_type_option": self.interpol_type.get()}))
107
+
108
+ # sample selection
109
+ self.display_pts = tk.BooleanVar()
110
+ self.display_pts.set(graxpert.prefs.display_pts)
111
+ self.display_pts.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.DISPLAY_PTS_CHANGED, {"display_pts": self.display_pts.get()}))
112
+
113
+ self.flood_select_pts = tk.BooleanVar()
114
+ self.flood_select_pts.set(graxpert.prefs.bg_flood_selection_option)
115
+ self.flood_select_pts.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.BG_FLOOD_SELECTION_CHANGED, {"bg_flood_selection_option": self.flood_select_pts.get()}))
116
+
117
+ self.bg_pts = tk.IntVar()
118
+ self.bg_pts.set(graxpert.prefs.bg_pts_option)
119
+ self.bg_pts.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.BG_PTS_CHANGED, {"bg_pts_option": self.bg_pts.get()}))
120
+
121
+ self.bg_tol = tk.DoubleVar()
122
+ self.bg_tol.set(graxpert.prefs.bg_tol_option)
123
+ self.bg_tol.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.BG_TOL_CHANGED, {"bg_tol_option": self.bg_tol.get()}))
124
+
125
+ # calculation
126
+ self.smoothing = tk.DoubleVar()
127
+ self.smoothing.set(graxpert.prefs.smoothing_option)
128
+ self.smoothing.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.SMOTTHING_CHANGED, {"smoothing_option": self.smoothing.get()}))
129
+
130
+ self.create_children()
131
+ self.setup_layout()
132
+ self.place_children()
133
+
134
+ eventbus.add_listener(AppEvents.INTERPOL_TYPE_CHANGED, self.place_children)
135
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: self.hide() if not e == "BGE" else None)
136
+
137
+ def create_children(self):
138
+ super().create_children()
139
+
140
+ # method selection
141
+ self.intp_type_title = ProcessingStep(self.sub_frame, number=0, title=_("Interpolation Method:"))
142
+ self.interpol_menu = GraXpertOptionMenu(self.sub_frame, variable=self.interpol_type, values=self.interpol_options)
143
+ tooltip.Tooltip(self.interpol_menu, text=tooltip.interpol_type_text)
144
+
145
+ # sample selection
146
+ self.sample_selection_title = ProcessingStep(self.sub_frame, number=0, title=_(" Sample Selection"))
147
+ self.display_pts_switch = GraXpertCheckbox(self.sub_frame, width=default_label_width, text=_("Display points"), variable=self.display_pts)
148
+ self.flood_select_pts_switch = GraXpertCheckbox(self.sub_frame, width=default_label_width, text=_("Flooded generation"), variable=self.flood_select_pts)
149
+ tooltip.Tooltip(self.flood_select_pts_switch, text=tooltip.bg_flood_text)
150
+ self.bg_pts_slider = ValueSlider(self.sub_frame, width=default_label_width, variable_name=_("Points per row"), variable=self.bg_pts, min_value=4, max_value=25, precision=0)
151
+ tooltip.Tooltip(self.bg_pts_slider, text=tooltip.num_points_text)
152
+ self.bg_tol_slider = ValueSlider(self.sub_frame, width=default_label_width, variable_name=_("Grid Tolerance"), variable=self.bg_tol, min_value=-2, max_value=10, precision=1)
153
+ tooltip.Tooltip(self.bg_tol_slider, text=tooltip.bg_tol_text)
154
+ self.bg_selection_button = GraXpertButton(self.sub_frame, text=_("Create Grid"), command=lambda: eventbus.emit(AppEvents.CREATE_GRID_REQUEST))
155
+ tooltip.Tooltip(self.bg_selection_button, text=tooltip.bg_select_text)
156
+ self.reset_button = GraXpertButton(self.sub_frame, text=_("Reset Sample Points"), command=lambda: eventbus.emit(AppEvents.RESET_POITS_REQUEST))
157
+ tooltip.Tooltip(self.reset_button, text=tooltip.reset_text)
158
+
159
+ # calculation
160
+ self.calculation_title = ProcessingStep(self.sub_frame, number=0, title=_(" Calculation"))
161
+ self.smoothing_slider = ValueSlider(self.sub_frame, width=default_label_width, variable_name=_("Smoothing"), variable=self.smoothing, min_value=0, max_value=1, precision=1)
162
+ tooltip.Tooltip(self.smoothing_slider, text=tooltip.smoothing_text)
163
+ self.calculate_button = GraXpertButton(
164
+ self.sub_frame,
165
+ text=_("Calculate Background"),
166
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
167
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
168
+ command=lambda: eventbus.emit(AppEvents.CALCULATE_REQUEST),
169
+ )
170
+ tooltip.Tooltip(self.calculate_button, text=tooltip.calculate_text)
171
+
172
+ def setup_layout(self):
173
+ super().setup_layout()
174
+
175
+ def place_children(self, event=None):
176
+ super().place_children()
177
+
178
+ row = -1
179
+
180
+ def next_row():
181
+ nonlocal row
182
+ row += 1
183
+ return row
184
+
185
+ # method selection
186
+ self.intp_type_title.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
187
+ self.interpol_menu.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
188
+
189
+ # sample selection
190
+ self.sample_selection_title.grid_forget()
191
+ self.display_pts_switch.grid_forget()
192
+ self.flood_select_pts_switch.grid_forget()
193
+ self.bg_pts_slider.grid_forget()
194
+ self.bg_tol_slider.grid_forget()
195
+ self.bg_selection_button.grid_forget()
196
+ self.reset_button.grid_forget()
197
+ if not self.interpol_type.get() == "AI":
198
+ self.sample_selection_title.grid(column=0, row=next_row(), columnspan=2, pady=pady, sticky=tk.EW)
199
+ self.display_pts_switch.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
200
+ self.flood_select_pts_switch.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
201
+ self.bg_pts_slider.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
202
+ self.bg_tol_slider.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
203
+ self.bg_selection_button.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
204
+ self.reset_button.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
205
+
206
+ # calculation
207
+ self.calculation_title.grid_forget()
208
+ self.smoothing_slider.grid_forget()
209
+ self.calculate_button.grid_forget()
210
+ self.calculation_title.grid(column=0, row=next_row(), pady=pady, columnspan=2, sticky=tk.EW)
211
+ self.smoothing_slider.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
212
+ self.calculate_button.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
213
+
214
+ def toggle(self):
215
+ super().toggle()
216
+ if self.show:
217
+ eventbus.emit(UiEvents.SHOW_MENU_REQUEST, "BGE")
218
+
219
+
220
+ class DeconvolutionMenu(CollapsibleMenuFrame):
221
+ def __init__(self, parent, **kwargs):
222
+ super().__init__(parent, title=_("Deconvolution"), show=False, number=4, **kwargs)
223
+
224
+ # method selection
225
+ self.deconvolution_options = ["Object-only", "Stars-only"]
226
+ self.deconvolution_type = tk.StringVar()
227
+ self.deconvolution_type.set(graxpert.prefs.deconvolution_type_option)
228
+ self.deconvolution_type.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.DECONVOLUTION_TYPE_CHANGED, {"deconvolution_type_option": self.deconvolution_type.get()}))
229
+
230
+ self.deconvolution_strength = tk.DoubleVar()
231
+ self.deconvolution_strength.set(graxpert.prefs.deconvolution_strength)
232
+ self.deconvolution_strength.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.DECONVOLUTION_STRENGTH_CHANGED, {"deconvolution_strength": self.deconvolution_strength.get()}))
233
+
234
+ self.deconvolution_psfsize = tk.DoubleVar()
235
+ self.deconvolution_psfsize.set(graxpert.prefs.deconvolution_psfsize)
236
+ self.deconvolution_psfsize.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.DECONVOLUTION_PSFSIZE_CHANGED, {"deconvolution_psfsize": self.deconvolution_psfsize.get()}))
237
+
238
+ self.create_children()
239
+ self.setup_layout()
240
+ self.place_children()
241
+
242
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: self.hide() if not e == "DECONVOLUTION" else None)
243
+
244
+ def create_children(self):
245
+ super().create_children()
246
+
247
+ # method selection
248
+ self.deconvolution_type_title = ProcessingStep(self.sub_frame, number=0, title=_("Deconvolution Method:"))
249
+ self.deconvolution_menu = GraXpertOptionMenu(self.sub_frame, variable=self.deconvolution_type, values=self.deconvolution_options)
250
+ tooltip.Tooltip(self.deconvolution_menu, text=tooltip.deconvolution_type_text)
251
+
252
+ self.deconvolution_button = GraXpertButton(
253
+ self.sub_frame,
254
+ text=_("Deconvolve Image"),
255
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
256
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
257
+ command=lambda: eventbus.emit(AppEvents.DECONVOLUTION_REQUEST),
258
+ )
259
+ self.tt_load = tooltip.Tooltip(self.deconvolution_button, text=tooltip.deconvolution_text)
260
+
261
+ self.deconvolution_strength_slider = ValueSlider(
262
+ self.sub_frame, width=default_label_width, variable_name=_("Deconvolution Strength"), variable=self.deconvolution_strength, min_value=0.0, max_value=1.0, precision=1
263
+ )
264
+ tooltip.Tooltip(self.deconvolution_strength_slider, text=tooltip.deconvolution_strength_text)
265
+
266
+ self.deconvolution_psfsize_slider = ValueSlider(
267
+ self.sub_frame, width=default_label_width, variable_name=_("Image FWHM (in pixels)"), variable=self.deconvolution_psfsize, min_value=0.0, max_value=14.0, precision=1
268
+ )
269
+ tooltip.Tooltip(self.deconvolution_psfsize_slider, text=tooltip.deconvolution_psfsize_text)
270
+
271
+ def setup_layout(self):
272
+ super().setup_layout()
273
+
274
+ def place_children(self):
275
+ super().place_children()
276
+
277
+ row = -1
278
+
279
+ def next_row():
280
+ nonlocal row
281
+ row += 1
282
+ return row
283
+
284
+ # method selection
285
+ self.deconvolution_type_title.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
286
+ self.deconvolution_menu.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
287
+
288
+ self.deconvolution_strength_slider.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
289
+ self.deconvolution_psfsize_slider.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
290
+ self.deconvolution_button.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
291
+
292
+ def toggle(self):
293
+ super().toggle()
294
+ if self.show:
295
+ eventbus.emit(UiEvents.SHOW_MENU_REQUEST, "DECONVOLUTION")
296
+
297
+
298
+ class DenoiseMenu(CollapsibleMenuFrame):
299
+ def __init__(self, parent, **kwargs):
300
+ super().__init__(parent, title=_("Denoising"), show=False, number=5, **kwargs)
301
+
302
+ self.denoise_strength = tk.DoubleVar()
303
+ self.denoise_strength.set(graxpert.prefs.denoise_strength)
304
+ self.denoise_strength.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.DENOISE_STRENGTH_CHANGED, {"denoise_strength": self.denoise_strength.get()}))
305
+
306
+ self.create_children()
307
+ self.setup_layout()
308
+ self.place_children()
309
+
310
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: self.hide() if not e == "DENOISE" else None)
311
+
312
+ def create_children(self):
313
+ super().create_children()
314
+
315
+ self.denoise_button = GraXpertButton(
316
+ self.sub_frame,
317
+ text=_("Denoise Image"),
318
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
319
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
320
+ command=lambda: eventbus.emit(AppEvents.DENOISE_REQUEST),
321
+ )
322
+ self.tt_load = tooltip.Tooltip(self.denoise_button, text=tooltip.denoise_text)
323
+
324
+ self.denoise_strength_slider = ValueSlider(
325
+ self.sub_frame, width=default_label_width, variable_name=_("Denoise Strength"), variable=self.denoise_strength, min_value=0.0, max_value=1.0, precision=2
326
+ )
327
+ tooltip.Tooltip(self.denoise_strength_slider, text=tooltip.denoise_strength_text)
328
+
329
+ def setup_layout(self):
330
+ super().setup_layout()
331
+
332
+ def place_children(self):
333
+ super().place_children()
334
+
335
+ self.denoise_strength_slider.grid(column=1, row=0, pady=pady, sticky=tk.EW)
336
+ self.denoise_button.grid(column=1, row=2, pady=pady, sticky=tk.EW)
337
+
338
+ def toggle(self):
339
+ super().toggle()
340
+ if self.show:
341
+ eventbus.emit(UiEvents.SHOW_MENU_REQUEST, "DENOISE")
342
+
343
+
344
+ class SaveMenu(CollapsibleMenuFrame):
345
+ def __init__(self, parent, **kwargs):
346
+ super().__init__(parent, title=_("Saving"), show=False, number=6, **kwargs)
347
+
348
+ # saving
349
+ self.saveas_options = ["16 bit Tiff", "32 bit Tiff", "16 bit Fits", "32 bit Fits", "16 bit XISF", "32 bit XISF"]
350
+ self.saveas_type = tk.StringVar()
351
+ self.saveas_type.set(graxpert.prefs.saveas_option)
352
+ self.saveas_type.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.SAVE_AS_CHANGED, {"saveas_option": self.saveas_type.get()}))
353
+ self.saveas_stretched = tk.BooleanVar()
354
+ self.saveas_stretched.set(graxpert.prefs.saveas_stretched)
355
+ self.saveas_stretched.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.SAVE_STRETCHED_CHANGED, {"saveas_stretched": self.saveas_stretched.get()}))
356
+
357
+ self.create_children()
358
+ self.setup_layout()
359
+ self.place_children()
360
+
361
+ eventbus.add_listener(UiEvents.SHOW_MENU_REQUEST, lambda e: self.hide() if not e == "SAVE" else None)
362
+
363
+ def create_children(self):
364
+ super().create_children()
365
+
366
+ # saving
367
+ self.saveas_menu = GraXpertOptionMenu(self.sub_frame, variable=self.saveas_type, values=self.saveas_options)
368
+ tooltip.Tooltip(self.saveas_menu, text=tooltip.saveas_text)
369
+ self.save_button = GraXpertButton(
370
+ self.sub_frame,
371
+ text=_("Save Selected"),
372
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
373
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
374
+ command=lambda: eventbus.emit(AppEvents.SAVE_REQUEST),
375
+ )
376
+ tooltip.Tooltip(self.save_button, text=tooltip.save_pic_text)
377
+ self.saveas_stretched_checkbox = GraXpertCheckbox(self.sub_frame, width=default_label_width, text=_("Save Stretched"), variable=self.saveas_stretched)
378
+ tooltip.Tooltip(self.saveas_stretched_checkbox, text=tooltip.saveas_stretched_text)
379
+
380
+ def setup_layout(self):
381
+ super().setup_layout()
382
+
383
+ def place_children(self):
384
+ super().place_children()
385
+
386
+ row = -1
387
+
388
+ def next_row():
389
+ nonlocal row
390
+ row += 1
391
+ return row
392
+
393
+ # saving
394
+ self.saveas_menu.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
395
+ self.save_button.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
396
+ self.saveas_stretched_checkbox.grid(column=1, row=next_row(), pady=pady, sticky=tk.EW)
397
+
398
+ def toggle(self):
399
+ super().toggle()
400
+ if self.show:
401
+ eventbus.emit(UiEvents.SHOW_MENU_REQUEST, "SAVE")
402
+
403
+
404
+ class LeftMenu(GraXpertScrollableFrame):
405
+ def __init__(self, parent, **kwargs):
406
+ super().__init__(parent, **kwargs)
407
+ self.create_children()
408
+ self.setup_layout()
409
+ self.place_children()
410
+
411
+ def create_children(self):
412
+ self.load_menu = LoadMenu(self, fg_color="transparent")
413
+ self.crop_menu = CropMenu(self, fg_color="transparent")
414
+ self.extraction_menu = ExtractionMenu(self, fg_color="transparent")
415
+ self.deconvolution_menu = DeconvolutionMenu(self, fg_color="transparent")
416
+ self.denoise_menu = DenoiseMenu(self, fg_color="transparent")
417
+ self.save_menu = SaveMenu(self, fg_color="transparent")
418
+
419
+ def setup_layout(self):
420
+ self.columnconfigure(0, weight=1)
421
+ self.rowconfigure(0, weight=1)
422
+
423
+ def place_children(self):
424
+
425
+ row = -1
426
+
427
+ def next_row():
428
+ nonlocal row
429
+ row += 1
430
+ return row
431
+
432
+ self.load_menu.grid(column=0, row=next_row(), ipadx=padx, sticky=tk.N)
433
+ self.crop_menu.grid(column=0, row=next_row(), ipadx=padx, sticky=tk.N)
434
+ self.extraction_menu.grid(column=0, row=next_row(), ipadx=padx, sticky=tk.N)
435
+ self.deconvolution_menu.grid(column=0, row=next_row(), ipadx=padx, sticky=tk.N)
436
+ self.denoise_menu.grid(column=0, row=next_row(), ipadx=padx, sticky=tk.N)
437
+ self.save_menu.grid(column=0, row=next_row(), ipadx=padx, sticky=tk.N)
@@ -0,0 +1,142 @@
1
+ import logging
2
+ import tkinter as tk
3
+ from os import path
4
+ from queue import Empty, Queue
5
+ from threading import Thread
6
+
7
+ from customtkinter import CTkButton, CTkFont, CTkFrame, CTkImage, CTkLabel, CTkProgressBar, DoubleVar, StringVar, ThemeManager
8
+
9
+ from graxpert.application.app_events import AppEvents
10
+ from graxpert.application.eventbus import eventbus
11
+ from graxpert.localization import _
12
+ from graxpert.resource_utils import resource_image
13
+
14
+
15
+ class LoadingFrame(CTkFrame):
16
+ def __init__(self, parent, **kwargs):
17
+ super().__init__(parent, **kwargs)
18
+ self.create_children()
19
+ self.setup_layout()
20
+ self.place_children()
21
+
22
+ def create_children(self):
23
+ font = CTkFont(size=15)
24
+ self.text = CTkLabel(
25
+ self,
26
+ text=_("Calculating..."),
27
+ image=CTkImage(light_image=resource_image("hourglass.png"), dark_image=resource_image("hourglass.png"), size=(30, 30)),
28
+ font=font,
29
+ compound=tk.LEFT,
30
+ )
31
+
32
+ def setup_layout(self):
33
+ self.columnconfigure(0, weight=1)
34
+ self.rowconfigure(0, weight=1)
35
+
36
+ def place_children(self):
37
+ self.text.grid(column=0, row=0)
38
+
39
+
40
+ class DynamicProgressFrame(CTkFrame):
41
+ def __init__(self, parent, label_lext=_("Progress:"), cancellable=False, **kwargs):
42
+ super().__init__(parent, **kwargs)
43
+
44
+ self.inner_frame = CTkFrame(self, fg_color="transparent")
45
+ self.inner_frame.grid(row=0, column=0, sticky=tk.NSEW, padx=10, pady=(5, 10))
46
+
47
+ self.text = StringVar(self, value=label_lext)
48
+ self.variable = DoubleVar(self, value=0.0)
49
+ self.cancellable = cancellable
50
+
51
+ self.create_children()
52
+ self.setup_layout()
53
+ self.place_children()
54
+
55
+ def create_children(self):
56
+ self.label = CTkLabel(
57
+ self.inner_frame,
58
+ textvariable=self.text,
59
+ width=280,
60
+ )
61
+ self.pb = CTkProgressBar(self.inner_frame, variable=self.variable)
62
+ self.pb.grid(column=0, row=0, sticky=tk.NSEW, pady=10)
63
+ self.cancel_button = CTkButton(
64
+ self.inner_frame,
65
+ text=_("Cancel"),
66
+ command=lambda: eventbus.emit(AppEvents.CANCEL_PROCESSING),
67
+ fg_color=ThemeManager.theme["Accent.CTkButton"]["fg_color"],
68
+ hover_color=ThemeManager.theme["Accent.CTkButton"]["hover_color"],
69
+ )
70
+
71
+ def setup_layout(self):
72
+ self.inner_frame.columnconfigure(0, weight=1)
73
+ self.inner_frame.rowconfigure(0, weight=1)
74
+
75
+ def place_children(self):
76
+ self.label.grid(column=0, row=0, sticky=tk.NSEW)
77
+ self.pb.grid(column=0, row=1, sticky=tk.NSEW)
78
+ if self.cancellable:
79
+ self.cancel_button.grid(column=0, row=2)
80
+ else:
81
+ self.cancel_button.grid_forget()
82
+
83
+ def close(self):
84
+ self.pb.grid_forget()
85
+ self.cancel_button.grid_forget()
86
+ self.update()
87
+ self.destroy()
88
+
89
+ def update_progress(self, progress):
90
+ self.variable.set(progress) # * 100
91
+ logging.info("Progress: {}%".format(int(self.variable.get() * 100)))
92
+ self.pb.update()
93
+
94
+
95
+ class DynamicProgressThread(Thread):
96
+ def __init__(self, interval=1, total=100, callback=None):
97
+ Thread.__init__(self)
98
+ self.daemon = True
99
+ self.interval = interval
100
+ self.callback = callback
101
+ self.total = total
102
+ self.current_progress = 0
103
+ self.update_queue = Queue()
104
+ self.start()
105
+
106
+ def run(self):
107
+ while True:
108
+ try:
109
+ # display every interval secs
110
+ task = self.update_queue.get(timeout=self.interval)
111
+ except Empty:
112
+ continue
113
+
114
+ current_progress, total = task
115
+ self.update_queue.task_done()
116
+ if current_progress == total:
117
+ # once we have done uploading everything return
118
+ self.done_progress()
119
+ return
120
+
121
+ # minio needs this method
122
+ def set_meta(self, total_length, object_name=None):
123
+ self.total = total_length
124
+
125
+ def update(self, size):
126
+ if not isinstance(size, int):
127
+ raise ValueError(f"{type(size)} type can not be displayed. Please change it to Int.")
128
+
129
+ self.current_progress += size
130
+ self.update_queue.put((self.current_progress, self.total))
131
+
132
+ if self.callback is not None:
133
+ self.callback(self.progress())
134
+
135
+ def done_progress(self):
136
+ self.total = 0
137
+ self.current_progress = 0
138
+
139
+ def progress(self):
140
+ if self.total == 0:
141
+ return 0
142
+ return float(self.current_progress) / float(self.total)