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.
- graxpert/AstroImageRepository.py +114 -0
- graxpert/__init__.py +1 -0
- graxpert/ai_model_handling.py +215 -0
- graxpert/app_state.py +10 -0
- graxpert/application/__init__.py +0 -0
- graxpert/application/app.py +741 -0
- graxpert/application/app_events.py +96 -0
- graxpert/application/eventbus.py +24 -0
- graxpert/astroimage.py +308 -0
- graxpert/background_extraction.py +290 -0
- graxpert/background_flood_selection.py +240 -0
- graxpert/background_grid_selection.py +64 -0
- graxpert/cmdline_tools.py +547 -0
- graxpert/commands.py +200 -0
- graxpert/deconvolution.py +176 -0
- graxpert/denoising.py +178 -0
- graxpert/grid_utils.py +38 -0
- graxpert/img/GraXpert_LOGO_Hauptvariante.png +0 -0
- graxpert/img/Icon.png +0 -0
- graxpert/img/LDN1235_original.jpg +0 -0
- graxpert/img/LDN1235_processed.jpg +0 -0
- graxpert/img/__init__.py +0 -0
- graxpert/img/gfx_numbers.png +0 -0
- graxpert/img/gfx_numbers.svg +737 -0
- graxpert/img/graXpert_Startbadge_Umbriel.png +0 -0
- graxpert/img/hourglass.png +0 -0
- graxpert/locales/__init__.py +0 -0
- graxpert/locales/de_DE/LC_MESSAGES/base.mo +0 -0
- graxpert/localization.py +39 -0
- graxpert/main.py +441 -0
- graxpert/mp_logging.py +97 -0
- graxpert/parallel_processing.py +3 -0
- graxpert/preferences.py +139 -0
- graxpert/radialbasisinterpolation.py +215 -0
- graxpert/resource_utils.py +35 -0
- graxpert/s3_secrets.py +16 -0
- graxpert/skyall.py +152 -0
- graxpert/stretch.py +151 -0
- graxpert/theme/__init__.py +0 -0
- graxpert/theme/graxpert-dark-blue.json +367 -0
- graxpert/ui/__init__.py +1 -0
- graxpert/ui/application_frame.py +115 -0
- graxpert/ui/canvas.py +558 -0
- graxpert/ui/left_menu.py +437 -0
- graxpert/ui/loadingframe.py +142 -0
- graxpert/ui/right_menu.py +315 -0
- graxpert/ui/statusbar.py +102 -0
- graxpert/ui/styling.py +23 -0
- graxpert/ui/tooltip.py +194 -0
- graxpert/ui/ui_events.py +19 -0
- graxpert/ui/widgets.py +297 -0
- graxpert/ui_scaling.py +13 -0
- graxpert/version.py +2 -0
- graxpert-3.2.0a0.dev4.dist-info/METADATA +219 -0
- graxpert-3.2.0a0.dev4.dist-info/RECORD +58 -0
- graxpert-3.2.0a0.dev4.dist-info/WHEEL +5 -0
- graxpert-3.2.0a0.dev4.dist-info/entry_points.txt +2 -0
- graxpert-3.2.0a0.dev4.dist-info/top_level.txt +1 -0
graxpert/ui/left_menu.py
ADDED
|
@@ -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)
|