From 19e2c7977c3099609498574337c63f60e612fc28 Mon Sep 17 00:00:00 2001 From: bartool Date: Sun, 21 Sep 2025 18:46:38 +0200 Subject: [PATCH] feat: read gphoto config --- controllers/main_controller.py | 5 +- core/camera/camera_manager.py | 10 ++-- core/camera/gphoto_camera.py | 88 ++++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/controllers/main_controller.py b/controllers/main_controller.py index ff5a30a..5bfd6d5 100644 --- a/controllers/main_controller.py +++ b/controllers/main_controller.py @@ -31,6 +31,9 @@ class MainController: self.photo_button: QPushButton = view.photo_button self.photo_button.clicked.connect(self.take_photo) + self.record_button: QPushButton = view.record_button + # self.record_button.clicked.connect(self.fun_test) + self.color_list.colorSelected.connect(self.on_color_selected) self.color_list.editColor.connect(self.on_edit_color) self.thumbnail_list.selectedThumbnail.connect(self.on_thumbnail_selected) @@ -84,7 +87,7 @@ class MainController: def start_liveview(self): self.manager.start_camera() - self.manager.start_stream() + # self.manager.start_stream() def shutdown(self): self.manager.stop() \ No newline at end of file diff --git a/core/camera/camera_manager.py b/core/camera/camera_manager.py index 53345dd..31f7616 100644 --- a/core/camera/camera_manager.py +++ b/core/camera/camera_manager.py @@ -19,13 +19,15 @@ class CameraManager(QThread): self.is_connected = False - - self.start() - - def run(self) -> None: self.timer = QTimer() self.timer.setInterval(int(1000 / self.fps)) self.timer.timeout.connect(self._update_frame) + self.start() + + def run(self) -> None: + # self.timer = QTimer() + # self.timer.setInterval(int(1000 / self.fps)) + # self.timer.timeout.connect(self._update_frame) self.exec() def start_camera(self) -> None: diff --git a/core/camera/gphoto_camera.py b/core/camera/gphoto_camera.py index 54a5b71..a18e617 100644 --- a/core/camera/gphoto_camera.py +++ b/core/camera/gphoto_camera.py @@ -1,30 +1,63 @@ +from typing import Optional, List +from dataclasses import dataclass, field import gphoto2 as gp import cv2 import numpy as np from .base_camera import BaseCamera +camera_widget_types = { + gp.GP_WIDGET_WINDOW: "GP_WIDGET_WINDOW", # type: ignore + gp.GP_WIDGET_SECTION: "GP_WIDGET_SECTION", # type: ignore + gp.GP_WIDGET_TEXT: "GP_WIDGET_TEXT", # type: ignore + gp.GP_WIDGET_RANGE: "GP_WIDGET_RANGE", # type: ignore + gp.GP_WIDGET_TOGGLE: "GP_WIDGET_TOGGLE", # type: ignore + gp.GP_WIDGET_RADIO: "GP_WIDGET_RADIO", # type: ignore + gp.GP_WIDGET_MENU: "GP_WIDGET_MENU", # type: ignore + gp.GP_WIDGET_BUTTON: "GP_WIDGET_BUTTON", # type: ignore + gp.GP_WIDGET_DATE: "GP_WIDGET_DATE", # type: ignore +} + + +@dataclass +class CameraWidget: + id: int + name: str + label: str + type: str + widget: object + value: Optional[str] = None + choices: List[str] = field(default_factory=list) + + def __str__(self) -> str: + return f"[{self.id} - {self.type}] '{self.name}' {self.label}\n\tvalue = {self.value} | choices = {self.choices}" + + class GPhotoCamera(BaseCamera): def __init__(self) -> None: super().__init__() self.camera = None + self.widgets: List[CameraWidget] = [] def connect(self) -> bool: self.error_msg = None try: self.camera = gp.Camera() # type: ignore self.camera.init() + self.read_config() + widget = self.get_setting_by_name("iso") + self.set_setting_by_id(widget.id, "100") return True except Exception as e: self.error_msg = f"[GPHOTO2] {e}" self.camera = None return False - def disconnect(self) -> None: if self.camera: self.camera.exit() self.camera = None + self.widgets.clear() def get_frame(self): self.error_msg = None @@ -32,15 +65,62 @@ class GPhotoCamera(BaseCamera): if self.camera is None: self.error_msg = "[GPHOTO2] Camera is not initialized." return (False, None) - + try: file = self.camera.capture_preview() # type: ignore data = file.get_data_and_size() frame = np.frombuffer(data, dtype=np.uint8) frame = cv2.imdecode(frame, cv2.IMREAD_COLOR) - + return (True, frame) except Exception as e: self.error_msg = f"[GPHOTO2] {e}" return (False, None) - + + def get_setting_by_id(self, id: int) -> CameraWidget: + return next(w for w in self.widgets if w.id == id) + + def get_setting_by_name(self, name: str) -> CameraWidget: + return next(w for w in self.widgets if w.name == name) + + def set_setting_by_id(self, id: int, value: str): + widget = self.get_setting_by_id(id) + if value in widget.choices: + widget.widget.set_value(value) # type: ignore + self.camera.set_single_config(widget.name, widget.widget) # type: ignore + + def read_config(self): + if not self.camera: + return + + self.widgets.clear() + + def parse_widget(widget): + temp_widget = CameraWidget( + id=widget.get_id(), + name=widget.get_name(), + label=widget.get_label(), + type=camera_widget_types[widget.get_type()], + widget=widget + ) + + try: + temp_widget.value = widget.get_value() + except gp.GPhoto2Error: + pass + + try: + temp_widget.choices = list(widget.get_choices()) + except gp.GPhoto2Error: + pass + + self.widgets.append(temp_widget) + + for i in range(widget.count_children()): + parse_widget(widget.get_child(i)) + + config = self.camera.get_config() + parse_widget(config) + + for wid in self.widgets: + print(wid)