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 config: 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() config = self.camera.get_config() self.read_config(config) for widget in self.widgets: print(widget) widget = self.get_setting_by_name("iso") self.set_setting_by_id(widget.id, "400") self.set_setting_by_id(widget.id, "100") self.set_setting_by_id(widget.id, "1600") 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 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(self, widget:CameraWidget, value): if value not in widget.choices: return widget.config.set_value(value) # type: ignore self.camera.set_single_config(widget.name, widget.config) # type: ignore widget.value = value new_config = self.camera.get_single_config(widget.name) print(f"old: {widget}") print(f"new: {new_config}") def set_setting_by_id(self, id: int, value: str): widget = self.get_setting_by_id(id) if value not in widget.choices: return widget.config.set_value(value) # type: ignore self.camera.set_single_config(widget.name, widget.config) # type: ignore # widget.value = value new_config = self.parse_widget( self.camera.get_single_config(widget.name) ) print(f"old: {widget}") print(f"new: {new_config}") def parse_widget(self, config): temp_widget = CameraWidget( id=config.get_id(), name=config.get_name(), label=config.get_label(), type=camera_widget_types[config.get_type()], config=config ) try: temp_widget.value = config.get_value() except gp.GPhoto2Error: pass try: temp_widget.choices = list(config.get_choices()) except gp.GPhoto2Error: pass return temp_widget def read_config(self, config): self.widgets.append(self.parse_widget(config)) for i in range(config.count_children()): child = config.get_child(i) self.read_config(child)