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 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)