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
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import webbrowser
|
|
3
|
+
from tkinter import messagebox
|
|
4
|
+
|
|
5
|
+
import customtkinter as ctk
|
|
6
|
+
from customtkinter import CTkFont, CTkImage, CTkLabel, CTkSwitch, CTkTextbox
|
|
7
|
+
from packaging import version
|
|
8
|
+
|
|
9
|
+
from graxpert.ai_model_handling import bge_ai_models_dir, deconvolution_object_ai_models_dir, deconvolution_stars_ai_models_dir, denoise_ai_models_dir, list_local_versions, list_remote_versions
|
|
10
|
+
from graxpert.application.app import graxpert
|
|
11
|
+
from graxpert.application.app_events import AppEvents
|
|
12
|
+
from graxpert.application.eventbus import eventbus
|
|
13
|
+
from graxpert.localization import _, lang
|
|
14
|
+
from graxpert.resource_utils import resource_image
|
|
15
|
+
from graxpert.s3_secrets import bge_bucket_name, deconvolution_object_bucket_name, deconvolution_stars_bucket_name, denoise_bucket_name
|
|
16
|
+
from graxpert.ui.widgets import GraXpertOptionMenu, GraXpertScrollableFrame, ProcessingStep, ValueSlider, padx, pady
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HelpText(CTkTextbox):
|
|
20
|
+
def __init__(self, master, text="", rows=1, font=None, **kwargs):
|
|
21
|
+
super().__init__(master, width=250, fg_color="transparent", wrap="word", activate_scrollbars=False, **kwargs)
|
|
22
|
+
self.configure(height=self._font.metrics("linespace") * rows + 4 * pady)
|
|
23
|
+
self.insert("0.0", text)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RightFrameBase(GraXpertScrollableFrame):
|
|
27
|
+
def __init__(self, master, **kwargs):
|
|
28
|
+
super().__init__(master, **kwargs)
|
|
29
|
+
self.row = 0
|
|
30
|
+
self.heading_font = CTkFont(size=15, weight="bold")
|
|
31
|
+
self.heading_font2 = CTkFont(size=13, weight="bold")
|
|
32
|
+
|
|
33
|
+
def nrow(self):
|
|
34
|
+
self.row += 1
|
|
35
|
+
return self.row
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class HelpFrame(RightFrameBase):
|
|
39
|
+
def __init__(self, master, **kwargs):
|
|
40
|
+
super().__init__(master, **kwargs)
|
|
41
|
+
|
|
42
|
+
self.img = resource_image("GraXpert_LOGO_Hauptvariante.png")
|
|
43
|
+
self.create_and_place_children()
|
|
44
|
+
self.setup_layout()
|
|
45
|
+
|
|
46
|
+
def _on_destroy(self, event):
|
|
47
|
+
"""This method is called when the widget is destroyed."""
|
|
48
|
+
if self.img:
|
|
49
|
+
self.img.close()
|
|
50
|
+
|
|
51
|
+
def default_grid(self):
|
|
52
|
+
return {"column": 0, "row": self.nrow(), "padx": padx, "pady": pady, "sticky": tk.EW}
|
|
53
|
+
|
|
54
|
+
def create_and_place_children(self):
|
|
55
|
+
|
|
56
|
+
logo = CTkImage(
|
|
57
|
+
light_image=self.img,
|
|
58
|
+
dark_image=self.img,
|
|
59
|
+
size=(225, 111),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
CTkLabel(self, image=logo, text="").grid(column=0, row=self.nrow(), padx=padx, pady=pady, sticky=tk.NSEW)
|
|
63
|
+
CTkLabel(self, text=_("Instructions"), font=self.heading_font).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
64
|
+
|
|
65
|
+
ProcessingStep(self, number=1, indent=0, title=_(" Loading")).grid(**self.default_grid())
|
|
66
|
+
HelpText(self, text=_("Load your image.")).grid(**self.default_grid())
|
|
67
|
+
|
|
68
|
+
ProcessingStep(self, number=2, indent=0, title=_(" Stretch Options")).grid(**self.default_grid())
|
|
69
|
+
HelpText(self, rows=2, text=_("Stretch your image if necessary to reveal gradients.")).grid(**self.default_grid())
|
|
70
|
+
|
|
71
|
+
ProcessingStep(self, number=3, indent=0, title=_(" Sample Selection")).grid(**self.default_grid())
|
|
72
|
+
HelpText(
|
|
73
|
+
self,
|
|
74
|
+
rows=5,
|
|
75
|
+
text=_("Select background points\n a) manually with left click\n b) automatically via grid (grid selection)" "\nYou can remove already set points by right clicking on them."),
|
|
76
|
+
).grid(**self.default_grid())
|
|
77
|
+
|
|
78
|
+
ProcessingStep(self, number=4, indent=0, title=_(" Calculation")).grid(**self.default_grid())
|
|
79
|
+
HelpText(self, rows=2, text=_("Click on Calculate Background to get the processed image.")).grid(**self.default_grid())
|
|
80
|
+
|
|
81
|
+
ProcessingStep(self, number=5, indent=0, title=_(" Saving")).grid(**self.default_grid())
|
|
82
|
+
HelpText(self, text=_("Save the processed image.")).grid(**self.default_grid())
|
|
83
|
+
|
|
84
|
+
CTkLabel(self, text=_("Keybindings"), font=self.heading_font).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
85
|
+
|
|
86
|
+
HelpText(self, text=_("Left click on picture: Set sample point")).grid(**self.default_grid())
|
|
87
|
+
HelpText(self, rows=2, text=_("Left click on picture + drag: Move picture")).grid(**self.default_grid())
|
|
88
|
+
HelpText(self, rows=2, text=_("Left click on sample point + drag: Move sample point")).grid(**self.default_grid())
|
|
89
|
+
HelpText(self, rows=2, text=_("Right click on sample point: Delete sample point")).grid(**self.default_grid())
|
|
90
|
+
HelpText(self, text=_("Mouse wheel: Zoom")).grid(**self.default_grid())
|
|
91
|
+
HelpText(self, rows=3, text=_("Ctrl+Z/Y: Undo/Redo sample point")).grid(**self.default_grid())
|
|
92
|
+
|
|
93
|
+
CTkLabel(self, text=_("Licenses"), font=self.heading_font).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
94
|
+
|
|
95
|
+
def callback(url):
|
|
96
|
+
webbrowser.open_new(url)
|
|
97
|
+
|
|
98
|
+
row = self.nrow()
|
|
99
|
+
HelpText(self, text=_("GraXpert is licensed under GPL-3:")).grid(column=0, row=row, padx=padx, pady=pady, sticky=tk.W)
|
|
100
|
+
url_link_1 = "https://raw.githubusercontent.com/Steffenhir/GraXpert/main/License.md"
|
|
101
|
+
url_label_1 = CTkLabel(self, text="<Link>", text_color="dodger blue")
|
|
102
|
+
url_label_1.grid(column=0, row=row, padx=padx, sticky=tk.E)
|
|
103
|
+
url_label_1.bind("<Button-1>", lambda e: callback(url_link_1))
|
|
104
|
+
|
|
105
|
+
row = self.nrow()
|
|
106
|
+
HelpText(self, rows=2, text=_("Background Extraction AI models are licensed under CC BY-NC-SA:")).grid(column=0, row=row, padx=padx, pady=pady, sticky=tk.W)
|
|
107
|
+
url_link_2 = "https://raw.githubusercontent.com/Steffenhir/GraXpert/main/licenses/BGE-Model-LICENSE.html"
|
|
108
|
+
url_label_2 = CTkLabel(self, text="<Link>", text_color="dodger blue")
|
|
109
|
+
url_label_2.grid(column=0, row=row, padx=padx, sticky=tk.E)
|
|
110
|
+
url_label_2.bind("<Button-1>", lambda e: callback(url_link_2))
|
|
111
|
+
|
|
112
|
+
row = self.nrow()
|
|
113
|
+
HelpText(self, rows=2, text=_("Deconvolution AI models are licensed under CC BY-NC-SA:")).grid(column=0, row=row, padx=padx, pady=pady, sticky=tk.W)
|
|
114
|
+
url_link_3 = "https://raw.githubusercontent.com/Steffenhir/GraXpert/main/licenses/Deconvolution-Model-LICENSE.html"
|
|
115
|
+
url_label_3 = CTkLabel(self, text="<Link>", text_color="dodger blue")
|
|
116
|
+
url_label_3.grid(column=0, row=row, padx=padx, sticky=tk.E)
|
|
117
|
+
url_label_3.bind("<Button-1>", lambda e: callback(url_link_3))
|
|
118
|
+
|
|
119
|
+
row = self.nrow()
|
|
120
|
+
HelpText(self, rows=2, text=_("Denoising AI models are licensed under CC BY-NC-SA:")).grid(column=0, row=row, padx=padx, pady=pady, sticky=tk.W)
|
|
121
|
+
url_link_3 = "https://raw.githubusercontent.com/Steffenhir/GraXpert/main/licenses/Denoise-Model-LICENSE.html"
|
|
122
|
+
url_label_3 = CTkLabel(self, text="<Link>", text_color="dodger blue")
|
|
123
|
+
url_label_3.grid(column=0, row=row, padx=padx, sticky=tk.E)
|
|
124
|
+
url_label_3.bind("<Button-1>", lambda e: callback(url_link_3))
|
|
125
|
+
|
|
126
|
+
def setup_layout(self):
|
|
127
|
+
self.columnconfigure(0, weight=1)
|
|
128
|
+
self.rowconfigure(0, weight=1)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class AdvancedFrame(RightFrameBase):
|
|
132
|
+
def __init__(self, master, **kwargs):
|
|
133
|
+
super().__init__(master, **kwargs)
|
|
134
|
+
|
|
135
|
+
# sample points
|
|
136
|
+
self.sample_size = tk.IntVar()
|
|
137
|
+
self.sample_size.set(graxpert.prefs.sample_size)
|
|
138
|
+
self.sample_size.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.SAMPLE_SIZE_CHANGED, {"sample_size": self.sample_size.get()}))
|
|
139
|
+
|
|
140
|
+
self.sample_color = tk.IntVar()
|
|
141
|
+
self.sample_color.set(graxpert.prefs.sample_color)
|
|
142
|
+
self.sample_color.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.SAMPLE_COLOR_CHANGED, {"sample_color": self.sample_color.get()}))
|
|
143
|
+
|
|
144
|
+
# interpolation
|
|
145
|
+
self.rbf_kernels = ["thin_plate", "quintic", "cubic", "linear"]
|
|
146
|
+
self.rbf_kernel = tk.StringVar()
|
|
147
|
+
self.rbf_kernel.set(graxpert.prefs.RBF_kernel)
|
|
148
|
+
self.rbf_kernel.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.RBF_KERNEL_CHANGED, {"RBF_kernel": self.rbf_kernel.get()}))
|
|
149
|
+
|
|
150
|
+
self.spline_orders = ["1", "2", "3", "4", "5"]
|
|
151
|
+
self.spline_order = tk.StringVar()
|
|
152
|
+
self.spline_order.set(str(graxpert.prefs.spline_order))
|
|
153
|
+
self.spline_order.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.SPLINE_ORDER_CHANGED, {"spline_order": int(self.spline_order.get())}))
|
|
154
|
+
|
|
155
|
+
self.corr_types = ["Subtraction", "Division"]
|
|
156
|
+
self.corr_type = tk.StringVar()
|
|
157
|
+
self.corr_type.set(graxpert.prefs.corr_type)
|
|
158
|
+
self.corr_type.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.CORRECTION_TYPE_CHANGED, {"corr_type": self.corr_type.get()}))
|
|
159
|
+
|
|
160
|
+
# interface
|
|
161
|
+
self.langs = ["English", "Deutsch"]
|
|
162
|
+
self.lang = tk.StringVar()
|
|
163
|
+
self.lang.set(graxpert.prefs.lang)
|
|
164
|
+
self.lang.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.LANGUAGE_CHANGED, {"lang": self.lang.get()}))
|
|
165
|
+
|
|
166
|
+
self.scaling = tk.DoubleVar()
|
|
167
|
+
self.scaling.set(graxpert.prefs.scaling)
|
|
168
|
+
self.scaling.trace_add("write", self.on_scaling_change)
|
|
169
|
+
|
|
170
|
+
# bge ai model
|
|
171
|
+
bge_remote_versions = list_remote_versions(bge_bucket_name)
|
|
172
|
+
bge_local_versions = list_local_versions(bge_ai_models_dir)
|
|
173
|
+
self.bge_ai_options = set([])
|
|
174
|
+
self.bge_ai_options.update([rv["version"] for rv in bge_remote_versions])
|
|
175
|
+
self.bge_ai_options.update([lv["version"] for lv in bge_local_versions])
|
|
176
|
+
self.bge_ai_options = sorted(self.bge_ai_options, key=lambda k: version.parse(k), reverse=True)
|
|
177
|
+
|
|
178
|
+
self.bge_ai_version = tk.StringVar(master)
|
|
179
|
+
self.bge_ai_version.set("None") # default value
|
|
180
|
+
if graxpert.prefs.bge_ai_version is not None:
|
|
181
|
+
self.bge_ai_version.set(graxpert.prefs.bge_ai_version)
|
|
182
|
+
else:
|
|
183
|
+
self.bge_ai_options.insert(0, "None")
|
|
184
|
+
self.bge_ai_version.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.BGE_AI_VERSION_CHANGED, {"bge_ai_version": self.bge_ai_version.get()}))
|
|
185
|
+
|
|
186
|
+
# object deconvolution ai models
|
|
187
|
+
deconvolution_object_ai_remote_versions = list_remote_versions(deconvolution_object_bucket_name)
|
|
188
|
+
deconvolution_object_ai_local_versions = list_local_versions(deconvolution_object_ai_models_dir)
|
|
189
|
+
self.deconvolution_object_ai_options = set([])
|
|
190
|
+
self.deconvolution_object_ai_options.update([rv["version"] for rv in deconvolution_object_ai_remote_versions])
|
|
191
|
+
self.deconvolution_object_ai_options.update([lv["version"] for lv in deconvolution_object_ai_local_versions])
|
|
192
|
+
self.deconvolution_object_ai_options = sorted(self.deconvolution_object_ai_options, key=lambda k: version.parse(k), reverse=True)
|
|
193
|
+
|
|
194
|
+
self.deconvolution_object_ai_version = tk.StringVar(master)
|
|
195
|
+
self.deconvolution_object_ai_version.set("None") # default value
|
|
196
|
+
if graxpert.prefs.deconvolution_object_ai_version is not None:
|
|
197
|
+
self.deconvolution_object_ai_version.set(graxpert.prefs.deconvolution_object_ai_version)
|
|
198
|
+
else:
|
|
199
|
+
self.deconvolution_object_ai_options.insert(0, "None")
|
|
200
|
+
self.deconvolution_object_ai_version.trace_add(
|
|
201
|
+
"write", lambda a, b, c: eventbus.emit(AppEvents.DECONVOLUTION_OBJECT_AI_VERSION_CHANGED, {"deconvolution_object_ai_version": self.deconvolution_object_ai_version.get()})
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# stars deconvolution ai models
|
|
205
|
+
deconvolution_stars_ai_remote_versions = list_remote_versions(deconvolution_stars_bucket_name)
|
|
206
|
+
deconvolution_stars_ai_local_versions = list_local_versions(deconvolution_stars_ai_models_dir)
|
|
207
|
+
self.deconvolution_stars_ai_options = set([])
|
|
208
|
+
self.deconvolution_stars_ai_options.update([rv["version"] for rv in deconvolution_stars_ai_remote_versions])
|
|
209
|
+
self.deconvolution_stars_ai_options.update([lv["version"] for lv in deconvolution_stars_ai_local_versions])
|
|
210
|
+
self.deconvolution_stars_ai_options = sorted(self.deconvolution_stars_ai_options, key=lambda k: version.parse(k), reverse=True)
|
|
211
|
+
|
|
212
|
+
self.deconvolution_stars_ai_version = tk.StringVar(master)
|
|
213
|
+
self.deconvolution_stars_ai_version.set("None") # default value
|
|
214
|
+
if graxpert.prefs.deconvolution_stars_ai_version is not None:
|
|
215
|
+
self.deconvolution_stars_ai_version.set(graxpert.prefs.deconvolution_stars_ai_version)
|
|
216
|
+
else:
|
|
217
|
+
self.deconvolution_stars_ai_options.insert(0, "None")
|
|
218
|
+
self.deconvolution_stars_ai_version.trace_add(
|
|
219
|
+
"write", lambda a, b, c: eventbus.emit(AppEvents.DECONVOLUTION_STARS_AI_VERSION_CHANGED, {"deconvolution_stars_ai_version": self.deconvolution_stars_ai_version.get()})
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# denoise ai model
|
|
223
|
+
denoise_remote_versions = list_remote_versions(denoise_bucket_name)
|
|
224
|
+
denoise_local_versions = list_local_versions(denoise_ai_models_dir)
|
|
225
|
+
self.denoise_ai_options = set([])
|
|
226
|
+
self.denoise_ai_options.update([rv["version"] for rv in denoise_remote_versions])
|
|
227
|
+
self.denoise_ai_options.update([lv["version"] for lv in denoise_local_versions])
|
|
228
|
+
self.denoise_ai_options = sorted(self.denoise_ai_options, key=lambda k: version.parse(k), reverse=True)
|
|
229
|
+
|
|
230
|
+
self.denoise_ai_version = tk.StringVar(master)
|
|
231
|
+
self.denoise_ai_version.set("None") # default value
|
|
232
|
+
if graxpert.prefs.denoise_ai_version is not None:
|
|
233
|
+
self.denoise_ai_version.set(graxpert.prefs.denoise_ai_version)
|
|
234
|
+
else:
|
|
235
|
+
self.denoise_ai_options.insert(0, "None")
|
|
236
|
+
self.denoise_ai_version.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.DENOISE_AI_VERSION_CHANGED, {"denoise_ai_version": self.denoise_ai_version.get()}))
|
|
237
|
+
|
|
238
|
+
# ai settings
|
|
239
|
+
self.ai_batch_size_options = ["1", "2", "4", "8", "16", "32"]
|
|
240
|
+
self.ai_batch_size = tk.IntVar()
|
|
241
|
+
self.ai_batch_size.set(graxpert.prefs.ai_batch_size)
|
|
242
|
+
self.ai_batch_size.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.AI_BATCH_SIZE_CHANGED, {"ai_batch_size": self.ai_batch_size.get()}))
|
|
243
|
+
|
|
244
|
+
self.ai_gpu_acceleration = tk.BooleanVar()
|
|
245
|
+
self.ai_gpu_acceleration.set(graxpert.prefs.ai_gpu_acceleration)
|
|
246
|
+
self.ai_gpu_acceleration.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.AI_GPU_ACCELERATION_CHANGED, {"ai_gpu_acceleration": self.ai_gpu_acceleration.get()}))
|
|
247
|
+
|
|
248
|
+
self.create_and_place_children()
|
|
249
|
+
self.setup_layout()
|
|
250
|
+
|
|
251
|
+
def on_scaling_change(self, a, b, c):
|
|
252
|
+
eventbus.emit(AppEvents.SCALING_CHANGED, {"scaling": self.scaling.get()})
|
|
253
|
+
ctk.set_widget_scaling(self.scaling.get())
|
|
254
|
+
|
|
255
|
+
def create_and_place_children(self):
|
|
256
|
+
CTkLabel(self, text=_("Advanced Settings"), font=self.heading_font).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
257
|
+
|
|
258
|
+
# sample points
|
|
259
|
+
CTkLabel(self, text=_("Sample Points"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
260
|
+
|
|
261
|
+
ValueSlider(self, variable=self.sample_size, variable_name=_("Sample size"), min_value=5, max_value=50, precision=0).grid(**self.default_grid())
|
|
262
|
+
ValueSlider(self, variable=self.sample_color, variable_name=_("Sample color"), min_value=0, max_value=360, precision=0).grid(**self.default_grid())
|
|
263
|
+
|
|
264
|
+
# interpolation
|
|
265
|
+
CTkLabel(self, text=_("Interpolation"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
266
|
+
|
|
267
|
+
CTkLabel(self, text=_("RBF Kernel")).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
268
|
+
GraXpertOptionMenu(self, variable=self.rbf_kernel, values=self.rbf_kernels).grid(**self.default_grid())
|
|
269
|
+
|
|
270
|
+
CTkLabel(self, text=_("Spline order")).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
271
|
+
GraXpertOptionMenu(self, variable=self.spline_order, values=self.spline_orders).grid(**self.default_grid())
|
|
272
|
+
|
|
273
|
+
CTkLabel(self, text=_("Correction")).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
274
|
+
GraXpertOptionMenu(self, variable=self.corr_type, values=self.corr_types).grid(**self.default_grid())
|
|
275
|
+
|
|
276
|
+
# interface
|
|
277
|
+
CTkLabel(self, text=_("Interface"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
278
|
+
|
|
279
|
+
def lang_change(lang):
|
|
280
|
+
messagebox.showerror("", _("Please restart the program to change the language."))
|
|
281
|
+
|
|
282
|
+
CTkLabel(self, text=_("Language")).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
283
|
+
GraXpertOptionMenu(self, variable=self.lang, values=self.langs).grid(**self.default_grid())
|
|
284
|
+
|
|
285
|
+
ValueSlider(self, variable=self.scaling, variable_name=_("Scaling"), min_value=1, max_value=2, precision=1).grid(**self.default_grid())
|
|
286
|
+
|
|
287
|
+
# bge ai model
|
|
288
|
+
CTkLabel(self, text=_("Background Extraction AI-Model"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
289
|
+
GraXpertOptionMenu(self, variable=self.bge_ai_version, values=self.bge_ai_options).grid(**self.default_grid())
|
|
290
|
+
|
|
291
|
+
# object-deconvolution ai model
|
|
292
|
+
CTkLabel(self, text=_("Object Deconvolution AI-Model"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
293
|
+
GraXpertOptionMenu(self, variable=self.deconvolution_object_ai_version, values=self.deconvolution_object_ai_options).grid(**self.default_grid())
|
|
294
|
+
|
|
295
|
+
# stars-deconvolution ai model
|
|
296
|
+
CTkLabel(self, text=_("Stars Deconvolution AI-Model"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
297
|
+
GraXpertOptionMenu(self, variable=self.deconvolution_stars_ai_version, values=self.deconvolution_stars_ai_options).grid(**self.default_grid())
|
|
298
|
+
|
|
299
|
+
# denoise ai model
|
|
300
|
+
CTkLabel(self, text=_("Denoising AI-Model"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
301
|
+
GraXpertOptionMenu(self, variable=self.denoise_ai_version, values=self.denoise_ai_options).grid(**self.default_grid())
|
|
302
|
+
|
|
303
|
+
# ai settings
|
|
304
|
+
CTkLabel(self, text=_("AI inference batch size"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
305
|
+
GraXpertOptionMenu(self, variable=self.ai_batch_size, values=self.ai_batch_size_options).grid(**self.default_grid())
|
|
306
|
+
|
|
307
|
+
CTkLabel(self, text=_("AI Hardware Acceleration"), font=self.heading_font2).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
308
|
+
CTkSwitch(self, text=_("Enable Acceleration"), variable=self.ai_gpu_acceleration).grid(column=0, row=self.nrow(), pady=pady, sticky=tk.N)
|
|
309
|
+
|
|
310
|
+
def setup_layout(self):
|
|
311
|
+
self.columnconfigure(0, weight=1)
|
|
312
|
+
self.rowconfigure(0, weight=1)
|
|
313
|
+
|
|
314
|
+
def default_grid(self):
|
|
315
|
+
return {"column": 0, "row": self.nrow(), "padx": padx, "pady": pady}
|
graxpert/ui/statusbar.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
|
|
3
|
+
from customtkinter import CTkFrame, CTkLabel, StringVar
|
|
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.AstroImageRepository import ImageTypes
|
|
10
|
+
from graxpert.localization import _
|
|
11
|
+
from graxpert.ui.ui_events import UiEvents
|
|
12
|
+
from graxpert.ui.widgets import GraXpertCheckbox, GraXpertOptionMenu, ProcessingStep, ValueSlider, default_label_width, padx, pady
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StatusBar(CTkFrame):
|
|
16
|
+
def __init__(self, master, **kwargs):
|
|
17
|
+
super().__init__(master, **kwargs)
|
|
18
|
+
|
|
19
|
+
self.stretch_options = ["No Stretch", "10% Bg, 3 sigma", "15% Bg, 3 sigma", "20% Bg, 3 sigma", "30% Bg, 2 sigma"]
|
|
20
|
+
self.stretch_option_current = StringVar()
|
|
21
|
+
self.stretch_option_current.set(graxpert.prefs.stretch_option)
|
|
22
|
+
self.stretch_option_current.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.STRETCH_OPTION_CHANGED, {"stretch_option": self.stretch_option_current.get()}))
|
|
23
|
+
|
|
24
|
+
self.saturation = tk.DoubleVar()
|
|
25
|
+
self.saturation.set(graxpert.prefs.saturation)
|
|
26
|
+
self.saturation.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.CHANGE_SATURATION_REQUEST, {"saturation": self.saturation.get()}))
|
|
27
|
+
|
|
28
|
+
self.channels_linked = tk.BooleanVar()
|
|
29
|
+
self.channels_linked.set(graxpert.prefs.channels_linked_option)
|
|
30
|
+
self.channels_linked.trace_add("write", lambda a, b, c: eventbus.emit(AppEvents.CHANNELS_LINKED_CHANGED, {"channels_linked": self.channels_linked.get()}))
|
|
31
|
+
|
|
32
|
+
self.create_children()
|
|
33
|
+
self.setup_layout()
|
|
34
|
+
self.place_children()
|
|
35
|
+
self.register_events()
|
|
36
|
+
|
|
37
|
+
# widget setup
|
|
38
|
+
def create_children(self):
|
|
39
|
+
self.label_image_info = CTkLabel(self, text="image info")
|
|
40
|
+
self.label_image_pixel = CTkLabel(self, text="(x, y)")
|
|
41
|
+
|
|
42
|
+
self.stretch_option_frame = CTkFrame(self)
|
|
43
|
+
|
|
44
|
+
self.stretch_options_title = ProcessingStep(self.stretch_option_frame, number=0, indent=2, title=_(" Stretch Options"))
|
|
45
|
+
self.stretch_menu = GraXpertOptionMenu(
|
|
46
|
+
self.stretch_option_frame,
|
|
47
|
+
variable=self.stretch_option_current,
|
|
48
|
+
values=self.stretch_options,
|
|
49
|
+
)
|
|
50
|
+
tooltip.Tooltip(self.stretch_menu, text=tooltip.stretch_text)
|
|
51
|
+
self.saturation_slider = ValueSlider(
|
|
52
|
+
self.stretch_option_frame,
|
|
53
|
+
width=default_label_width,
|
|
54
|
+
variable_name=_("Saturation"),
|
|
55
|
+
variable=self.saturation,
|
|
56
|
+
min_value=0,
|
|
57
|
+
max_value=3,
|
|
58
|
+
precision=1,
|
|
59
|
+
)
|
|
60
|
+
self.channels_linked_switch = GraXpertCheckbox(self.stretch_option_frame, width=default_label_width, text=_("Channels linked"), variable=self.channels_linked)
|
|
61
|
+
|
|
62
|
+
def setup_layout(self):
|
|
63
|
+
self.columnconfigure(0, weight=1)
|
|
64
|
+
self.rowconfigure(0, weight=1)
|
|
65
|
+
|
|
66
|
+
def place_children(self):
|
|
67
|
+
self.stretch_option_frame.grid(column=0, row=0, sticky=tk.NS)
|
|
68
|
+
self.label_image_info.grid(column=0, row=0, padx=padx, sticky=tk.W)
|
|
69
|
+
self.label_image_pixel.grid(column=0, row=0, padx=padx, sticky=tk.E)
|
|
70
|
+
|
|
71
|
+
self.stretch_menu.grid(column=0, row=0, padx=padx, pady=pady, sticky=tk.E)
|
|
72
|
+
self.saturation_slider.grid(column=1, row=0, padx=padx, pady=pady, sticky=tk.EW)
|
|
73
|
+
self.channels_linked_switch.grid(column=2, row=0, padx=padx, pady=pady, sticky=tk.W)
|
|
74
|
+
|
|
75
|
+
def register_events(self):
|
|
76
|
+
eventbus.add_listener(AppEvents.LOAD_IMAGE_END, self.on_load_image_end)
|
|
77
|
+
eventbus.add_listener(UiEvents.MOUSE_MOVED, self.on_mouse_move)
|
|
78
|
+
|
|
79
|
+
# event handling
|
|
80
|
+
def on_load_image_end(self, event):
|
|
81
|
+
self.label_image_info.configure(
|
|
82
|
+
text=f"{graxpert.data_type} : {graxpert.images.get(ImageTypes.Original).img_display.width} x {graxpert.images.get(ImageTypes.Original).img_display.height} {graxpert.images.get(ImageTypes.Original).img_display.mode}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def on_mouse_move(self, event):
|
|
86
|
+
if graxpert.images.get(graxpert.display_type) is None:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
image_point = graxpert.to_image_point(event["mouse_event"].x, event["mouse_event"].y)
|
|
90
|
+
if len(image_point) != 0:
|
|
91
|
+
text = "x=" + f"{image_point[0]:.2f}" + ",y=" + f"{image_point[1]:.2f} "
|
|
92
|
+
if graxpert.images.get(graxpert.display_type).img_array.shape[2] == 3:
|
|
93
|
+
R, G, B = graxpert.images.get(graxpert.display_type).get_local_median(image_point)
|
|
94
|
+
text = text + "RGB = (" + f"{R:.4f}," + f"{G:.4f}," + f"{B:.4f})"
|
|
95
|
+
|
|
96
|
+
if graxpert.images.get(graxpert.display_type).img_array.shape[2] == 1:
|
|
97
|
+
L = graxpert.images.get(graxpert.display_type).get_local_median(image_point)
|
|
98
|
+
text = text + "L= " + f"{L:.4f}"
|
|
99
|
+
|
|
100
|
+
self.label_image_pixel.configure(text=text)
|
|
101
|
+
else:
|
|
102
|
+
self.label_image_pixel.configure(text="(--, --)")
|
graxpert/ui/styling.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
from importlib import resources
|
|
4
|
+
|
|
5
|
+
import customtkinter
|
|
6
|
+
|
|
7
|
+
from graxpert.resource_utils import resource_path, temp_resource_path
|
|
8
|
+
from graxpert.ui_scaling import get_scaling_factor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def style():
|
|
12
|
+
theme_file = "graxpert-dark-blue.json"
|
|
13
|
+
|
|
14
|
+
# Use importlib.resources to get a reliable path to the theme file
|
|
15
|
+
# This works whether the code is run from source or installed via pip/pipx
|
|
16
|
+
with resources.as_file(resources.files('graxpert.theme').joinpath(theme_file)) as theme_path:
|
|
17
|
+
# Now copy the file from the reliably found path
|
|
18
|
+
shutil.copy(theme_path, temp_resource_path(theme_file))
|
|
19
|
+
|
|
20
|
+
customtkinter.set_default_color_theme(temp_resource_path(theme_file))
|
|
21
|
+
customtkinter.set_appearance_mode("dark")
|
|
22
|
+
scaling = get_scaling_factor()
|
|
23
|
+
customtkinter.set_widget_scaling(scaling)
|
graxpert/ui/tooltip.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import tkinter.ttk as ttk
|
|
3
|
+
|
|
4
|
+
from customtkinter import CTkFrame, CTkLabel, CTkToplevel
|
|
5
|
+
|
|
6
|
+
from graxpert.application.app_events import AppEvents
|
|
7
|
+
from graxpert.application.eventbus import eventbus
|
|
8
|
+
from graxpert.localization import _
|
|
9
|
+
from graxpert.ui_scaling import get_scaling_factor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Tooltip:
|
|
13
|
+
"""
|
|
14
|
+
It creates a tooltip for a given widget as the mouse goes on it.
|
|
15
|
+
|
|
16
|
+
see:
|
|
17
|
+
|
|
18
|
+
http://stackoverflow.com/questions/3221956/
|
|
19
|
+
what-is-the-simplest-way-to-make-tooltips-
|
|
20
|
+
in-tkinter/36221216#36221216
|
|
21
|
+
|
|
22
|
+
http://www.daniweb.com/programming/software-development/
|
|
23
|
+
code/484591/a-tooltip-class-for-tkinter
|
|
24
|
+
|
|
25
|
+
- Originally written by vegaseat on 2014.09.09.
|
|
26
|
+
|
|
27
|
+
- Modified to include a delay time by Victor Zaccardo on 2016.03.25.
|
|
28
|
+
|
|
29
|
+
- Modified
|
|
30
|
+
- to correct extreme right and extreme bottom behavior,
|
|
31
|
+
- to stay inside the screen whenever the tooltip might go out on
|
|
32
|
+
the top but still the screen is higher than the tooltip,
|
|
33
|
+
- to use the more flexible mouse positioning,
|
|
34
|
+
- to add customizable background color, padding, waittime and
|
|
35
|
+
wraplength on creation
|
|
36
|
+
by Alberto Vassena on 2016.11.05.
|
|
37
|
+
|
|
38
|
+
Tested on Ubuntu 16.04/16.10, running Python 3.5.2
|
|
39
|
+
|
|
40
|
+
TODO: themes styles support
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, widget, *, pad=(5, 3, 5, 3), text="widget info", waittime=500, wraplength=250):
|
|
44
|
+
self.waittime = waittime # in miliseconds, originally 500
|
|
45
|
+
self.wraplength = wraplength * get_scaling_factor() # in pixels, originally 180
|
|
46
|
+
self.widget = widget
|
|
47
|
+
self.text = text
|
|
48
|
+
self.widget.bind("<Enter>", self.onEnter)
|
|
49
|
+
self.widget.bind("<Leave>", self.onLeave)
|
|
50
|
+
self.widget.bind("<ButtonPress>", self.onLeave)
|
|
51
|
+
self.pad = pad
|
|
52
|
+
self.id = None
|
|
53
|
+
self.tw = None
|
|
54
|
+
self.enable_tt = True
|
|
55
|
+
eventbus.add_listener(AppEvents.CALCULATE_REQUEST, lambda e: self.disable())
|
|
56
|
+
eventbus.add_listener(AppEvents.DENOISE_REQUEST, lambda e: self.disable())
|
|
57
|
+
eventbus.add_listener(AppEvents.CALCULATE_END, lambda e: self.enable())
|
|
58
|
+
eventbus.add_listener(AppEvents.DENOISE_END, lambda e: self.enable())
|
|
59
|
+
|
|
60
|
+
def onEnter(self, event=None):
|
|
61
|
+
self.schedule()
|
|
62
|
+
|
|
63
|
+
def onLeave(self, event=None):
|
|
64
|
+
self.unschedule()
|
|
65
|
+
self.hide()
|
|
66
|
+
|
|
67
|
+
def schedule(self):
|
|
68
|
+
self.unschedule()
|
|
69
|
+
self.id = self.widget.after(self.waittime, self.show)
|
|
70
|
+
|
|
71
|
+
def unschedule(self):
|
|
72
|
+
id_ = self.id
|
|
73
|
+
self.id = None
|
|
74
|
+
if id_:
|
|
75
|
+
self.widget.after_cancel(id_)
|
|
76
|
+
|
|
77
|
+
def show(self):
|
|
78
|
+
|
|
79
|
+
if not self.enable_tt:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
def tip_pos_calculator(widget, label, *, tip_delta=(10, 5), pad=(5, 3, 5, 3)):
|
|
83
|
+
w = widget
|
|
84
|
+
|
|
85
|
+
s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight()
|
|
86
|
+
|
|
87
|
+
width, height = (pad[0] + label.winfo_reqwidth() + pad[2], pad[1] + label.winfo_reqheight() + pad[3])
|
|
88
|
+
|
|
89
|
+
mouse_x, mouse_y = w.winfo_pointerxy()
|
|
90
|
+
|
|
91
|
+
x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1]
|
|
92
|
+
x2, y2 = x1 + width, y1 + height
|
|
93
|
+
|
|
94
|
+
x_delta = x2 - s_width
|
|
95
|
+
if x_delta < 0:
|
|
96
|
+
x_delta = 0
|
|
97
|
+
y_delta = y2 - s_height
|
|
98
|
+
if y_delta < 0:
|
|
99
|
+
y_delta = 0
|
|
100
|
+
|
|
101
|
+
offscreen = (x_delta, y_delta) != (0, 0)
|
|
102
|
+
|
|
103
|
+
if offscreen:
|
|
104
|
+
if x_delta:
|
|
105
|
+
x1 = mouse_x - tip_delta[0] - width
|
|
106
|
+
|
|
107
|
+
if y_delta:
|
|
108
|
+
y1 = mouse_y - tip_delta[1] - height
|
|
109
|
+
|
|
110
|
+
offscreen_again = y1 < 0 # out on the top
|
|
111
|
+
|
|
112
|
+
if offscreen_again:
|
|
113
|
+
# No further checks will be done.
|
|
114
|
+
|
|
115
|
+
# TIP:
|
|
116
|
+
# A further mod might automagically augment the
|
|
117
|
+
# wraplength when the tooltip is too high to be
|
|
118
|
+
# kept inside the screen.
|
|
119
|
+
y1 = 0
|
|
120
|
+
|
|
121
|
+
return x1, y1
|
|
122
|
+
|
|
123
|
+
pad = self.pad
|
|
124
|
+
widget = self.widget
|
|
125
|
+
|
|
126
|
+
# creates a toplevel window
|
|
127
|
+
self.tw = CTkToplevel(widget)
|
|
128
|
+
|
|
129
|
+
# Leaves only the label and removes the app window
|
|
130
|
+
self.tw.wm_overrideredirect(True)
|
|
131
|
+
|
|
132
|
+
win = CTkFrame(self.tw, border_width=0)
|
|
133
|
+
label = CTkLabel(win, text=self.text, justify=tk.LEFT, wraplength=self.wraplength)
|
|
134
|
+
|
|
135
|
+
label.grid(padx=(pad[0], pad[2]), pady=(pad[1], pad[3]), sticky=tk.NSEW)
|
|
136
|
+
win.grid()
|
|
137
|
+
|
|
138
|
+
x, y = tip_pos_calculator(widget, label)
|
|
139
|
+
|
|
140
|
+
self.tw.wm_geometry("+%d+%d" % (x, y))
|
|
141
|
+
|
|
142
|
+
def hide(self):
|
|
143
|
+
tw = self.tw
|
|
144
|
+
if tw:
|
|
145
|
+
tw.destroy()
|
|
146
|
+
self.tw = None
|
|
147
|
+
|
|
148
|
+
def enable(self):
|
|
149
|
+
self.enable_tt = True
|
|
150
|
+
|
|
151
|
+
def disable(self):
|
|
152
|
+
self.enable_tt = False
|
|
153
|
+
self.hide()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
load_text = _("Load your image you would like to correct. \n" "\n" "Supported formats: .tiff, .fits, .png, .jpg \n" "Supported bitdepths: 16 bit integer, 32 bit float")
|
|
157
|
+
|
|
158
|
+
stretch_text = _("Automatically stretch the picture to make gradients more visible. " "The saved pictures are unaffected by the stretch.")
|
|
159
|
+
|
|
160
|
+
reset_text = _("Reset all the chosen background points.")
|
|
161
|
+
|
|
162
|
+
bg_select_text = _("Creates a grid with the specified amount of points per row " "and rejects points below a threshold defined by the tolerance.")
|
|
163
|
+
|
|
164
|
+
bg_tol_text = _("The tolerance adjusts the threshold for rejection of background points " "with automatic background selection")
|
|
165
|
+
|
|
166
|
+
bg_flood_text = _("If enabled, additional grid points are automatically created based on " "1) the luminance of the sample just added and " "2) the grid tolerance slider below.")
|
|
167
|
+
|
|
168
|
+
num_points_text = _("Adjust the number of points per row for the grid created by" " automatic background selection.")
|
|
169
|
+
|
|
170
|
+
interpol_type_text = _("Choose between different interpolation methods.")
|
|
171
|
+
|
|
172
|
+
smoothing_text = _(
|
|
173
|
+
"Adjust the smoothing parameter for the interpolation method. "
|
|
174
|
+
"A too small smoothing parameter may lead to over- and undershooting "
|
|
175
|
+
"inbetween background points, while a too large smoothing parameter "
|
|
176
|
+
"may not be suited for large deviations in gradients."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
calculate_text = _("Use the specified interpolation method to calculate a background model " "and subtract it from the picture. This may take a while.")
|
|
180
|
+
|
|
181
|
+
deconvolution_type_text = _("Choose between different deconvolution methods.")
|
|
182
|
+
deconvolution_text = _("Use GraXpert's deconvolution AI model to reduce the blur in your image. This may take a while")
|
|
183
|
+
deconvolution_strength_text = _("Determines strength of deconvolution.")
|
|
184
|
+
deconvolution_psfsize_text = _("Informs the AI on how much blur to expect in the image. The right parameters is found when all artifacts disappear.")
|
|
185
|
+
|
|
186
|
+
denoise_text = _("Use GraXpert's denoising AI model to reduce the noise in your image. This may take a while")
|
|
187
|
+
denoise_strength_text = _("Determines strength of denoising.")
|
|
188
|
+
denoise_threshold_text = _("Determines the upper bound up to which pixels are denoised. Pixels above this threshold are not denoised and taken from the original image.")
|
|
189
|
+
|
|
190
|
+
saveas_text = _("Choose the bitdepth of the saved pictures and the file format. " "If you are working with a .fits image the fits header will " "be preserved.")
|
|
191
|
+
saveas_stretched_text = _("Enable to save the stretched image instead of the linear one. The color saturation is not changed.")
|
|
192
|
+
save_pic_text = _("Save the currently selected picture")
|
|
193
|
+
|
|
194
|
+
display_text = _("Switch display between \n" "\n" "Original: Your original picture \n" "Processed: Picture with subtracted background model \n" "Background: The background model")
|
graxpert/ui/ui_events.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UiEvents(Enum):
|
|
5
|
+
# main ui requests
|
|
6
|
+
RESET_ZOOM_REQUEST = auto()
|
|
7
|
+
# menu requests
|
|
8
|
+
SHOW_MENU_REQUEST = auto()
|
|
9
|
+
# crop
|
|
10
|
+
TURN_ON_CROP_MODE = auto()
|
|
11
|
+
TURN_OFF_CROP_MODE = auto()
|
|
12
|
+
APPLY_CROP_REQUEST = auto()
|
|
13
|
+
# right sidebar requests
|
|
14
|
+
HELP_FRAME_TOGGLED = auto()
|
|
15
|
+
ADVANCED_FRAME_TOGGLED = auto()
|
|
16
|
+
# mouse events
|
|
17
|
+
MOUSE_MOVED = auto()
|
|
18
|
+
# cosmetics
|
|
19
|
+
DISPLAY_START_BADGE_REQUEST = auto()
|