diff options
| -rw-r--r-- | chuni-hands-evolved.py | 586 |
1 files changed, 166 insertions, 420 deletions
diff --git a/chuni-hands-evolved.py b/chuni-hands-evolved.py index 0dee3fd..3bce15c 100644 --- a/chuni-hands-evolved.py +++ b/chuni-hands-evolved.py @@ -6,6 +6,8 @@ from PIL import Image, ImageTk import numpy as np import keyboard # Requires `keyboard` library import json +import threading +import time zones = [ {"x": 300, "y": 100, "width": 50, "height": 50}, @@ -32,6 +34,10 @@ CAMERA_WIDTH = 1280 CAMERA_HEIGHT = 720 ZONE_TRIGGERED_STATE = [False] * len(zones) +# Shared variables between threads +latest_frame = None +frame_lock = threading.Lock() +running = True # Control flag for the camera thread def get_avg_brightness(frame, zone): x, y, w, h = zone["x"], zone["y"], zone["width"], zone["height"] @@ -40,7 +46,6 @@ def get_avg_brightness(frame, zone): return 0 return np.mean(cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)) - def get_available_cameras(): print("Now testing to see which cameras are available... (disregard the errors here)") available_cameras = [] @@ -51,21 +56,17 @@ def get_available_cameras(): cap.release() return available_cameras - def calculate_preview_size(width, height, max_height=720): - """Calculate preview dimensions maintaining aspect ratio""" if height <= max_height: return width, height - aspect_ratio = width / height new_height = max_height new_width = int(aspect_ratio * new_height) return new_width, new_height +def setup_gui(camera_width: int, camera_height: int, preview_width: int, preview_height: int) -> tk.Tk: + global current_camera_index, x_slider, y_slider, spacing_slider, keystrokes_enabled, chuniio_enabled, update_rate, video_canvas -def setup_gui( - camera_width: int, camera_height: int, preview_width: int, preview_height: int -) -> tk.Tk: root = tk.Tk() root.title("chuni-hands-evolved") root.attributes("-topmost", True) @@ -75,7 +76,6 @@ def setup_gui( window_width = min(int(screen_width * 0.8), preview_width + 40) window_height = min(int(screen_height * 0.8), preview_height + 300) - x = (screen_width - window_width) // 2 y = (screen_height - window_height) // 2 root.geometry(f"{window_width}x{window_height}+{x}+{y}") @@ -89,207 +89,82 @@ def setup_gui( "label_font": ("Arial", 10), "button_font": ("Arial", 10, "bold"), } - root.configure(bg=style["bg"]) - main_container = tk.Frame( - root, bg=style["bg"], padx=style["padding"], pady=style["padding"] - ) - main_container.pack( - fill="both", expand=True, padx=style["padding"], pady=style["padding"] - ) + main_container = tk.Frame(root, bg=style["bg"], padx=style["padding"], pady=style["padding"]) + main_container.pack(fill="both", expand=True, padx=style["padding"], pady=style["padding"]) # Video canvas video_frame = tk.Frame(main_container, bg=style["frame_bg"], relief="ridge", bd=2) video_frame.pack(fill="both", expand=True, pady=(0, style["padding"])) - video_canvas = tk.Canvas(video_frame, width=preview_width, height=preview_height) video_canvas.pack(fill="both", expand=True) - # Camera selection - camera_frame = tk.Frame( - main_container, bg=style["frame_bg"], relief="ridge", bd=2, padx=10, pady=10 - ) + # Camera selection controls + camera_frame = tk.Frame(main_container, bg=style["frame_bg"], relief="ridge", bd=2, padx=10, pady=10) camera_frame.pack(fill="x", pady=(0, style["padding"])) - tk.Label( - camera_frame, - text="Camera Number:", - font=style["label_font"], - bg=style["frame_bg"], - ).pack(side="left") - + tk.Label(camera_frame, text="Camera Number:", font=style["label_font"], bg=style["frame_bg"]).pack(side="left") available_cameras = get_available_cameras() if not available_cameras: print("No cameras found!") available_cameras = [0] - camera_var = StringVar() - camera_var.set( - str( - current_camera_index - if current_camera_index in available_cameras - else available_cameras[0] - ) - ) + camera_var.set(str(current_camera_index if current_camera_index in available_cameras else available_cameras[0])) camera_dropdown = tk.OptionMenu(camera_frame, camera_var, *available_cameras) camera_dropdown.configure(width=10) camera_dropdown.pack(side="left", padx=(10, 0)) - tk.Label( - camera_frame, - text="Update Rate (ms):", - font=style["label_font"], - bg=style["frame_bg"], - ).pack(side="left") - - update_rate = tk.StringVar(value="5") - update_rate_spinbox = tk.Spinbox( - camera_frame, - from_=1, - to=100, - textvariable=update_rate, - width=5, - ) + tk.Label(camera_frame, text="Update Rate (ms):", font=style["label_font"], bg=style["frame_bg"]).pack(side="left") + update_rate = StringVar(value="5") + update_rate_spinbox = tk.Spinbox(camera_frame, from_=1, to=100, textvariable=update_rate, width=5) update_rate_spinbox.pack(side="left", padx=5) - tk.Label( - camera_frame, - text="You should set the update rate to something like 100 while editing for a smoother experience, then set it back to 5 for normal use.", - font=style["label_font"], - bg=style["frame_bg"], - ).pack(side="top", pady=(0, style["padding"])) + tk.Label(camera_frame, + text="Set a high update rate (e.g. 100) while editing, then a lower value (e.g. 5) for normal use.", + font=style["label_font"], bg=style["frame_bg"]).pack(side="top", pady=(0, style["padding"])) - controls_frame = tk.Frame( - main_container, bg=style["frame_bg"], relief="ridge", bd=2, padx=10, pady=10 - ) + # Position and spacing controls + controls_frame = tk.Frame(main_container, bg=style["frame_bg"], relief="ridge", bd=2, padx=10, pady=10) controls_frame.pack(fill="x", pady=(0, style["padding"])) - + inputs_frame = tk.Frame(controls_frame, bg=style["frame_bg"]) inputs_frame.pack(fill="x") + # X Position x_frame = tk.Frame(inputs_frame, bg=style["frame_bg"]) x_frame.pack(fill="x", pady=(0, 5)) - tk.Label( - x_frame, - text="X Position:", - font=style["label_font"], - bg=style["frame_bg"], - width=15, - ).pack(side="left") - - x_var = tk.StringVar(value="0") - x_spinbox = tk.Spinbox( - x_frame, - from_=-500, - to=1280, - textvariable=x_var, - increment=10, - width=10, - command=lambda: update_position("x"), - ) - x_spinbox.pack(side="left", padx=5) - - x_slider = Scale( - x_frame, - from_=-500, - to=1280, - orient="horizontal", - length=400, - showvalue=0, - command=lambda v: x_var.set(str(int(float(v)))), - ) + tk.Label(x_frame, text="X Position:", font=style["label_font"], bg=style["frame_bg"], width=15).pack(side="left") + x_var = StringVar(value="0") + x_slider = Scale(x_frame, from_=-500, to=1280, orient="horizontal", length=400, showvalue=0, + command=lambda v: x_var.set(str(int(float(v))))) x_slider.pack(side="left", fill="x", expand=True, padx=5) + # Y Position y_frame = tk.Frame(inputs_frame, bg=style["frame_bg"]) y_frame.pack(fill="x", pady=(0, 5)) - tk.Label( - y_frame, - text="Y Position:", - font=style["label_font"], - bg=style["frame_bg"], - width=15, - ).pack(side="left") - - y_var = tk.StringVar(value="0") - y_spinbox = tk.Spinbox( - y_frame, - from_=-200, - to=720, - textvariable=y_var, - increment=10, - width=10, - command=lambda: update_position("y"), - ) - y_spinbox.pack(side="left", padx=5) - - y_slider = Scale( - y_frame, - from_=-200, - to=720, - orient="horizontal", - length=400, - showvalue=0, - command=lambda v: y_var.set(str(int(float(v)))), - ) + tk.Label(y_frame, text="Y Position:", font=style["label_font"], bg=style["frame_bg"], width=15).pack(side="left") + y_var = StringVar(value="0") + y_slider = Scale(y_frame, from_=-200, to=720, orient="horizontal", length=400, showvalue=0, + command=lambda v: y_var.set(str(int(float(v))))) y_slider.pack(side="left", fill="x", expand=True, padx=5) - # Spacing controls + # Spacing spacing_frame = tk.Frame(inputs_frame, bg=style["frame_bg"]) spacing_frame.pack(fill="x") - tk.Label( - spacing_frame, - text="Sensor Spacing:", - font=style["label_font"], - bg=style["frame_bg"], - width=15, - ).pack(side="left") - - spacing_var = tk.StringVar(value="100") - spacing_spinbox = tk.Spinbox( - spacing_frame, - from_=10, - to=300, - textvariable=spacing_var, - increment=5, - width=10, - command=lambda: update_position("spacing"), - ) - spacing_spinbox.pack(side="left", padx=5) - - spacing_slider = Scale( - spacing_frame, - from_=10, - to=300, - orient="horizontal", - length=400, - showvalue=0, - command=lambda v: spacing_var.set(str(int(float(v)))), - ) + tk.Label(spacing_frame, text="Sensor Spacing:", font=style["label_font"], bg=style["frame_bg"], width=15).pack(side="left") + spacing_var = StringVar(value="100") + spacing_slider = Scale(spacing_frame, from_=10, to=300, orient="horizontal", length=400, showvalue=0, + command=lambda v: spacing_var.set(str(int(float(v))))) spacing_slider.pack(side="left", fill="x", expand=True, padx=5) + # Sensor Width width_frame = tk.Frame(inputs_frame, bg=style["frame_bg"]) width_frame.pack(fill="x") - tk.Label( - width_frame, - text="Sensor Width:", - font=style["label_font"], - bg=style["frame_bg"], - width=15, - ).pack(side="left") - - width_var = tk.StringVar(value="50") - width_spinbox = tk.Spinbox( - width_frame, - from_=10, - to=1000, - textvariable=width_var, - increment=5, - width=10, - command=lambda: update_width(), - ) + tk.Label(width_frame, text="Sensor Width:", font=style["label_font"], bg=style["frame_bg"], width=15).pack(side="left") + width_var = StringVar(value="50") + width_spinbox = tk.Spinbox(width_frame, from_=10, to=1000, textvariable=width_var, increment=5, width=10) width_spinbox.pack(side="left", padx=5) - def update_width(): try: value = int(width_var.get()) @@ -298,40 +173,11 @@ def setup_gui( zone["width"] = value except ValueError: pass + width_spinbox.config(command=update_width) - def update_position(type): - try: - if type == "x": - value = int(x_var.get()) - if -500 <= value <= 1280: - x_slider.set(value) - elif type == "y": - value = int(y_var.get()) - if -200 <= value <= 720: - y_slider.set(value) - elif type == "spacing": - value = int(spacing_var.get()) - if 10 <= value <= 300: - spacing_slider.set(value) - except ValueError: - pass - - def on_mousewheel(event, spinbox): - if event.delta > 0: - spinbox.invoke("buttonup") - else: - spinbox.invoke("buttondown") - - x_spinbox.bind("<MouseWheel>", lambda e: on_mousewheel(e, x_spinbox)) - y_spinbox.bind("<MouseWheel>", lambda e: on_mousewheel(e, y_spinbox)) - spacing_spinbox.bind("<MouseWheel>", lambda e: on_mousewheel(e, spacing_spinbox)) - width_spinbox.bind("<MouseWheel>", lambda e: on_mousewheel(e, width_spinbox)) - - buttons_frame = tk.Frame( - main_container, bg=style["frame_bg"], relief="ridge", bd=2, padx=10, pady=10 - ) + # Buttons + buttons_frame = tk.Frame(main_container, bg=style["frame_bg"], relief="ridge", bd=2, padx=10, pady=10) buttons_frame.pack(fill="x") - def recalibrate(): ret, frame = cap.read() if ret: @@ -339,7 +185,9 @@ def setup_gui( print("Recalibration complete.") else: print("Error: Could not capture frame for recalibration.") - + calibration_button = Button(buttons_frame, text="Recalibrate", command=recalibrate, bg=style["button_bg"], + fg=style["button_fg"], font=style["button_font"], relief="flat", padx=20, pady=5) + calibration_button.pack(side="left", padx=5) def save_config(): config = { "x_offset": x_slider.get(), @@ -352,113 +200,50 @@ def setup_gui( with open(CONFIG_FILE, "w") as f: json.dump(config, f) messagebox.showinfo("Config", "Configuration saved successfully.") - - button_kwargs = { - "bg": style["button_bg"], - "fg": style["button_fg"], - "font": style["button_font"], - "relief": "flat", - "padx": 20, - "pady": 5, - } - - calibration_button = Button( - buttons_frame, text="Recalibrate", command=recalibrate, **button_kwargs - ) - calibration_button.pack(side="left", padx=5) - - save_button = Button( - buttons_frame, text="Save Config", command=save_config, **button_kwargs - ) + save_button = Button(buttons_frame, text="Save Config", command=save_config, bg=style["button_bg"], + fg=style["button_fg"], font=style["button_font"], relief="flat", padx=20, pady=5) save_button.pack(side="left", padx=5) - chuniio_enabled = BooleanVar(value=True) - chuniio_button = Checkbutton( - buttons_frame, - text="Brokenithm Evolved chuniio", - variable=chuniio_enabled, - font=style["button_font"], - relief="flat", - padx=20, - pady=5, - bg=style["frame_bg"], - ) + chuniio_button = Checkbutton(buttons_frame, text="Brokenithm Evolved chuniio", variable=chuniio_enabled, + font=style["button_font"], relief="flat", padx=20, pady=5, bg=style["frame_bg"]) chuniio_button.pack(side="right", padx=5) - keystrokes_enabled = BooleanVar(value=False) - keystroke_button = Button( - buttons_frame, - text="⌨ Keystrokes: OFF", - command=lambda: toggle_keystrokes(), - **button_kwargs, - ) + keystroke_button = Button(buttons_frame, text="⌨ Keystrokes: OFF", + command=lambda: toggle_keystrokes(), bg=style["button_bg"], + fg=style["button_fg"], font=style["button_font"], relief="flat", padx=20, pady=5) keystroke_button.pack(side="right", padx=5) - def toggle_keystrokes(): current = keystrokes_enabled.get() keystrokes_enabled.set(not current) - keystroke_button.config( - text="⌨ Keystrokes: ON" if not current else "⌨ Keystrokes: OFF", - bg="#4a90e2" if not current else "#e74c3c", - ) - + keystroke_button.config(text="⌨ Keystrokes: ON" if not current else "⌨ Keystrokes: OFF", + bg="#4a90e2" if not current else "#e74c3c") + def switch_camera(camera_width: int, camera_height: int): + global cap, current_camera_index + current_camera_index = int(camera_var.get()) + cap.release() + cap = cv2.VideoCapture(current_camera_index) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, camera_width) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_height) + if not cap.isOpened(): + messagebox.showerror("Camera Error", "Failed to access the selected camera.") + camera_var.trace_add("write", lambda *args: switch_camera(camera_width, camera_height)) + def on_window_resize(event): if event.widget == root: new_width = video_frame.winfo_width() new_height = video_frame.winfo_height() - aspect_ratio = preview_width / preview_height - - if new_width / new_height > aspect_ratio: + aspect_ratio = preview_width/preview_height + if new_width/new_height > aspect_ratio: canvas_height = new_height - canvas_width = int(canvas_height * aspect_ratio) + canvas_width = int(canvas_height*aspect_ratio) else: canvas_width = new_width - canvas_height = int(canvas_width / aspect_ratio) - + canvas_height = int(canvas_width/aspect_ratio) video_canvas.configure(width=canvas_width, height=canvas_height) - root.bind("<Configure>", on_window_resize) - controls_frame = tk.Frame( - main_container, bg=style["frame_bg"], relief="ridge", bd=2 - ) - controls_frame.pack(fill="x", pady=(0, style["padding"])) - - buttons_frame = tk.Frame(main_container, bg=style["frame_bg"], relief="ridge", bd=2) - buttons_frame.pack(fill="x") - for slider in [x_slider, y_slider, spacing_slider]: - slider.pack(fill="x", expand=True, padx=5) - - def switch_camera(camera_width: int, camera_height: int): - global cap, current_camera_index - current_camera_index = int(camera_var.get()) - cap.release() - cap = cv2.VideoCapture(current_camera_index) - cap.set(cv2.CAP_PROP_FRAME_WIDTH, camera_width) - cap.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_height) - if not cap.isOpened(): - messagebox.showerror( - "Camera Error", "Failed to access the selected camera." - ) - - camera_var.trace_add( - "write", - lambda *args: switch_camera( - camera_width=camera_width, camera_height=camera_height - ), - ) - - return ( - root, - video_canvas, - x_slider, - y_slider, - width_var, - spacing_slider, - keystrokes_enabled, - chuniio_enabled, - update_rate, - ) - + + return (root, video_canvas, x_slider, y_slider, width_var, spacing_slider, + keystrokes_enabled, chuniio_enabled, update_rate) def calibrate(frame): global zone_color_state @@ -468,108 +253,89 @@ def calibrate(frame): zone_color_state.append(brightness) print("Calibration completed. Initial lighting values:", zone_color_state) +def camera_loop(preview_width, preview_height, chuniio_shared_memory): + global cap, latest_frame, running + while running: + ret, frame = cap.read() + if not ret: + print("Error: Could not capture frame.") + continue + frame = cv2.flip(frame, 1) + # Get offsets and spacing from the UI controls (assumed to be safe for reading) + try: + x_offset = int(x_slider.get()) + y_offset = int(y_slider.get()) + spacing = int(spacing_slider.get()) + except Exception: + x_offset, y_offset, spacing = 0, 0, 100 + + # Update zone positions + for i, zone in enumerate(zones): + zone["x"] = base_positions[i]["x"] + x_offset + zone["y"] = base_positions[0]["y"] + y_offset + i * spacing -def update_frame( - preview_width, - preview_height, - chuniio_shared_memory, -): - global cap - ret, frame = cap.read() - if not ret: - print("Error: Could not capture frame.") - return + # Process zones + for i, zone in enumerate(zones): + current_brightness = get_avg_brightness(frame, zone) + if abs(current_brightness - zone_color_state[i]) > 20: + if not ZONE_TRIGGERED_STATE[i]: + if keystrokes_enabled.get(): + print(f"Key '{_UMIGIRI_32_AIRZONE_LAYOUT[i]}' pressed.") + keyboard.press(_UMIGIRI_32_AIRZONE_LAYOUT[i]) + print(f"Zone {i} triggered") + ZONE_TRIGGERED_STATE[i] = True + cv2.rectangle(frame, (zone["x"], zone["y"]), + (zone["x"]+zone["width"], zone["y"]+zone["height"]), (0,255,0), 3) + else: + if ZONE_TRIGGERED_STATE[i]: + if keystrokes_enabled.get(): + keyboard.release(_UMIGIRI_32_AIRZONE_LAYOUT[i]) + print(f"Key '{_UMIGIRI_32_AIRZONE_LAYOUT[i]}' released.") + ZONE_TRIGGERED_STATE[i] = False + cv2.rectangle(frame, (zone["x"], zone["y"]), + (zone["x"]+zone["width"], zone["y"]+zone["height"]), (0,0,255), 2) - frame = cv2.flip(frame, 1) - x_offset = x_slider.get() - y_offset = y_slider.get() - spacing = spacing_slider.get() + if chuniio_enabled.get(): + chuniio.write_to_airzone(ZONE_TRIGGERED_STATE, chuniio_shared_memory) - # Update zone positions - for i, zone in enumerate(zones): - zone["x"] = base_positions[i]["x"] + x_offset - zone["y"] = base_positions[0]["y"] + y_offset + i * spacing + # Convert frame to RGB + rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + with frame_lock: + latest_frame = rgb_frame + + try: + rate = max(1, int(update_rate.get())) + except ValueError: + rate = 5 + time.sleep(rate/1000) - # Process zones - for i, zone in enumerate(zones): - current_brightness = get_avg_brightness(frame, zone) - if abs(current_brightness - zone_color_state[i]) > 20: - if not ZONE_TRIGGERED_STATE[i]: - if keystrokes_enabled.get(): - print(f"Key '{_UMIGIRI_32_AIRZONE_LAYOUT[i]}' pressed.") - keyboard.press(_UMIGIRI_32_AIRZONE_LAYOUT[i]) - print(f"Zone {i} triggered") - ZONE_TRIGGERED_STATE[i] = True - cv2.rectangle( - frame, - (zone["x"], zone["y"]), - (zone["x"] + zone["width"], zone["y"] + zone["height"]), - (0, 255, 0), - 3, - ) +def update_canvas(): + global latest_frame + with frame_lock: + frame_copy = latest_frame.copy() if latest_frame is not None else None + if frame_copy is not None: + canvas_width = video_canvas.winfo_width() + canvas_height = video_canvas.winfo_height() + aspect_ratio = frame_copy.shape[1] / frame_copy.shape[0] + if canvas_width / canvas_height > aspect_ratio: + display_height = canvas_height + display_width = int(display_height * aspect_ratio) else: - if ZONE_TRIGGERED_STATE[i]: - if keystrokes_enabled.get(): - keyboard.release(_UMIGIRI_32_AIRZONE_LAYOUT[i]) - print(f"Key '{_UMIGIRI_32_AIRZONE_LAYOUT[i]}' released.") - ZONE_TRIGGERED_STATE[i] = False - cv2.rectangle( - frame, - (zone["x"], zone["y"]), - (zone["x"] + zone["width"], zone["y"] + zone["height"]), - (0, 0, 255), - 2, - ) - - if chuniio_enabled.get(): - chuniio.write_to_airzone(ZONE_TRIGGERED_STATE, chuniio_shared_memory) - - # Convert to RGB - rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - - # Get canvas dimensions - canvas_width = video_canvas.winfo_width() - canvas_height = video_canvas.winfo_height() - - # Ensure valid dimensions before resizing - if canvas_width <= 0 or canvas_height <= 0: - canvas_width = preview_width - canvas_height = preview_height - - # Calculate aspect ratio preserving dimensions - aspect_ratio = frame.shape[1] / frame.shape[0] - if canvas_width / canvas_height > aspect_ratio: - display_height = canvas_height - display_width = int(display_height * aspect_ratio) - else: - display_width = canvas_width - display_height = int(display_width / aspect_ratio) - - # Ensure minimum dimensions - display_width = max(display_width, 1) - display_height = max(display_height, 1) - - # Resize frame - try: - rgb_frame = cv2.resize(rgb_frame, (display_width, display_height)) - except cv2.error as e: - print(f"Resize error: {display_width}x{display_height}") - return - - # Display frame - img = ImageTk.PhotoImage(Image.fromarray(rgb_frame)) - video_canvas.delete("all") - video_canvas.create_image( - canvas_width // 2, canvas_height // 2, anchor="center", image=img - ) - video_canvas.image = img - - try: - rate = max(1, int(update_rate.get())) - except ValueError: - rate = 5 - root.after(rate, update_frame, preview_width, preview_height, chuniio_shared_memory) - + display_width = canvas_width + display_height = int(display_width / aspect_ratio) + display_width = max(display_width, 1) + display_height = max(display_height, 1) + try: + resized_frame = cv2.resize(frame_copy, (display_width, display_height)) + except Exception as e: + print("Resize error:", e) + resized_frame = frame_copy + img = ImageTk.PhotoImage(Image.fromarray(resized_frame)) + video_canvas.delete("all") + video_canvas.create_image(canvas_width//2, canvas_height//2, anchor="center", image=img) + video_canvas.image = img + root.after(33, update_canvas) # refresh UI at ~30 FPS def load_config() -> dict: try: @@ -578,22 +344,8 @@ def load_config() -> dict: except FileNotFoundError: return {} - def main(): - global \ - root, \ - current_camera_index, \ - keystrokes_enabled, \ - chuniio_enabled, \ - video_canvas, \ - x_slider, \ - y_slider, \ - width_var, \ - spacing_slider, \ - cap, \ - update_rate, \ - CAMERA_WIDTH, \ - CAMERA_HEIGHT + global root, current_camera_index, cap, video_canvas, x_slider, y_slider, width_var, spacing_slider, keystrokes_enabled, chuniio_enabled, update_rate, CAMERA_WIDTH, CAMERA_HEIGHT, latest_frame, running print("Now starting up... please wait a bit...") config = load_config() current_camera_index = config.get("camera_index", 0) @@ -606,11 +358,8 @@ def main(): actual_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) actual_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) - if actual_width != CAMERA_WIDTH or actual_height != CAMERA_HEIGHT: - print( - f"Resolution {CAMERA_WIDTH}x{CAMERA_HEIGHT} not supported. Using {actual_width}x{actual_height}." - ) + print(f"Resolution {CAMERA_WIDTH}x{CAMERA_HEIGHT} not supported. Using {actual_width}x{actual_height}.") CAMERA_WIDTH = int(actual_width) CAMERA_HEIGHT = int(actual_height) cap.set(cv2.CAP_PROP_FRAME_WIDTH, actual_width) @@ -621,29 +370,26 @@ def main(): else: print("Error: Could not capture initial frame. Check your camera.") - ( - root, - video_canvas, - x_slider, - y_slider, - width_var, - spacing_slider, - keystrokes_enabled, - chuniio_enabled, - update_rate, - ) = setup_gui(CAMERA_WIDTH, CAMERA_HEIGHT, preview_width, preview_height) + (root, video_canvas, x_slider, y_slider, width_var, spacing_slider, + keystrokes_enabled, chuniio_enabled, update_rate) = setup_gui(CAMERA_WIDTH, CAMERA_HEIGHT, preview_width, preview_height) x_slider.set(config.get("x_offset", 0)) y_slider.set(config.get("y_offset", 0)) spacing_slider.set(config.get("spacing", 100)) chuniio_enabled.set(config.get("chuniio_enabled", True)) - chuniio_shared_memory = chuniio.open_sharedmem() if chuniio_enabled.get() else None width_var.set(str(config.get("width", 50))) for zone in zones: zone["width"] = int(width_var.get()) - update_frame(preview_width, preview_height, chuniio_shared_memory) + + chuniio_shared_memory = chuniio.open_sharedmem() if chuniio_enabled.get() else None + + cam_thread = threading.Thread(target=camera_loop, args=(preview_width, preview_height, chuniio_shared_memory), daemon=True) + cam_thread.start() + + update_canvas() + root.mainloop() + running = False cap.release() - main() |
