diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 35ae742..3409085 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -90,11 +90,13 @@ jobs: run: | pip install setuptools wheel && ` cd .\pyinstaller\bootloader && ` + (Get-Content .\wscript) -Replace "'run'", "'graxpert'" | Set-Content .\wscript && ` (Get-Content .\src\main.c) -Replace 'pyi_main\(', 'my_pyi_main(' | Set-Content .\src\main.c && ` (Get-Content .\src\pyi_main.h) -Replace 'pyi_main\(', 'my_pyi_main(' | Set-Content .\src\pyi_main.h && ` (Get-Content .\src\pyi_main.c) -Replace 'pyi_main\(', 'my_pyi_main(' | Set-Content .\src\pyi_main.c && ` python ./waf all && ` cd .. && ` + (Get-Content .\setup.py) -Replace 'run.exe', 'graxpert.exe' | Set-Content .\setup.py && ` python setup.py install && ` cd .. && ` pip install -r requirements.txt @@ -132,11 +134,13 @@ jobs: run: | pip install setuptools wheel && ` cd .\pyinstaller\bootloader && ` + (Get-Content .\wscript) -Replace "'run'", "'graxpert'" | Set-Content .\wscript && ` (Get-Content .\src\main.c) -Replace 'pyi_main\(', 'my_pyi_main(' | Set-Content .\src\main.c && ` (Get-Content .\src\pyi_main.h) -Replace 'pyi_main\(', 'my_pyi_main(' | Set-Content .\src\pyi_main.h && ` (Get-Content .\src\pyi_main.c) -Replace 'pyi_main\(', 'my_pyi_main(' | Set-Content .\src\pyi_main.c && ` python ./waf all && ` cd .. && ` + (Get-Content .\setup.py) -Replace 'run.exe', 'graxpert.exe' | Set-Content .\setup.py && ` python setup.py install && ` cd .. && ` pip install -r requirements.txt @@ -229,4 +233,4 @@ jobs: GraXpert-win64.exe GraXpert-linux.zip GraXpert-win64.zip - GraXpert-macos-x86_64.dmg + GraXpert-macos-x86_64.dmg \ No newline at end of file diff --git a/forest-dark.tcl b/forest-dark.tcl index 41222f8..3bb8db4 100644 --- a/forest-dark.tcl +++ b/forest-dark.tcl @@ -248,7 +248,7 @@ namespace eval ttk::theme::forest-dark { ] -border 4 -sticky nsew # Menubutton - ttk::style configure TMenubutton -padding {8 4 4 4} + ttk::style configure TMenubutton -padding {8 4 4 4} -anchor center ttk::style element create Menubutton.button image \ [list $I(rect-basic) \ diff --git a/locales/de_DE/LC_MESSAGES/base.mo b/locales/de_DE/LC_MESSAGES/base.mo index 251514d..ff19da2 100644 Binary files a/locales/de_DE/LC_MESSAGES/base.mo and b/locales/de_DE/LC_MESSAGES/base.mo differ diff --git a/locales/de_DE/LC_MESSAGES/base.po b/locales/de_DE/LC_MESSAGES/base.po index 0807701..d798f7c 100644 --- a/locales/de_DE/LC_MESSAGES/base.po +++ b/locales/de_DE/LC_MESSAGES/base.po @@ -14,6 +14,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: pygettext.py 1.5\n" +msgid "Crop" +msgstr "Zuschneiden" + +msgid "Crop mode on/off" +msgstr "Zuschnittsmodus an/aus" + +msgid "Apply crop" +msgstr "Zuschnitt anwenden" + +msgid "Background Extraction" +msgstr "Hintergrundentfernung" #: src/gui.py:147 msgid " Loading" @@ -21,7 +32,7 @@ msgstr " Laden" #: src/gui.py:152 msgid "Load Image" -msgstr "Lade Bild" +msgstr "Bild laden" #: src/gui.py:160 msgid " Stretch Options" @@ -33,7 +44,7 @@ msgstr " Punktauswahl" #: src/gui.py:176 msgid "Display points" -msgstr "Zeige Punkte" +msgstr "Punkte anzeigen" #: src/gui.py:185 src/gui.py:191 msgid "Points per row: {}" @@ -45,7 +56,7 @@ msgstr "Gittertoleranz: {}" #: src/gui.py:231 msgid "Create Grid" -msgstr "Erstelle Gitter" +msgstr "Gitter erstellen" #: src/gui.py:237 msgid "Reset Sample Points" @@ -61,7 +72,7 @@ msgstr "Interpolationsmethode" #: src/gui.py:287 msgid "Calculate Background" -msgstr "Berechne Hintergrund" +msgstr "Hintergrund berechnen" #: src/gui.py:294 msgid " Saving" @@ -69,11 +80,11 @@ msgstr " Speichern" #: src/gui.py:308 msgid "Save Background" -msgstr "Speichere Hintergrund" +msgstr "Hintergrund speichern" #: src/gui.py:315 msgid "Save Processed" -msgstr "Speichere Bild" +msgstr "Bild speichern" #: src/gui.py:345 msgid "An error occurred while loading your picture." @@ -204,6 +215,12 @@ msgstr "Samplefarbe: {}" msgid "Spline order" msgstr "Grad der Splines" +msgid "Language" +msgstr "Sprache" + +msgid "Correction" +msgstr "Korrektur" + #: src/loadingframe.py:18 msgid "Calculating..." msgstr "Berechne..." diff --git a/src/astroimage.py b/src/astroimage.py index ca2965c..57c8b42 100644 --- a/src/astroimage.py +++ b/src/astroimage.py @@ -65,8 +65,8 @@ def update_display(self): img_display = self.stretch() img_display = img_display*255 - if self.roworder == "TOP-DOWN": - img_display = np.flip(img_display, axis=0) + #if self.roworder == "TOP-DOWN": + # img_display = np.flip(img_display, axis=0) if(img_display.shape[2] == 1): self.img_display = Image.fromarray(img_display[:,:,0].astype(np.uint8)) @@ -77,8 +77,8 @@ def update_display(self): def update_display_from_array(self, img_display): img_display = img_display*255 - if self.roworder == "TOP-DOWN": - img_display = np.flip(img_display, axis=0) + #if self.roworder == "TOP-DOWN": + # img_display = np.flip(img_display, axis=0) if(img_display.shape[2] == 1): self.img_display = Image.fromarray(img_display[:,:,0].astype(np.uint8)) @@ -118,6 +118,13 @@ def get_stretch(self): elif(self.stretch_option.get() == "30% Bg, 2 sigma"): return (0.3, 2) + def crop(self, startx, endx, starty, endy): + self.img_array = self.img_array[starty:endy,startx:endx,:] + self.img_display = self.img_display.crop((startx, starty, endx, endy)) + self.width = self.img_array.shape[1] + self.height = self.img_array.shape[0] + return + def update_fits_header(self, original_header, background_mean): if(original_header is None): self.fits_header = fits.Header() diff --git a/src/background_extraction.py b/src/background_extraction.py index 03200ad..ddd2e68 100644 --- a/src/background_extraction.py +++ b/src/background_extraction.py @@ -23,7 +23,8 @@ from radialbasisinterpolation import RadialBasisInterpolation -def extract_background(in_imarray, background_points, interpolation_type, smoothing, downscale_factor, sample_size, RBF_kernel, spline_order): +def extract_background(in_imarray, background_points, interpolation_type, smoothing, + downscale_factor, sample_size, RBF_kernel, spline_order, corr_type): shm_imarray = shared_memory.SharedMemory(create=True, size=in_imarray.nbytes) shm_background = shared_memory.SharedMemory(create=True, size=in_imarray.nbytes) @@ -40,10 +41,15 @@ def extract_background(in_imarray, background_points, interpolation_type, smooth for c in range(num_colors): futures.insert(c, executor.submit(interpol, shm_imarray.name, shm_background.name, c, x_sub, y_sub, in_imarray.shape, interpolation_type, smoothing, downscale_factor, sample_size, RBF_kernel, spline_order, imarray.dtype)) wait(futures) - - #Subtract background from image - mean = np.mean(background) - imarray[:,:,:] = imarray[:,:,:] - background[:,:,:] + mean + + #Correction + if(corr_type == "Subtraction"): + mean = np.mean(background) + imarray[:,:,:] = imarray[:,:,:] - background[:,:,:] + mean + elif(corr_type == "Division"): + for c in range(num_colors): + mean = np.mean(imarray[:,:,c]) + imarray[:,:,c] = imarray[:,:,c] / background[:,:,c] * mean #clip image imarray[:,:,:] = imarray.clip(min=0.0,max=1.0) diff --git a/src/collapsible_frame.py b/src/collapsible_frame.py new file mode 100644 index 0000000..fa9e820 --- /dev/null +++ b/src/collapsible_frame.py @@ -0,0 +1,36 @@ +import tkinter as tk +from tkinter import ttk + +class CollapsibleFrame(tk.Frame): + + def __init__(self, parent, text="", *args, **options): + tk.Frame.__init__(self, parent, *args, **options) + + self.show = tk.IntVar() + self.show.set(0) + + self.title_frame = ttk.Frame(self) + self.title_frame.pack(fill="x", expand=1) + + ttk.Label(self.title_frame, text=text, font="Verdana 10 bold").pack(side="left", fill="x", expand=1) + + self.toggle_button = ttk.Checkbutton(self.title_frame, width=2, text='+', command=self.toggle, + variable=self.show, style='Toolbutton') + self.toggle_button.pack(side="left") + + self.sub_frame = tk.Frame(self, relief="sunken", borderwidth=0) + + def toggle(self): + if bool(self.show.get()): + self.sub_frame.pack(fill="x", expand=1) + self.toggle_button.configure(text='-') + else: + self.sub_frame.forget() + self.toggle_button.configure(text='+') + + self.update() + self.master.update() + width = self.master.winfo_width() + self.master.master.configure(width=width) + self.master.master.configure(scrollregion=self.master.master.bbox("all")) + self.master.master.yview_moveto("0.0") \ No newline at end of file diff --git a/src/gui.py b/src/gui.py index df06ebd..875a1fe 100644 --- a/src/gui.py +++ b/src/gui.py @@ -23,6 +23,7 @@ from preferences import app_state_2_prefs, load_preferences, prefs_2_app_state, save_preferences from stretch import stretch_all import tooltip +from collapsible_frame import CollapsibleFrame from loadingframe import LoadingFrame from help_panel import Help_Panel from astroimage import AstroImage @@ -81,6 +82,8 @@ def __init__(self, master=None): self.cmd.execute() self.create_widget() + self.bgextr_menu.show.set(1) + self.bgextr_menu.toggle() self.reset_transform() @@ -122,6 +125,8 @@ def create_widget(self): self.clicked_inside_pt_idx = 0 self.clicked_inside_pt_coord = None + self.crop_mode = False + self.master.bind("", self.mouse_down_left) self.master.bind("", self.mouse_release_left) # Left Mouse Button self.master.bind("", self.mouse_down_right) # Middle Mouse Button (Right Mouse Button on macs) @@ -140,6 +145,8 @@ def create_widget(self): #Side menu + heading_font = "Verdana 10 bold" + self.side_canvas = tk.Canvas(self.master, borderwidth=0, bd=0, highlightthickness=0, name="left_panel") self.side_canvas.pack(side=tk.TOP, fill=tk.Y, expand=True) @@ -148,22 +155,43 @@ def create_widget(self): scal = get_scaling_factor(self.master)*0.75 - self.side_menu = tk.Frame(self.side_canvas, borderwidth=0) - + self.side_menu = tk.Frame(self.side_canvas) + + #Crop menu + self.crop_menu = CollapsibleFrame(self.side_menu, text=_("Crop") + " ") + self.crop_menu.grid(column=0, row=0, pady=(20*scal,5*scal), padx=15*scal, sticky="news") + self.crop_menu.sub_frame.grid_columnconfigure(0, weight=1) + + for i in range(2): + self.crop_menu.sub_frame.grid_rowconfigure(i, weight=1) + + self.cropmode_button = ttk.Button(self.crop_menu.sub_frame, + text=_("Crop mode on/off"), + command=self.toggle_crop_mode, + ) + self.cropmode_button.grid(column=0, row=0, pady=(20*scal,5*scal), padx=15*scal, sticky="news") + + self.cropapply_button = ttk.Button(self.crop_menu.sub_frame, + text=_("Apply crop"), + command=self.crop_apply, + ) + self.cropapply_button.grid(column=0, row=1, pady=(5*scal,20*scal), padx=15*scal, sticky="news") - self.side_menu.grid_columnconfigure(0) + #Background extraction menu + self.bgextr_menu = CollapsibleFrame(self.side_menu, text=_("Background Extraction") + " ") + self.bgextr_menu.grid(column=0, row=1, pady=(5*scal,20*scal), padx=15*scal, sticky="news") + self.bgextr_menu.sub_frame.grid_columnconfigure(0, weight=1) for i in range(21): - self.side_menu.grid_rowconfigure(i, weight=1) + self.bgextr_menu.sub_frame.grid_rowconfigure(i, weight=1) - heading_font = "Verdana 10 bold" #---Open Image--- num_pic = ImageTk.PhotoImage(file=resource_path("img/gfx_number_1-scaled.png")) - text = tk.Label(self.side_menu, text=_(" Loading"), image=num_pic, font=heading_font, compound="left") + text = tk.Label(self.bgextr_menu.sub_frame, text=_(" Loading"), image=num_pic, font=heading_font, compound="left") text.image = num_pic text.grid(column=0, row=0, pady=(20*scal,5*scal), padx=0, sticky="w") - self.load_image_button = ttk.Button(self.side_menu, + self.load_image_button = ttk.Button(self.bgextr_menu.sub_frame, text=_("Load Image"), command=self.menu_open_clicked, ) @@ -172,7 +200,7 @@ def create_widget(self): #--Stretch Options-- num_pic = ImageTk.PhotoImage(file=resource_path("img/gfx_number_2-scaled.png")) - text = tk.Label(self.side_menu, text=_(" Stretch Options"), image=num_pic, font=heading_font, compound="left") + text = tk.Label(self.bgextr_menu.sub_frame, text=_(" Stretch Options"), image=num_pic, font=heading_font, compound="left") text.image = num_pic text.grid(column=0, row=2, pady=5*scal, padx=0, sticky="w") @@ -181,20 +209,20 @@ def create_widget(self): self.stretch_option_current.set(self.stretch_options[0]) if "stretch_option" in self.prefs: self.stretch_option_current.set(self.prefs["stretch_option"]) - self.stretch_menu = ttk.OptionMenu(self.side_menu, self.stretch_option_current, self.stretch_option_current.get(), *self.stretch_options, command=self.change_stretch) + self.stretch_menu = ttk.OptionMenu(self.bgextr_menu.sub_frame, self.stretch_option_current, self.stretch_option_current.get(), *self.stretch_options, command=self.change_stretch) self.stretch_menu.grid(column=0, row=3, pady=(5*scal,30*scal), padx=15*scal, sticky="news") tt_stretch= tooltip.Tooltip(self.stretch_menu, text=tooltip.stretch_text) #---Sample Selection--- num_pic = ImageTk.PhotoImage(file=resource_path("img/gfx_number_3-scaled.png")) - text = tk.Label(self.side_menu, text=_(" Sample Selection"), image=num_pic, font=heading_font, compound="left") + text = tk.Label(self.bgextr_menu.sub_frame, text=_(" Sample Selection"), image=num_pic, font=heading_font, compound="left") text.image = num_pic text.grid(column=0, row=4, pady=5*scal, padx=0, sticky="w") self.display_pts = tk.BooleanVar() self.display_pts.set(True) - self.display_pts_switch = ttk.Checkbutton(self.side_menu, text=" "+_("Display points"), compound=tk.LEFT, var=self.display_pts, command=self.redraw_points) + self.display_pts_switch = ttk.Checkbutton(self.bgextr_menu.sub_frame, text=" "+_("Display points"), compound=tk.LEFT, var=self.display_pts, command=self.redraw_points) self.display_pts_switch.grid(column=0, row=5, pady=(5*scal,5*scal), padx=15*scal, sticky="ews") self.bg_pts = tk.IntVar() @@ -202,7 +230,7 @@ def create_widget(self): if "bg_pts_option" in self.prefs: self.bg_pts.set(self.prefs["bg_pts_option"]) - self.bg_selection_text = tk.Message(self.side_menu, text=_("Points per row: {}").format(self.bg_pts.get())) + self.bg_selection_text = tk.Message(self.bgextr_menu.sub_frame, text=_("Points per row: {}").format(self.bg_pts.get())) self.bg_selection_text.config(width=500 * scal) self.bg_selection_text.grid(column=0, row=6, pady=(5*scal,5*scal), padx=15*scal, sticky="ews") @@ -211,7 +239,7 @@ def on_bg_pts_slider(bgs_points): self.bg_selection_text.configure(text=_("Points per row: {}").format(self.bg_pts.get())) self.bg_pts_slider = ttk.Scale( - self.side_menu, + self.bgextr_menu.sub_frame, orient=tk.HORIZONTAL, from_=4, to=25, @@ -228,7 +256,7 @@ def on_bg_pts_slider(bgs_points): if "bg_tol_option" in self.prefs: self.bg_tol.set(self.prefs["bg_tol_option"]) - self.bg_selection_tol = tk.Message(self.side_menu, text=_("Grid Tolerance: {}").format(self.bg_tol.get())) + self.bg_selection_tol = tk.Message(self.bgextr_menu.sub_frame, text=_("Grid Tolerance: {}").format(self.bg_tol.get())) self.bg_selection_tol.config(width=500) self.bg_selection_tol.grid(column=0, row=8, pady=(5*scal,5*scal), padx=15*scal, sticky="ews") @@ -237,7 +265,7 @@ def on_bg_tol_slider(bg_tol): self.bg_selection_tol.configure(text=_("Grid Tolerance: {}").format(self.bg_tol.get())) self.bg_tol_slider = ttk.Scale( - self.side_menu, + self.bgextr_menu.sub_frame, orient=tk.HORIZONTAL, from_=-2, to=10, @@ -248,13 +276,13 @@ def on_bg_tol_slider(bg_tol): self.bg_tol_slider.grid(column=0, row=9, pady=(0,10*scal), padx=15*scal, sticky="ew") tt_tol_points= tooltip.Tooltip(self.bg_tol_slider, text=tooltip.bg_tol_text) - self.bg_selection_button = ttk.Button(self.side_menu, + self.bg_selection_button = ttk.Button(self.bgextr_menu.sub_frame, text=_("Create Grid"), command=self.select_background) self.bg_selection_button.grid(column=0, row=10, pady=5*scal, padx=15*scal, sticky="news") tt_bg_select = tooltip.Tooltip(self.bg_selection_button, text= tooltip.bg_select_text) - self.reset_button = ttk.Button(self.side_menu, + self.reset_button = ttk.Button(self.bgextr_menu.sub_frame, text=_("Reset Sample Points"), command=self.reset_backgroundpts) self.reset_button.grid(column=0, row=11, pady=(5*scal,30*scal), padx=15*scal, sticky="news") @@ -262,11 +290,11 @@ def on_bg_tol_slider(bg_tol): #---Calculation--- num_pic = ImageTk.PhotoImage(file=resource_path("img/gfx_number_4-scaled.png")) - text = tk.Label(self.side_menu, text=_(" Calculation"), image=num_pic, font=heading_font, compound="left") + text = tk.Label(self.bgextr_menu.sub_frame, text=_(" Calculation"), image=num_pic, font=heading_font, compound="left") text.image = num_pic text.grid(column=0, row=12, pady=5*scal, padx=0, sticky="w") - self.intp_type_text = tk.Message(self.side_menu, text=_("Interpolation Method:")) + self.intp_type_text = tk.Message(self.bgextr_menu.sub_frame, text=_("Interpolation Method:")) self.intp_type_text.config(width=500) self.intp_type_text.grid(column=0, row=13, pady=(5*scal,5*scal), padx=15*scal, sticky="ews") @@ -275,7 +303,7 @@ def on_bg_tol_slider(bg_tol): self.interpol_type.set(self.interpol_options[0]) if "interpol_type_option" in self.prefs: self.interpol_type.set(self.prefs["interpol_type_option"]) - self.interpol_menu = ttk.OptionMenu(self.side_menu, self.interpol_type, self.interpol_type.get(), *self.interpol_options) + self.interpol_menu = ttk.OptionMenu(self.bgextr_menu.sub_frame, self.interpol_type, self.interpol_type.get(), *self.interpol_options) self.interpol_menu.grid(column=0, row=14, pady=(0,5*scal), padx=15*scal, sticky="news") tt_interpol_type= tooltip.Tooltip(self.interpol_menu, text=tooltip.interpol_type_text) @@ -284,7 +312,7 @@ def on_bg_tol_slider(bg_tol): if "smoothing_option" in self.prefs: self.smoothing.set(self.prefs["smoothing_option"]) - self.smooth_text = tk.Message(self.side_menu, text="Smoothing: {}".format(self.smoothing.get())) + self.smooth_text = tk.Message(self.bgextr_menu.sub_frame, text="Smoothing: {}".format(self.smoothing.get())) self.smooth_text.config(width=500) self.smooth_text.grid(column=0, row=15, pady=(5*scal,5*scal), padx=15*scal, sticky="ews") @@ -293,7 +321,7 @@ def on_smoothing_slider(smoothing): self.smooth_text.configure(text="Smoothing: {}".format(self.smoothing.get())) self.smoothing_slider = ttk.Scale( - self.side_menu, + self.bgextr_menu.sub_frame, orient=tk.HORIZONTAL, from_=0, to=1, @@ -304,7 +332,7 @@ def on_smoothing_slider(smoothing): self.smoothing_slider.grid(column=0, row=16, pady=(0,10*scal), padx=15*scal, sticky="ew") tt_smoothing= tooltip.Tooltip(self.smoothing_slider, text=tooltip.smoothing_text) - self.calculate_button = ttk.Button(self.side_menu, + self.calculate_button = ttk.Button(self.bgextr_menu.sub_frame, text=_("Calculate Background"), command=self.calculate) self.calculate_button.grid(column=0, row=17, pady=(5*scal,30*scal), padx=15*scal, sticky="news") @@ -312,7 +340,7 @@ def on_smoothing_slider(smoothing): #---Saving--- num_pic = ImageTk.PhotoImage(file=resource_path("img/gfx_number_5-scaled.png")) - self.saveas_text = tk.Label(self.side_menu, text=_(" Saving"), image=num_pic, font=heading_font, compound="left") + self.saveas_text = tk.Label(self.bgextr_menu.sub_frame, text=_(" Saving"), image=num_pic, font=heading_font, compound="left") self.saveas_text.image = num_pic self.saveas_text.grid(column=0, row=18, pady=5*scal, padx=0, sticky="w") @@ -321,24 +349,24 @@ def on_smoothing_slider(smoothing): self.saveas_type.set(self.saveas_options[0]) if "saveas_option" in self.prefs: self.saveas_type.set(self.prefs["saveas_option"]) - self.saveas_menu = ttk.OptionMenu(self.side_menu, self.saveas_type, self.saveas_type.get(), *self.saveas_options) + self.saveas_menu = ttk.OptionMenu(self.bgextr_menu.sub_frame, self.saveas_type, self.saveas_type.get(), *self.saveas_options) self.saveas_menu.grid(column=0, row=19, pady=(5*scal,20*scal), padx=15*scal, sticky="news") tt_interpol_type= tooltip.Tooltip(self.saveas_menu, text=tooltip.saveas_text) - self.save_background_button = ttk.Button(self.side_menu, + self.save_background_button = ttk.Button(self.bgextr_menu.sub_frame, text=_("Save Background"), command=self.save_background_image) self.save_background_button.grid(column=0, row=20, pady=5*scal, padx=15*scal, sticky="news") tt_save_bg = tooltip.Tooltip(self.save_background_button, text=tooltip.save_bg_text) - self.save_button = ttk.Button(self.side_menu, + self.save_button = ttk.Button(self.bgextr_menu.sub_frame, text=_("Save Processed"), command=self.save_image) self.save_button.grid(column=0, row=21, pady=(5*scal,10*scal), padx=15*scal, sticky="news") tt_save_pic= tooltip.Tooltip(self.save_button, text=tooltip.save_pic_text) - + self.side_canvas.create_window((0,0), window=self.side_menu) self.side_canvas.configure(yscrollcommand=self.scrollbar.set) self.side_canvas.bind('', lambda e: self.side_canvas.configure(scrollregion=self.side_canvas.bbox("all"))) @@ -346,6 +374,7 @@ def on_smoothing_slider(smoothing): width = self.side_menu.winfo_width() self.side_canvas.configure(width=width) self.side_canvas.yview_moveto("0.0") + def menu_open_clicked(self, event=None): @@ -402,6 +431,42 @@ def menu_open_clicked(self, event=None): self.loading_frame.end() return + def toggle_crop_mode(self): + + if self.images["Original"] is None: + messagebox.showerror("Error", _("Please load your picture first.")) + return + + self.startx = 0 + self.starty = 0 + self.endx = self.images["Original"].width + self.endy = self.images["Original"].height + + if(self.crop_mode): + self.crop_mode = False + else: + self.crop_mode = True + + self.redraw_points() + + def crop_apply(self): + + if (not self.crop_mode): + return + + for astroimg in self.images.values(): + if(astroimg is not None): + astroimg.crop(self.startx, self.endx, self.starty, self.endy) + + self.reset_backgroundpts() + self.crop_mode = False + self.zoom_fit(self.images[self.display_type.get()].width, self.images[self.display_type.get()].height) + self.redraw_image() + self.redraw_points() + return + + + def select_background(self,event=None): if self.images["Original"] is None: @@ -540,7 +605,8 @@ def calculate(self): imarray,np.array(background_points), self.interpol_type.get(),self.smoothing.get(), downscale_factor, self.sample_size.get(), - self.RBF_kernel.get(),self.spline_order.get() + self.RBF_kernel.get(),self.spline_order.get(), + self.corr_type.get() )) self.images["Processed"] = AstroImage(self.stretch_option_current) @@ -603,6 +669,13 @@ def mouse_down_left(self,event): self.clicked_inside_pt = True self.clicked_inside_pt_idx = min_idx self.clicked_inside_pt_coord = self.cmd.app_state["background_points"][min_idx] + + if(self.crop_mode): + #Check if inside circles to move crop corners + corner1 = self.to_canvas_point(self.startx, self.starty) + corner2 = self.to_canvas_point(self.endx, self.endy) + if((event.x - corner1[0])**2 + (event.y - corner1[1])**2 < 15**2 or (event.x - corner2[0])**2 + (event.y - corner2[1])**2 < 15**2): + self.clicked_inside_pt = True self.__old_event = event @@ -613,7 +686,7 @@ def mouse_release_left(self,event): return - if self.clicked_inside_pt: + if self.clicked_inside_pt and not self.crop_mode: new_point = self.to_image_point(event.x,event.y) self.cmd.app_state["background_points"][self.clicked_inside_pt_idx] = self.clicked_inside_pt_coord self.cmd = Command(MOVE_POINT_HANDLER, prev=self.cmd, new_point=new_point, idx=self.clicked_inside_pt_idx) @@ -642,13 +715,29 @@ def mouse_move_left(self, event): if(self.left_drag_timer == -1): self.left_drag_timer = event.time - if self.clicked_inside_pt and self.display_pts.get(): + if(self.clicked_inside_pt and self.display_pts.get() and not self.crop_mode): new_point = self.to_image_point(event.x, event.y) if len(new_point) != 0: self.cmd.app_state["background_points"][self.clicked_inside_pt_idx] = new_point self.redraw_points() + elif(self.clicked_inside_pt and self.crop_mode): + new_point = self.to_image_point_pinned(event.x, event.y) + corner1_canvas = self.to_canvas_point(self.startx, self.starty) + corner2_canvas = self.to_canvas_point(self.endx, self.endy) + + dist1 = (event.x - corner1_canvas[0])**2 + (event.y - corner1_canvas[1])**2 + dist2 = (event.x - corner2_canvas[0])**2 + (event.y - corner2_canvas[1])**2 + if(dist1 < dist2): + self.startx = int(new_point[0]) + self.starty = int(new_point[1]) + else: + self.endx = int(new_point[0]) + self.endy = int(new_point[1]) + + self.redraw_points() + else: if(event.time - self.left_drag_timer >= 100): self.translate(event.x - self.__old_event.x, event.y - self.__old_event.y) @@ -860,6 +949,28 @@ def to_image_point(self, x, y): return image_point + def to_image_point_pinned(self, x, y): + + if self.images[self.display_type.get()] is None: + return [] + + mat_inv = np.linalg.inv(self.mat_affine) + image_point = np.dot(mat_inv, (x, y, 1.)) + + width = self.images[self.display_type.get()].width + height = self.images[self.display_type.get()].height + + if image_point[0] < 0: + image_point[0] = 0 + if image_point[1] < 0: + image_point[1] = 0 + if image_point[0] > width: + image_point[0] = width + if image_point[1] > height: + image_point[1] = height + + return image_point + def to_canvas_point(self, x, y): return np.dot(self.mat_affine,(x,y,1.)) @@ -912,16 +1023,23 @@ def redraw_points(self): color = (int(color[0]*255), int(color[1]*255), int(color[2]*255)) color = '#%02x%02x%02x' % color - self.canvas.delete("rectangle") + self.canvas.delete("sample") + self.canvas.delete("crop") rectsize = self.sample_size.get() background_points = self.cmd.app_state["background_points"] - if self.display_pts.get(): + if self.display_pts.get() and not self.crop_mode: for point in background_points: corner1 = self.to_canvas_point(point[0]-rectsize,point[1]-rectsize) corner2 = self.to_canvas_point(point[0]+rectsize,point[1]+rectsize) - self.canvas.create_rectangle(corner1[0],corner1[1], corner2[0],corner2[1],outline=color, width=2, tags="rectangle") - + self.canvas.create_rectangle(corner1[0],corner1[1], corner2[0],corner2[1],outline=color, width=2, tags="sample") + + if self.crop_mode: + corner1 = self.to_canvas_point(self.startx, self.starty) + corner2 = self.to_canvas_point(self.endx, self.endy) + self.canvas.create_rectangle(corner1[0],corner1[1], corner2[0],corner2[1], outline=color, width=2, tags="crop") + self.canvas.create_oval(corner1[0]-15,corner1[1]-15, corner1[0]+15,corner1[1]+15, outline=color, width=2, tags="crop") + self.canvas.create_oval(corner2[0]-15,corner2[1]-15, corner2[0]+15,corner2[1]+15, outline=color, width=2, tags="crop") return def redraw_image(self): @@ -966,6 +1084,7 @@ def on_closing(self): self.prefs["RBF_kernel"] = self.RBF_kernel.get() self.prefs["spline_order"] = self.spline_order.get() self.prefs["lang"] = self.lang.get() + self.prefs["corr_type"] = self.corr_type.get() prefs_filename = os.path.join(user_config_dir(), ".graxpert", "preferences.json") save_preferences(prefs_filename, self.prefs) try: @@ -1010,8 +1129,9 @@ def scale_img(path, scaling, shape): root.tk.call("source", resource_path("forest-dark.tcl")) style = ttk.Style(root) - style.configure("Scale.slider", sliderlength=100, sliderthickness=100) style.theme_use("forest-dark") + style.configure("TButton", padding=(8*scaling, 12*scaling, 8*scaling, 12*scaling)) + style.configure("TMenubutton", padding=(8*scaling, 4*scaling, 4*scaling, 4*scaling)) root.tk.call("wm", "iconphoto", root._w, tk.PhotoImage(file=resource_path("img/Icon.png"))) root.tk.call('tk', 'scaling', scaling) root.option_add("*TkFDialog*foreground", "black") diff --git a/src/help_panel.py b/src/help_panel.py index 7bae1e7..f66fe7d 100644 --- a/src/help_panel.py +++ b/src/help_panel.py @@ -322,6 +322,20 @@ def lang_change(lang): self.lang_menu.grid(column=0, row=12, pady=(5*scaling,5*scaling), padx=(40,30), sticky="ews") + text = tk.Message(self.advanced_panel_window, text=_("Correction"), width=240 * scaling, font=heading_font2, anchor="center") + text.grid(column=0, row=13, padx=(10*scaling,10*scaling), pady=(20*scaling,10*scaling), sticky="ew") + + + self.app.corr_types = ["Subtraction", "Division"] + self.app.corr_type = tk.StringVar() + self.app.corr_type.set(self.app.corr_types[0]) + if "corr_type" in self.app.prefs: + self.app.corr_type.set(self.app.prefs["corr_type"]) + + self.corr_menu = ttk.OptionMenu(self.advanced_panel_window, self.app.corr_type, self.app.corr_type.get(), *self.app.corr_types) + self.corr_menu.grid(column=0, row=14, pady=(5*scaling,5*scaling), padx=(40,30), sticky="ews") + + self.advanced_canvas.create_window((0,0), window=self.advanced_panel_window) self.advanced_canvas.configure(yscrollcommand=self.advanced_scrollbar.set) self.advanced_canvas.bind('', lambda e: self.advanced_canvas.configure(scrollregion=self.advanced_canvas.bbox("all"))) diff --git a/src/preferences.py b/src/preferences.py index b82709a..6028825 100644 --- a/src/preferences.py +++ b/src/preferences.py @@ -24,6 +24,7 @@ class Prefs(TypedDict): sample_color: int RBF_kernel: AnyStr lang: AnyStr + corr_type: AnyStr DEFAULT_PREFS: Prefs = { "working_dir": os.getcwd(), @@ -40,7 +41,8 @@ class Prefs(TypedDict): "sample_color": 55, "RBF_kernel": "thin_plate", "spline_order": 3, - "lang": None + "lang": None, + "corr_type": "Subtraction" } def app_state_2_prefs(prefs: Prefs, app_state: AppState) -> Prefs: @@ -84,6 +86,8 @@ def merge_json(prefs: Prefs, json) -> Prefs: prefs["spline_order"] = json["spline_order"] if "lang" in json: prefs["lang"] = json["lang"] + if "corr_type" in json: + prefs["corr_type"] = json["corr_type"] return prefs def load_preferences(prefs_filename) -> Prefs: diff --git a/src/ui_scaling.py b/src/ui_scaling.py index 71d2302..9009c47 100644 --- a/src/ui_scaling.py +++ b/src/ui_scaling.py @@ -1,4 +1,5 @@ from screeninfo import get_monitors +from platform import system scaling_factor = None @@ -23,9 +24,10 @@ def get_scaling_factor(master): except: # ... if that fails try the first one in the list monitor = monitors[0] - - dpi = monitor.width / (monitor.width_mm / 24.0) - scaling_factor = dpi / 72.0 + + dpi = monitor.width / (monitor.width_mm / 25.4) + scaling_factor = dpi / 96.0 + except BaseException as e: print("WARNING: could not calculate monitor dpi:", e) scaling_factor = 1.0