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
graxpert/stretch.py ADDED
@@ -0,0 +1,151 @@
1
+ import multiprocessing
2
+ multiprocessing.freeze_support()
3
+
4
+ import logging
5
+ from concurrent.futures import wait
6
+ from multiprocessing import shared_memory
7
+
8
+ import numpy as np
9
+
10
+ from graxpert.mp_logging import get_logging_queue, worker_configurer
11
+ from graxpert.parallel_processing import executor
12
+
13
+ from dataclasses import dataclass
14
+
15
+ @dataclass
16
+ class MTFStretchParameters:
17
+ midtone: float
18
+ shadow_clipping: float
19
+ highlight_clipping: float = 1.0
20
+
21
+ class StretchParameters:
22
+ stretch_option: str
23
+ bg: float
24
+ sigma: float
25
+ do_stretch: bool = True
26
+ channels_linked: bool = False
27
+ images_linked: bool = False
28
+
29
+ def __init__(self, stretch_option: str, channels_linked: bool = False, images_linked: bool = False):
30
+ self.stretch_option = stretch_option
31
+ self.channels_linked = channels_linked
32
+ self. images_linked = images_linked
33
+
34
+ if stretch_option == "No Stretch":
35
+ self.do_stretch = False
36
+
37
+ elif stretch_option == "10% Bg, 3 sigma":
38
+ self.bg = 0.1
39
+ self.sigma = 3.0
40
+
41
+ elif stretch_option == "15% Bg, 3 sigma":
42
+ self.bg = 0.15
43
+ self.sigma = 3.0
44
+
45
+ elif stretch_option == "20% Bg, 3 sigma":
46
+ self.bg = 0.2
47
+ self.sigma = 3.0
48
+
49
+ elif stretch_option == "30% Bg, 2 sigma":
50
+ self.bg = 0.3
51
+ self.sigma = 2.0
52
+
53
+
54
+
55
+ def stretch(data, stretch_params: StretchParameters):
56
+ if not stretch_params.do_stretch:
57
+ return data
58
+
59
+ mtf_stretch_param = calculate_mtf_stretch_parameters_for_image(stretch_params, data)
60
+ return stretch_all([data], [mtf_stretch_param])[0]
61
+
62
+
63
+ def stretch_all(datas, mtf_stretch_params: list[MTFStretchParameters]):
64
+
65
+ futures = []
66
+ shms = []
67
+ copies = []
68
+ result = []
69
+ logging_queue = get_logging_queue()
70
+
71
+ for data, mtf_stretch_param in zip(datas, mtf_stretch_params):
72
+ shm = shared_memory.SharedMemory(create=True, size=data.nbytes)
73
+ copy = np.ndarray(data.shape, dtype=data.dtype, buffer=shm.buf)
74
+ np.copyto(copy, data)
75
+ shms.append(shm)
76
+ copies.append(copy)
77
+
78
+ for c in range(copy.shape[-1]):
79
+ futures.insert(c, executor.submit(stretch_channel, shm.name, c, mtf_stretch_param[c], copy.shape, copy.dtype, logging_queue, worker_configurer))
80
+ wait(futures)
81
+
82
+ for copy in copies:
83
+ copy = np.copy(copy)
84
+ result.append(copy)
85
+
86
+ for shm in shms:
87
+ shm.close()
88
+ shm.unlink()
89
+
90
+ return result
91
+
92
+
93
+ def calculate_mtf_stretch_parameters_for_image(stretch_params, image):
94
+ if stretch_params.channels_linked:
95
+ mtf_stretch_param = calculate_mtf_stretch_parameters_for_channel(stretch_params, image)
96
+ return [mtf_stretch_param] * image.shape[-1]
97
+
98
+ else:
99
+ return [calculate_mtf_stretch_parameters_for_channel(stretch_params, image[:,:,i]) for i in range(image.shape[-1])]
100
+
101
+ def calculate_mtf_stretch_parameters_for_channel(stretch_params, channel):
102
+ channel = channel.flatten()[::4]
103
+
104
+ indx_clip = np.logical_and(channel < 1.0, channel > 0.0)
105
+ median = np.median(channel[indx_clip])
106
+ mad = np.median(np.abs(channel[indx_clip]-median))
107
+
108
+ shadow_clipping = np.clip(median - stretch_params.sigma*mad, 0, 1.0)
109
+ highlight_clipping = 1.0
110
+ midtone = MTF((median-shadow_clipping)/(highlight_clipping - shadow_clipping), stretch_params.bg)
111
+
112
+ return MTFStretchParameters(midtone, shadow_clipping)
113
+
114
+
115
+ def stretch_channel(shm_name, c, mtf_stretch_params, shape, dtype, logging_queue, logging_configurer):
116
+
117
+ logging_configurer(logging_queue)
118
+ logging.info("stretch.stretch_channel started")
119
+
120
+ existing_shm = shared_memory.SharedMemory(name=shm_name)
121
+ channels = np.ndarray(shape, dtype, buffer=existing_shm.buf) #[:,:,channel_idx]
122
+ channel = channels[:,:,c]
123
+
124
+ try:
125
+ channel[channel <= mtf_stretch_params.shadow_clipping] = 0.0
126
+ channel[channel >= mtf_stretch_params.highlight_clipping] = 1.0
127
+
128
+ indx_inside = np.logical_and(channel > mtf_stretch_params.shadow_clipping, channel < mtf_stretch_params.highlight_clipping)
129
+
130
+ channel[indx_inside] = (channel[indx_inside]-mtf_stretch_params.shadow_clipping)/(mtf_stretch_params.highlight_clipping - mtf_stretch_params.shadow_clipping)
131
+
132
+ channel = MTF(channel, mtf_stretch_params.midtone)
133
+
134
+ except:
135
+ logging.exception("An error occured while stretching a color channel")
136
+ finally:
137
+ existing_shm.close()
138
+
139
+ logging.info("stretch.stretch_channel finished")
140
+
141
+
142
+ def MTF(data, midtone):
143
+
144
+ if type(data) is np.ndarray:
145
+ data[:] = (midtone-1)*data[:]/((2*midtone-1)*data[:]-midtone)
146
+ else:
147
+ data = (midtone-1) * data / ((2*midtone-1) * data - midtone)
148
+
149
+ return data
150
+
151
+
File without changes
@@ -0,0 +1,367 @@
1
+ {
2
+ "CTk": {
3
+ "fg_color": [
4
+ "gray95",
5
+ "gray10"
6
+ ]
7
+ },
8
+ "CTkToplevel": {
9
+ "fg_color": [
10
+ "gray95",
11
+ "gray10"
12
+ ]
13
+ },
14
+ "CTkFrame": {
15
+ "corner_radius": 6,
16
+ "border_width": 0,
17
+ "fg_color": [
18
+ "gray90",
19
+ "gray13"
20
+ ],
21
+ "top_fg_color": [
22
+ "gray85",
23
+ "gray16"
24
+ ],
25
+ "border_color": [
26
+ "gray65",
27
+ "gray28"
28
+ ]
29
+ },
30
+ "CTkButton": {
31
+ "corner_radius": 6,
32
+ "border_width": 0,
33
+ "fg_color": [
34
+ "#3a7ebf",
35
+ "#1f538d"
36
+ ],
37
+ "hover_color": [
38
+ "#325882",
39
+ "#14375e"
40
+ ],
41
+ "border_color": [
42
+ "#3E454A",
43
+ "#949A9F"
44
+ ],
45
+ "text_color": [
46
+ "#DCE4EE",
47
+ "#DCE4EE"
48
+ ],
49
+ "text_color_disabled": [
50
+ "gray74",
51
+ "gray60"
52
+ ]
53
+ },
54
+ "Accent.CTkButton": {
55
+ "fg_color": "#247f4c",
56
+ "hover_color": "#154b2d"
57
+ },
58
+ "Help.CTkButton": {
59
+ "fg_color": "#c46f1a",
60
+ "hover_color": "#73410f"
61
+ },
62
+ "CTkLabel": {
63
+ "corner_radius": 0,
64
+ "fg_color": "transparent",
65
+ "text_color": [
66
+ "gray14",
67
+ "gray84"
68
+ ]
69
+ },
70
+ "CTkEntry": {
71
+ "corner_radius": 6,
72
+ "border_width": 2,
73
+ "fg_color": [
74
+ "#F9F9FA",
75
+ "#343638"
76
+ ],
77
+ "border_color": [
78
+ "#979DA2",
79
+ "#565B5E"
80
+ ],
81
+ "text_color": [
82
+ "gray14",
83
+ "gray84"
84
+ ],
85
+ "placeholder_text_color": [
86
+ "gray52",
87
+ "gray62"
88
+ ]
89
+ },
90
+ "CTkCheckBox": {
91
+ "corner_radius": 6,
92
+ "border_width": 3,
93
+ "fg_color": [
94
+ "#3a7ebf",
95
+ "#1f538d"
96
+ ],
97
+ "border_color": [
98
+ "#3E454A",
99
+ "#949A9F"
100
+ ],
101
+ "hover_color": [
102
+ "#325882",
103
+ "#14375e"
104
+ ],
105
+ "checkmark_color": [
106
+ "#DCE4EE",
107
+ "gray90"
108
+ ],
109
+ "text_color": [
110
+ "gray14",
111
+ "gray84"
112
+ ],
113
+ "text_color_disabled": [
114
+ "gray60",
115
+ "gray45"
116
+ ]
117
+ },
118
+ "CTkSwitch": {
119
+ "corner_radius": 1000,
120
+ "border_width": 3,
121
+ "button_length": 0,
122
+ "fg_color": [
123
+ "#939BA2",
124
+ "#4A4D50"
125
+ ],
126
+ "progress_color": [
127
+ "#3a7ebf",
128
+ "#1f538d"
129
+ ],
130
+ "button_color": [
131
+ "gray36",
132
+ "#D5D9DE"
133
+ ],
134
+ "button_hover_color": [
135
+ "gray20",
136
+ "gray100"
137
+ ],
138
+ "text_color": [
139
+ "gray14",
140
+ "gray84"
141
+ ],
142
+ "text_color_disabled": [
143
+ "gray60",
144
+ "gray45"
145
+ ]
146
+ },
147
+ "CTkRadioButton": {
148
+ "corner_radius": 1000,
149
+ "border_width_checked": 6,
150
+ "border_width_unchecked": 3,
151
+ "fg_color": [
152
+ "#3a7ebf",
153
+ "#1f538d"
154
+ ],
155
+ "border_color": [
156
+ "#3E454A",
157
+ "#949A9F"
158
+ ],
159
+ "hover_color": [
160
+ "#325882",
161
+ "#14375e"
162
+ ],
163
+ "text_color": [
164
+ "gray14",
165
+ "gray84"
166
+ ],
167
+ "text_color_disabled": [
168
+ "gray60",
169
+ "gray45"
170
+ ]
171
+ },
172
+ "CTkProgressBar": {
173
+ "corner_radius": 1000,
174
+ "border_width": 0,
175
+ "fg_color": [
176
+ "#939BA2",
177
+ "#4A4D50"
178
+ ],
179
+ "progress_color": [
180
+ "#3a7ebf",
181
+ "#1f538d"
182
+ ],
183
+ "border_color": [
184
+ "gray",
185
+ "gray"
186
+ ]
187
+ },
188
+ "CTkSlider": {
189
+ "corner_radius": 1000,
190
+ "button_corner_radius": 1000,
191
+ "border_width": 6,
192
+ "button_length": 0,
193
+ "fg_color": [
194
+ "#939BA2",
195
+ "#4A4D50"
196
+ ],
197
+ "progress_color": [
198
+ "gray40",
199
+ "#AAB0B5"
200
+ ],
201
+ "button_color": [
202
+ "#3a7ebf",
203
+ "#1f538d"
204
+ ],
205
+ "button_hover_color": [
206
+ "#325882",
207
+ "#14375e"
208
+ ]
209
+ },
210
+ "CTkOptionMenu": {
211
+ "corner_radius": 6,
212
+ "fg_color": [
213
+ "#3a7ebf",
214
+ "#1f538d"
215
+ ],
216
+ "button_color": [
217
+ "#325882",
218
+ "#14375e"
219
+ ],
220
+ "button_hover_color": [
221
+ "#234567",
222
+ "#1e2c40"
223
+ ],
224
+ "text_color": [
225
+ "#DCE4EE",
226
+ "#DCE4EE"
227
+ ],
228
+ "text_color_disabled": [
229
+ "gray74",
230
+ "gray60"
231
+ ]
232
+ },
233
+ "CTkComboBox": {
234
+ "corner_radius": 6,
235
+ "border_width": 2,
236
+ "fg_color": [
237
+ "#F9F9FA",
238
+ "#343638"
239
+ ],
240
+ "border_color": [
241
+ "#979DA2",
242
+ "#565B5E"
243
+ ],
244
+ "button_color": [
245
+ "#979DA2",
246
+ "#565B5E"
247
+ ],
248
+ "button_hover_color": [
249
+ "#6E7174",
250
+ "#7A848D"
251
+ ],
252
+ "text_color": [
253
+ "gray14",
254
+ "gray84"
255
+ ],
256
+ "text_color_disabled": [
257
+ "gray50",
258
+ "gray45"
259
+ ]
260
+ },
261
+ "CTkScrollbar": {
262
+ "corner_radius": 1000,
263
+ "border_spacing": 4,
264
+ "fg_color": "transparent",
265
+ "button_color": [
266
+ "gray55",
267
+ "gray41"
268
+ ],
269
+ "button_hover_color": [
270
+ "gray40",
271
+ "gray53"
272
+ ]
273
+ },
274
+ "CTkSegmentedButton": {
275
+ "corner_radius": 6,
276
+ "border_width": 2,
277
+ "fg_color": [
278
+ "#979DA2",
279
+ "gray29"
280
+ ],
281
+ "selected_color": [
282
+ "#3a7ebf",
283
+ "#1f538d"
284
+ ],
285
+ "selected_hover_color": [
286
+ "#325882",
287
+ "#14375e"
288
+ ],
289
+ "unselected_color": [
290
+ "#979DA2",
291
+ "gray29"
292
+ ],
293
+ "unselected_hover_color": [
294
+ "gray70",
295
+ "gray41"
296
+ ],
297
+ "text_color": [
298
+ "#DCE4EE",
299
+ "#DCE4EE"
300
+ ],
301
+ "text_color_disabled": [
302
+ "gray74",
303
+ "gray60"
304
+ ]
305
+ },
306
+ "CTkTextbox": {
307
+ "corner_radius": 6,
308
+ "border_width": 0,
309
+ "fg_color": [
310
+ "gray100",
311
+ "gray20"
312
+ ],
313
+ "border_color": [
314
+ "#979DA2",
315
+ "#565B5E"
316
+ ],
317
+ "text_color": [
318
+ "gray14",
319
+ "gray84"
320
+ ],
321
+ "scrollbar_button_color": [
322
+ "gray55",
323
+ "gray41"
324
+ ],
325
+ "scrollbar_button_hover_color": [
326
+ "gray40",
327
+ "gray53"
328
+ ]
329
+ },
330
+ "CTkScrollableFrame": {
331
+ "label_fg_color": [
332
+ "gray80",
333
+ "gray21"
334
+ ]
335
+ },
336
+ "DropdownMenu": {
337
+ "fg_color": [
338
+ "gray90",
339
+ "gray20"
340
+ ],
341
+ "hover_color": [
342
+ "gray75",
343
+ "gray28"
344
+ ],
345
+ "text_color": [
346
+ "gray14",
347
+ "gray84"
348
+ ]
349
+ },
350
+ "CTkFont": {
351
+ "macOS": {
352
+ "family": "SF Display",
353
+ "size": 13,
354
+ "weight": "normal"
355
+ },
356
+ "Windows": {
357
+ "family": "Roboto",
358
+ "size": 13,
359
+ "weight": "normal"
360
+ },
361
+ "Linux": {
362
+ "family": "Roboto",
363
+ "size": 13,
364
+ "weight": "normal"
365
+ }
366
+ }
367
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,115 @@
1
+ import tkinter as tk
2
+
3
+ from customtkinter import CTkFrame
4
+
5
+ from graxpert.application.app import graxpert
6
+ from graxpert.application.app_events import AppEvents
7
+ from graxpert.application.eventbus import eventbus
8
+ from graxpert.commands import InitHandler
9
+ from graxpert.ui.canvas import Canvas
10
+ from graxpert.ui.left_menu import LeftMenu
11
+ from graxpert.ui.right_menu import AdvancedFrame, HelpFrame
12
+ from graxpert.ui.statusbar import StatusBar
13
+ from graxpert.ui.ui_events import UiEvents
14
+ from graxpert.ui.widgets import default_label_width, padx
15
+
16
+
17
+ class ApplicationFrame(CTkFrame):
18
+ def __init__(self, master, **kwargs):
19
+ super().__init__(master, **kwargs)
20
+
21
+ self.initial_title = master.title()
22
+ self.show_help = False
23
+ self.show_advanced = False
24
+
25
+ self.create_children()
26
+ self.setup_layout()
27
+ self.place_children()
28
+ self.create_bindings()
29
+ self.register_events()
30
+
31
+ def create_children(self):
32
+ self.left_menu = LeftMenu(self, fg_color="transparent", width=default_label_width + padx + 16)
33
+ self.canvas = Canvas(self)
34
+ self.help_frame = HelpFrame(self, fg_color="transparent", width=300)
35
+ self.advanced_frame = AdvancedFrame(self, fg_color="transparent", width=300)
36
+ self.statusbar_frame = StatusBar(self, fg_color="transparent")
37
+
38
+ def setup_layout(self):
39
+ self.columnconfigure(0, weight=0)
40
+ self.columnconfigure(1, weight=100)
41
+ self.columnconfigure(2, weight=0)
42
+ self.rowconfigure(0, weight=1)
43
+ self.rowconfigure(1, weight=0)
44
+
45
+ def place_children(self):
46
+ self.left_menu.grid(column=0, row=0, rowspan=2, ipadx=padx, sticky=tk.NS)
47
+ self.canvas.grid(column=1, row=0, sticky=tk.NSEW)
48
+ self.statusbar_frame.grid(column=1, row=1, sticky=tk.NSEW)
49
+
50
+ def create_bindings(self):
51
+ self.master.bind("<BackSpace>", lambda e: eventbus.emit(UiEvents.RESET_ZOOM_REQUEST)) # backspace -> reset zoom
52
+ self.master.bind("<Control-Key-0>", lambda e: eventbus.emit(UiEvents.RESET_ZOOM_REQUEST)) # ctrl + 0 -> reset zoom (Windows)
53
+ self.master.bind("<Command-Key-0>", lambda e: eventbus.emit(UiEvents.RESET_ZOOM_REQUEST)) # cmd + 0 -> reset zoom (Mac)
54
+ self.master.bind("<Control-Key-KP_0>", lambda e: eventbus.emit(UiEvents.RESET_ZOOM_REQUEST)) # ctrl + numpad 0 -> reset zoom (Windows)
55
+ self.master.bind("<Command-Key-KP_0>", lambda e: eventbus.emit(UiEvents.RESET_ZOOM_REQUEST)) # cmd + numpad 0 -> reset zoom (Mac)
56
+ self.master.bind("<Control-l>", lambda e: eventbus.emit(AppEvents.OPEN_FILE_DIALOG_REQUEST))
57
+ self.master.bind("<Command-l>", lambda e: eventbus.emit(AppEvents.OPEN_FILE_DIALOG_REQUEST))
58
+ self.master.bind("<Control-c>", lambda e: eventbus.emit(AppEvents.CALCULATE_REQUEST))
59
+ self.master.bind("<Command-c>", lambda e: eventbus.emit(AppEvents.CALCULATE_REQUEST))
60
+ self.master.bind("<Control-d>", lambda e: eventbus.emit(AppEvents.DENOISE_REQUEST))
61
+ self.master.bind("<Command-d>", lambda e: eventbus.emit(AppEvents.DENOISE_REQUEST))
62
+ self.master.bind("<Control-s>", lambda e: eventbus.emit(AppEvents.SAVE_REQUEST))
63
+ self.master.bind("<Command-s>", lambda e: eventbus.emit(AppEvents.SAVE_REQUEST))
64
+ self.master.bind("<Control-z>", self.undo) # undo
65
+ self.master.bind("<Control-y>", self.redo) # redo
66
+ self.master.bind("<Command-z>", self.undo) # undo on macs
67
+ self.master.bind("<Command-y>", self.redo) # redo on macs
68
+
69
+ def register_events(self):
70
+ eventbus.add_listener(UiEvents.HELP_FRAME_TOGGLED, self.toggle_help)
71
+ eventbus.add_listener(UiEvents.ADVANCED_FRAME_TOGGLED, self.toggle_advanced)
72
+ eventbus.add_listener(AppEvents.LOAD_IMAGE_END, self.on_load_image_end)
73
+
74
+ def place_right_frame(self):
75
+ self.help_frame.grid_forget()
76
+ self.advanced_frame.grid_forget()
77
+
78
+ if self.show_help:
79
+ self.help_frame.grid(column=2, row=0, rowspan=2, sticky=tk.NSEW)
80
+
81
+ if self.show_advanced:
82
+ self.advanced_frame.grid(column=2, row=0, rowspan=2, sticky=tk.NSEW)
83
+
84
+ # event handling
85
+ def on_load_image_end(self, event):
86
+ self.master.title(f'{self.initial_title} - {event["filename"]}')
87
+
88
+ def redo(self, event):
89
+ if graxpert.cmd.next is not None:
90
+ redo = graxpert.cmd.redo()
91
+ graxpert.cmd = redo
92
+ eventbus.emit(AppEvents.REDRAW_POINTS_REQUEST)
93
+
94
+ # widget logic
95
+ def toggle_help(self, event):
96
+ if self.show_help:
97
+ self.show_help = False
98
+ else:
99
+ self.show_advanced = False
100
+ self.show_help = True
101
+ self.place_right_frame()
102
+
103
+ def toggle_advanced(self, event):
104
+ if self.show_advanced:
105
+ self.show_advanced = False
106
+ else:
107
+ self.show_help = False
108
+ self.show_advanced = True
109
+ self.place_right_frame()
110
+
111
+ def undo(self, event):
112
+ if not type(graxpert.cmd.handler) is InitHandler:
113
+ undo = graxpert.cmd.undo()
114
+ graxpert.cmd = undo
115
+ eventbus.emit(AppEvents.REDRAW_POINTS_REQUEST)