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 } class GPhotoCamera(BaseCamera): def __init__(self) -> None: super().__init__() self.camera = None self.configs: List[dict] = [] 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 config in self.configs: print(config) config_iso = self.get_config_by_name("iso") self.set_config_by_id(config_iso['id'], "400") self.set_config_by_id(config_iso['id'], "100") self.set_config_by_id(config_iso['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.configs.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_config_by_id(self, id: int): return next(w for w in self.configs if w['id'] == id) def get_config_by_name(self, name: str): return next(w for w in self.configs if w['name'] == name) def set_config(self, config, value): if value not in config['choices']: return config['config'].set_value(value) # type: ignore if self._save_config(config): config['value'] = value def set_config_by_id(self, id: int, value: str): config = self.get_config_by_id(id) self.set_config(config, value) def set_config_by_name(self, name: str, value: str): config = self.get_config_by_name(name) self.set_config(config, value) def _save_config(self, config): if not self.camera: return False self.camera.set_single.config(config['name'], config['widget']) return True def parse_config(self, config): new_config = { "id": config.get_id(), "name": config.get_name(), "label": config.get_label(), "type": camera_widget_types[config.get_type()], "widget": config } try: new_config["value"] = config.get_value() except gp.GPhoto2Error: pass try: new_config["choices"] = list(config.get_choices()) except gp.GPhoto2Error: pass return new_config def read_config(self, config): self.configs.append(self.parse_config(config)) for i in range(config.count_children()): child = config.get_child(i) self.read_config(child)