import tkinter as tk import PIL.Image, PIL.ImageTk import numpy as np from tkinter import ttk from tkinter.font import Font from tkinter import filedialog, messagebox import subprocess import os import shutil class User_Interface(): def __init__(self, camera, preview_processing, gpio_instance, still_image_processing): #Instanzvariablen laden self.still_image_processing = still_image_processing self.preview_processing = preview_processing self.gpio_instance = gpio_instance self.camera = camera self.root = tk.Tk() #Verzögerung des Vorschaubildes self.video_feed_delay = 5 #Seitenverhältnis der Kamera und des Widgets auslesen, zur Platzierung des Vorschaubildes in diesem self.camera_aspect_ratio = self.camera.get_aspect_ratio() self.video_widget_aspect_ratio = None self.new_width = 800 self.new_height = 480 #Flags für das derzeit aktive Menü self.current_menu_right = None self.current_menu_down = None self.current_menu_down_right = None self.submenu = False #Ein leerer Pixel, wird zur quadratischen Form der Buttons benötigt self.pixel = tk.PhotoImage() self.keyboard_process = None #Parameter des Lineals, mit Aktion verknüpft, sobald der Wert im Eingabefeld geändert wird self.ruler_vertical_start = tk.StringVar() self.ruler_vertical_end = tk.StringVar() self.ruler_horizontal_start = tk.StringVar() self.ruler_horizontal_end = tk.StringVar() self.ruler_vertical_start.set(0) self.ruler_vertical_end.set(50) self.ruler_horizontal_start.set(0) self.ruler_horizontal_end.set(100) self.ruler_vertical_start.trace_add("write", lambda var, indx, mode: self.entry_fields_changed("ruler_vertical_start")) self.ruler_vertical_end.trace_add("write", lambda var, indx, mode: self.entry_fields_changed("ruler_vertical_end")) self.ruler_horizontal_start.trace_add("write", lambda var, indx, mode: self.entry_fields_changed("ruler_horizontal_start")) self.ruler_horizontal_end.trace_add("write", lambda var, indx, mode: self.entry_fields_changed("ruler_horizontal_end")) #Blitz / permanente Beleuchtung an / aus self.permanent_lighting = False self.activate_flash = False #Vollbild an / aus self.fullscreen_active = True # Definiere Schriftarten self.font_bold = Font(family="Helvetica", size=12, weight="bold") self.font_normal = Font(family="Helvetica", size=12) # Checkbox Größe anpassen self.style = ttk.Style() self.style.configure("LargeCheckbutton.TCheckbutton", font=self.font_normal, size=25) #Variablen für den Dateinamen, ebenfalls mit Aktion verknüpft self.storage_path_var = tk.StringVar(name="storage_path_var") self.storage_path_var.set("/home/pi") self.destination_path_var = tk.StringVar(name="destination_path_var") self.filename_var = tk.StringVar(name="filename_var") self.date_var = tk.BooleanVar(name="date_var") self.time_var = tk.BooleanVar(name="time_var") self.date_var.set(True) self.time_var.set(True) self.storage_path_var.trace_add("write", lambda *args: self.entry_fields_changed("Speicherpfad", *args)) self.destination_path_var.trace_add("write", lambda *args: self.entry_fields_changed("Zielpfad", *args)) self.filename_var.trace_add("write", lambda *args: self.entry_fields_changed("Dateiname", *args)) self.date_var.trace_add("write", lambda *args: self.entry_fields_changed("Datum", *args)) self.time_var.trace_add("write", lambda *args: self.entry_fields_changed("Uhrzeit", *args)) #GUI soll im Vollbild starten def window_settings(self): self.root.attributes('-fullscreen', True) self.root.title('Titel') def start_interface(self): self.root.mainloop() #Erstellen der Widgets aller Menüs def build_widgets(self): self.build_video_widget() self.build_hide_menu_button() self.main_menu_frame() self.crop_menu_frame() self.settings_menu_frame() self.file_menu_frame() self.main_menu_widgets() self.crop_menu_widgets() self.settings_menu_widgets() self.file_menu_widgets() self.root.grid_rowconfigure(0, weight=1) self.root.grid_columnconfigure(0, weight=1) self.root.grid_columnconfigure(1, minsize=0) def build_video_widget(self): self.video_widget = tk.Canvas(self.root, width=800, height=480, highlightthickness=0) self.video_widget.grid(row=0, column=0, sticky="nsew") #Button "<", ">" zum Anzeigen / Verstecken des Menüs def build_hide_menu_button(self): self.hide_menu_button_container = tk.Frame(self.video_widget, background="white", bd=0, highlightthickness=0) self.hide_menu_button_container.place(relx=1.0, rely=0.0, anchor="ne") self.hide_menu_button_settings = { "master": self.hide_menu_button_container, "image": self.pixel, "compound": "c", "height": 30, "width": 30, "command": self.toggle_menu } self.hide_menu_button = tk.Button(**self.hide_menu_button_settings, text="<") self.hide_menu_button.pack(side="right", padx=0, pady=0) #Frames des Hauptmenüs, alle Menüs teilen sich in 3 Frames auf: Rechts vom Vorschaubild, Unter dem Vorschaubild sowie ein Frame unten rechts zwischen dem rechten und dem unteren Frame (2x2-Raster) def main_menu_frame(self): self.main_menu_frame_right = tk.Frame(self.root) self.main_menu_frame_right.grid(row=0, column=1, sticky="nsew") self.main_menu_frame_down = tk.Frame(self.root) self.main_menu_frame_down.grid(row=1, column=0, sticky="nsew") self.main_menu_frame_down_right = tk.Frame(self.root) self.main_menu_frame_down_right.grid(row=1, column=1, sticky="nsew") self.main_menu_frame_right.grid_remove() self.main_menu_frame_down.grid_remove() self.main_menu_frame_down_right.grid_remove() #Frames des Zuschnitt-Menüs def crop_menu_frame(self): self.crop_menu_frame_right = tk.Frame(self.root) self.crop_menu_frame_right.grid(row=0, column=1, sticky="nsew") self.crop_menu_frame_down = tk.Frame(self.root) self.crop_menu_frame_down.grid(row=1, column=0, sticky="nsew") self.crop_menu_frame_down_right = tk.Frame(self.root) self.crop_menu_frame_down_right.grid(row=1, column=1, sticky="nsew") self.crop_menu_frame_right.grid_remove() self.crop_menu_frame_down.grid_remove() self.crop_menu_frame_down_right.grid_remove() #Frames des Bildeinstellungsmenüs def settings_menu_frame(self): self.settings_menu_frame_right = tk.Frame(self.root) self.settings_menu_frame_right.grid(row=0, column=1, sticky="nsew") self.settings_menu_frame_down = tk.Frame(self.root) self.settings_menu_frame_down.grid(row=1, column=0, sticky="nsew") self.settings_menu_frame_down_right = tk.Frame(self.root) self.settings_menu_frame_down_right.grid(row=1, column=1, sticky="nsew") self.settings_menu_frame_right.grid_remove() self.settings_menu_frame_down.grid_remove() self.settings_menu_frame_down_right.grid_remove() #Frames des Dateimenüs def file_menu_frame(self): self.file_menu_frame_right = tk.Frame(self.root) self.file_menu_frame_right.grid(row=0, column=1, sticky="nsew") self.file_menu_frame_down = tk.Frame(self.root) self.file_menu_frame_down.grid(row=1, column=0, sticky="nsew") self.file_menu_frame_down_right = tk.Frame(self.root) self.file_menu_frame_down_right.grid(row=1, column=1, sticky="nsew") self.file_menu_frame_right.grid_remove() self.file_menu_frame_down.grid_remove() self.file_menu_frame_down_right.grid_remove() #Widgets für das Hauptmenü erstellen def main_menu_widgets(self): self.settings_menu_button = tk.Button(self.main_menu_frame_right, text="Bild-\neinstellungen", image=self.pixel, compound="c", height=80, width=80, command=self.show_settings_menu) self.settings_menu_button.pack(pady=10) self.crop_menu_button = tk.Button(self.main_menu_frame_right, text="Zuschnitt", image=self.pixel, compound="c", height=80, width=80, command=self.show_crop_menu) self.crop_menu_button.pack(pady=30) self.file_menu_button = tk.Button(self.main_menu_frame_right, text="Datei-\neinstellungen", image=self.pixel, compound="c", height=80, width=80, command=self.show_file_menu) self.file_menu_button.pack(pady=30) buttons_frame_down = tk.Frame(self.main_menu_frame_down) buttons_frame_down.pack(pady=10, fill='x', anchor='center') #Einen Frame erstellen, um die beiden unteren Buttons zentriert auszurichten buttons_frame_down.grid_columnconfigure(0, weight=1) buttons_frame_down.grid_columnconfigure(3, weight=1) # Vollbild beenden Button zentriert im Grid platzieren self.fullscreen_button = tk.Button(buttons_frame_down, text="Vollbild beenden", command=self.toggle_fullscreen, width=20) self.fullscreen_button.grid(row=0, column=1, padx=20) # Zoom Button zentriert im Grid und neben dem Vollbild beenden Button platzieren self.zoom_button_state = tk.BooleanVar(value=False) self.zoom_button = tk.Button(buttons_frame_down, text="Zoom", command=self.toggle_zoom, width=20) self.zoom_button.grid(row=0, column=2, padx=20) #Widgets des Zuschnittmenüs / Slider für ROI def crop_menu_widgets(self): back_button = tk.Button(self.crop_menu_frame_down_right, text="Zurück", image=self.pixel, compound="c", height=40, width=40, command=self.show_main_menu) back_button.pack(pady=10) self.crop_width_value = tk.IntVar() self.crop_width_slider = tk.Scale(self.crop_menu_frame_down, from_=800, to=0, resolution= 2, orient='horizontal', variable=self.crop_width_value, command=self.update_value, width=30) self.crop_width_slider.pack(fill='x', padx=50) self.crop_width_value.set(800) self.crop_height_value = tk.IntVar() self.crop_height_slider = tk.Scale(self.crop_menu_frame_right, from_=480, to=0, resolution= 2, orient='vertical', variable=self.crop_height_value, command=self.update_value, width=30) self.crop_height_slider.pack(fill='y', expand=True, pady=25, padx=10) self.crop_height_value.set(480) def settings_menu_widgets(self): self.ext_lighting_state = tk.BooleanVar(value=False) self.ext_lighting_button = tk.Button(self.settings_menu_frame_right, text="Beleuchtung \nPermanent", image=self.pixel, compound="c", height=80, width=80, command=self.toggle_ext_lighting) self.ext_lighting_button.pack(pady=10) self.ext_flash_state = tk.BooleanVar(value=False) self.ext_flash_button = tk.Button(self.settings_menu_frame_right, text="Blitz", image=self.pixel, compound="c", height=80, width=80, command=self.toggle_flash) self.ext_flash_button.pack(pady=10) back_button = tk.Button(self.settings_menu_frame_down_right, text="Zurück", image=self.pixel, compound="c", height=40, width=40, command=self.show_main_menu) back_button.pack(pady=10) self.show_preview_grid_state = tk.BooleanVar(value=False) self.show_preview_grid_button = tk.Button(self.settings_menu_frame_right, text="Gitter", image=self.pixel, compound="c", height=80, width=80, command=self.toggle_preview_grid) self.show_preview_grid_button.pack(pady=30) buttons_frame = tk.Frame(self.settings_menu_frame_down) buttons_frame.pack(pady=15, fill='x', expand=True) # Button für das Lineal self.show_preview_ruler_state = tk.BooleanVar(value=False) self.show_preview_ruler_button = tk.Button(buttons_frame, text="Lineal", image=self.pixel, compound="c", height=30, width=80, command=self.toggle_preview_ruler) self.show_preview_ruler_button.pack(side='left', padx=20) # Container für Lineal horizontal mit zentrierten Eingabefeldern horizontal_ruler_frame = tk.Frame(buttons_frame, height=60) horizontal_ruler_frame.pack(side='left', padx=10) tk.Label(horizontal_ruler_frame, text="Lineal horizontal", height=1).pack() # Frame für die Eingabefelder, um sie zentriert unter dem Text zu positionieren horizontal_ruler_entries_frame = tk.Frame(horizontal_ruler_frame) horizontal_ruler_entries_frame.pack() tk.Entry(horizontal_ruler_entries_frame, width=5, textvariable=self.ruler_horizontal_start).pack(side='left', padx=(0, 5)) # Ein wenig Platz zwischen den Feldern tk.Entry(horizontal_ruler_entries_frame, width=5, textvariable=self.ruler_horizontal_end).pack(side='left') # Container für Lineal vertikal mit zentrierten Eingabefeldern vertical_ruler_frame = tk.Frame(buttons_frame, height=60) vertical_ruler_frame.pack(side='left', padx=10) tk.Label(vertical_ruler_frame, text="Lineal vertikal", height=1).pack() # Frame für die Eingabefelder, um sie zentriert unter dem Text zu positionieren vertical_ruler_entries_frame = tk.Frame(vertical_ruler_frame) vertical_ruler_entries_frame.pack() tk.Entry(vertical_ruler_entries_frame, width=5, textvariable=self.ruler_vertical_start).pack(side='left', padx=(0, 5)) # Ein wenig Platz zwischen den Feldern tk.Entry(vertical_ruler_entries_frame, width=5, textvariable=self.ruler_vertical_end).pack(side='left') # Button für die Uhrzeit self.show_preview_clock_state = tk.BooleanVar(value=False) self.show_preview_clock_button = tk.Button(buttons_frame, text="Uhrzeit", image=self.pixel, compound="c", height=30, width=80, command=self.toggle_preview_clock) self.show_preview_clock_button.pack(side='left', padx=20) # Button für die Tastatur keyboard_button = tk.Button(buttons_frame, text="Tastatur", image=self.pixel, compound="c", height=30, width=80, command=self.toggle_keyboard) keyboard_button.pack(side='left', padx=20) def file_menu_widgets(self): back_button_frame = tk.Frame(self.file_menu_frame_down_right) back_button_frame.pack(side='right', padx=10, pady=10) back_button = tk.Button(back_button_frame, text="Zurück", image=self.pixel, compound="c", height=40, width=40, command=self.show_main_menu) back_button.pack() text_font = Font(family="Helvetica", size=14) self.style.configure("LargeCheckbutton.TCheckbutton", font=('Helvetica', 14)) # "Kopiermenü"-Label label_copy_menu = tk.Label(self.file_menu_frame_right, text="Kopieren - Zielordner", font=self.font_bold) label_copy_menu.pack(pady=(15, 5)) # "Zielpfad"-Bereich destination_path_frame = tk.Frame(self.file_menu_frame_right) destination_path_frame.pack(pady=5, fill='x', padx=10) self.entry_destination_path = tk.Entry(destination_path_frame, textvariable=self.destination_path_var, font=text_font, width=40) self.entry_destination_path.pack(side='left') browse_button_destination = tk.Button(destination_path_frame, text="...", command=lambda: self.choose_directory(self.entry_destination_path)) browse_button_destination.pack(side='left', padx=(5, 0)) # "Kopieren"-Button button_copy = tk.Button(self.file_menu_frame_right, text="Kopieren", command=self.copy, font=self.font_normal) button_copy.pack(pady=15) keyboard_button = tk.Button(self.file_menu_frame_down, text="Tastatur", font=self.font_normal, command=self.toggle_keyboard) keyboard_button.pack(pady=10) # Horizontaler Trennstrich separator = ttk.Separator(self.file_menu_frame_right, orient='horizontal') separator.pack(fill='x', pady=5) label_file_menu = tk.Label(self.file_menu_frame_right, text="Dateimenü", font=self.font_bold) label_file_menu.pack(pady=(5, 5)) # "Speicherpfad"-Bereich label_storage_path = tk.Label(self.file_menu_frame_right, text="Speicherpfad", font=self.font_normal) label_storage_path.pack(pady=5) storage_path_frame = tk.Frame(self.file_menu_frame_right) storage_path_frame.pack(pady=5, fill='x', padx=10) # padx=10 fügt Abstand zum linken Rand hinzu self.entry_storage_path = tk.Entry(storage_path_frame, textvariable=self.storage_path_var, font=text_font, width=40) # Reduzierte Breite self.entry_storage_path.pack(side='left') browse_button_storage = tk.Button(storage_path_frame, text="...", command=lambda: self.choose_directory(self.entry_storage_path)) browse_button_storage.pack(side='left', padx=(5, 0)) # "Dateiname"-Label und Eingabefeld label_filename = tk.Label(self.file_menu_frame_right, text="Dateiname", font=self.font_normal) label_filename.pack(pady=5) self.entry_filename = tk.Entry(self.file_menu_frame_right, textvariable=self.filename_var, font=text_font, width=40) self.entry_filename.pack(pady=5) # Gemeinsamer Rahmen für Datum- und Uhrzeit-Checkboxen datetime_frame = tk.Frame(self.file_menu_frame_right) datetime_frame.pack(pady=5, fill='x') # Datum-Checkbox und Label date_frame = tk.Frame(datetime_frame) date_frame.pack(side='left', expand=True, padx=5) checkbox_date = ttk.Checkbutton(date_frame, text="Datum", style="LargeCheckbutton.TCheckbutton", variable=self.date_var) checkbox_date.grid(row=0, column=0, sticky="w") date_label = tk.Label(date_frame, text="", font=self.font_normal) date_label.grid(row=0, column=1, sticky="w") # Uhrzeit-Checkbox und Label time_frame = tk.Frame(datetime_frame) time_frame.pack(side='left', expand=True, padx=5) checkbox_time = ttk.Checkbutton(time_frame, text="Uhrzeit", style="LargeCheckbutton.TCheckbutton", variable=self.time_var) checkbox_time.grid(row=0, column=0, sticky="w") time_label = tk.Label(time_frame, text="", font=self.font_normal) time_label.grid(row=0, column=1, sticky="w") #Methode, um die Daten vom Arbeitspfad in den Zielordner zu kopieren def copy(self): try: if not os.path.exists(self.destination_path_var.get()): os.makedirs(self.destination_path_var.get()) for item in os.listdir(self.storage_path_var.get()): source_item = os.path.join(self.storage_path_var.get(), item) destination_item = os.path.join(self.destination_path_var.get(), item) if os.path.isdir(source_item): shutil.copytree(source_item, destination_item, dirs_exist_ok=True) else: shutil.copy2(source_item, destination_item) self.show_success_message("Kopieren erfolgreich", "Alle Inhalte wurden erfolgreich kopiert.") except Exception as e: messagebox.showerror("Fehler", f"Ein Fehler ist aufgetreten: {e}") #Methode, um ein Nachrichtenfenster anzuzeigen def show_success_message(self, title, message): msg_window = tk.Toplevel(self.root) msg_window.title(title) msg_window.geometry("300x100") tk.Label(msg_window, text=message).pack(pady=10) # OK-Button zum Schließen des Fensters ok_button = tk.Button(msg_window, text="OK", command=msg_window.destroy) ok_button.pack(pady=5) # Fenster automatisch nach 5 Sekunden schließen msg_window.after(5000, msg_window.destroy) #Aktuell aktives Menü schließen def close_menu(self): self.current_menu_right.grid_remove() self.current_menu_down.grid_remove() self.current_menu_down_right.grid_remove() #Aktuell aktives Menü öffnen (z.B. nachdem der Button ">", "<" gedrückt wurde) def open_menu(self): self.current_menu_right.grid() self.current_menu_down.grid() self.current_menu_down_right.grid() self.update_aspect_ratio() def show_main_menu(self): self.close_menu() self.current_menu_right = self.main_menu_frame_right self.current_menu_down = self.main_menu_frame_down self.current_menu_down_right = self.main_menu_frame_down_right self.open_menu() def show_crop_menu(self): self.close_menu() self.current_menu_right = self.crop_menu_frame_right self.current_menu_down = self.crop_menu_frame_down self.current_menu_down_right = self.crop_menu_frame_down_right self.open_menu() def show_settings_menu(self): self.close_menu() self.current_menu_right = self.settings_menu_frame_right self.current_menu_down = self.settings_menu_frame_down self.current_menu_down_right = self.settings_menu_frame_down_right self.open_menu() def show_file_menu(self): self.close_menu() self.current_menu_right = self.file_menu_frame_right self.current_menu_down = self.file_menu_frame_down self.current_menu_down_right = self.file_menu_frame_down_right self.open_menu() #Methode, um den File-Browser zu öffnen def choose_directory(self, entry_field): folder_selected = filedialog.askdirectory() if folder_selected: entry_field.delete(0, tk.END) entry_field.insert(0, folder_selected) #Methode wird ausgeführt, sobald sich der Wert in einem der Eingabefelder geändert hat def entry_fields_changed(self, var_name, *args): self.still_image_processing.update_file_parameters(self.filename_var.get(), self.storage_path_var.get(), self.destination_path_var.get(), self.date_var.get(), self.time_var.get()) self.ip.update_ruler_values(int(self.ruler_vertical_start.get()), int(self.ruler_vertical_end.get()), int(self.ruler_horizontal_start.get()), int(self.ruler_horizontal_end.get())) self.still_image_processing.get_ruler_settings(int(self.ruler_vertical_start.get()), int(self.ruler_vertical_end.get()), int(self.ruler_horizontal_start.get()), int(self.ruler_horizontal_end.get())) #Blendet das Menü ein bzw. aus def toggle_menu(self): if self.current_menu_right and self.current_menu_right.grid_info(): self.root.grid_columnconfigure(1, minsize=0) self.close_menu() self.update_aspect_ratio() self.video_widget.grid(row=0, column=0, sticky="nsew") self.hide_menu_button.config(text="<") else: if not self.current_menu_right: self.current_menu_right = self.main_menu_frame_right self.current_menu_down = self.main_menu_frame_down self.current_menu_down_right = self.main_menu_frame_down_right self.root.grid_columnconfigure(1, minsize=30) self.open_menu() self.menu_width = self.current_menu_right.winfo_width() self.video_widget.grid(row=0, column=0, sticky="nsew") self.hide_menu_button.config(text=">") #Methode um die ROI-Werte an die Bildbearbeitung zu übergeben def update_value(self, value): self.preview_processing.update_roi(self.crop_width_value.get(), self.crop_height_value.get()) self.still_image_processing.update_roi(self.crop_width_value.get(), self.crop_height_value.get()) #Externe Beleuchtung permanent ein- / ausschalten def toggle_ext_lighting(self): self.ext_lighting_state.set(not self.ext_lighting_state.get()) self.update_button_appearance(self.ext_lighting_button, self.ext_lighting_state) if self.ext_lighting_state.get(): self.permanent_lighting = True else: self.permanent_lighting = False self.gpio_instance.get_gui_settings(self.activate_flash, self.permanent_lighting) #Blitz ein- / ausschalten def toggle_flash(self): self.ext_flash_state.set(not self.ext_flash_state.get()) self.update_button_appearance(self.ext_flash_button, self.ext_flash_state) if self.ext_flash_state.get(): self.activate_flash = True else: self.activate_flash = False self.gpio_instance.get_gui_settings(self.activate_flash, self.permanent_lighting) #Button Gitter ein- aus def toggle_preview_grid(self): self.show_preview_grid_state.set(not self.show_preview_grid_state.get()) self.update_button_appearance(self.show_preview_grid_button, self.show_preview_grid_state) self.send_settings() #Button Lineal ein- aus def toggle_preview_ruler(self): self.show_preview_ruler_state.set(not self.show_preview_ruler_state.get()) self.update_button_appearance(self.show_preview_ruler_button, self.show_preview_ruler_state) self.send_settings() #Button Uhr ein - aus def toggle_preview_clock(self): self.show_preview_clock_state.set(not self.show_preview_clock_state.get()) self.update_button_appearance(self.show_preview_clock_button, self.show_preview_clock_state) self.send_settings() #Button Zoom ein- aus def toggle_zoom(self): self.zoom_button_state.set(not self.zoom_button_state.get()) self.update_button_appearance(self.zoom_button, self.zoom_button_state) self.change_preview_resolution() self.send_settings() #Aussehen der Toggle-Buttons variieren def update_button_appearance(self, button, state_var): if state_var.get(): button.config(relief="sunken", bg="dimgray") # Eingeschalteter Zustand else: button.config(relief="raised", bg="lightgray") # Ausgeschalteter Zustand #Einstellungen der Toggle-Buttons an die bildverarbeitenden Instanzen übergeben def send_settings(self): self.preview_processing.get_settings(self.show_preview_grid_state.get(), self.show_preview_ruler_state.get(), self.show_preview_clock_state.get(), self.zoom_button_state.get()) self.still_image_processing.update_picture_parameters(self.show_preview_clock_state.get(), self.show_preview_ruler_state.get()) #Seitenverhältnis anpassen, sodass das video_widget sich entsprechend der ROI anpasst def update_aspect_ratio(self): self.root.update_idletasks() self.video_widget_width = self.video_widget.winfo_width() self.video_widget_height = self.video_widget.winfo_height() self.video_widget_aspect_ratio = self.video_widget_width / self.video_widget_height if self.video_widget_aspect_ratio is None: self.new_width = 800 self.new_height = 480 else: self.new_width = self.video_widget_width self.new_height = int(self.new_width / self.camera_aspect_ratio) #Tastatur ein- aus def toggle_keyboard(self): if self.keyboard_process: self.keyboard_process.terminate() self.keyboard_process = None else: self.keyboard_process = subprocess.Popen(['matchbox-keyboard']) #Vorschaubild im video_widget aktualisieren def update_video_widget(self): image = self.camera.get_preview_image() #Bild aus der Kamera holen image = self.preview_processing.get_preview_image(image) # Weitere Bildverarbeitung #Abfragen, falls das Bild in einem unerwarteten Format auftritt if image.shape[2] not in [3, 4]: raise ValueError("Das Bild muss 3 (RGB) oder 4 (RGBA) Farbkanäle haben") if image.dtype != np.uint8: image = (image - image.min()) / (image.max() - image.min()) * 255.0 image = image.astype(np.uint8) # Erzeugen des PIL-Bildes aus dem Array und Skalieren auf die neue Größe resized_image = PIL.Image.fromarray(image).resize((self.new_width, self.new_height), PIL.Image.ANTIALIAS) self.preview_image = PIL.ImageTk.PhotoImage(image=resized_image) self.video_widget.create_image(0, 0, image=self.preview_image, anchor=tk.NW) self.root.after(self.video_feed_delay, self.update_video_widget) #Erhöhen der Vorschauauflösung, um den gezoomten Bereich klarer darzustellen def change_preview_resolution(self): self.camera.get_highres_preview() #Vollbild ein- ausschalten def toggle_fullscreen(self): if self.fullscreen_active == True: self.root.attributes('-fullscreen', False) self.fullscreen_button.config(text="Vollbild") self.fullscreen_active = False else: self.root.attributes('-fullscreen', True) self.fullscreen_button.config(text="Vollbild beenden") self.fullscreen_active = True