diff --git a/controllers/mock_gphoto.py b/controllers/mock_gphoto.py index bbe6bc5..832b102 100644 --- a/controllers/mock_gphoto.py +++ b/controllers/mock_gphoto.py @@ -1,6 +1,16 @@ import cv2 import numpy as np +GP_WIDGET_WINDOW = 0 +GP_WIDGET_SECTION = 0 +GP_WIDGET_TEXT = 0 +GP_WIDGET_RANGE = 0 +GP_WIDGET_TOGGLE = 0 +GP_WIDGET_RADIO = 0 +GP_WIDGET_MENU = 0 +GP_WIDGET_BUTTON = 0 +GP_WIDGET_DATE = 0 + class GPhoto2Error(Exception): pass @@ -19,6 +29,65 @@ class CameraFileMock: return self._data return self._data, len(self._data) +class CameraListMock: + def count(self): + return 1 + + def get_name(self, idx): + return f"mock_name {idx}" + + def get_value(self, idx): + return f"mock_value {idx}" + +class MockPortInfo: + def __init__(self, address): + self.address = address + +class PortInfoList: + def __init__(self): + self._ports = [] + + def load(self): + # Dodaj przykładowe porty + self._ports = [MockPortInfo("usb:001,002"), MockPortInfo("usb:001,003")] + + def lookup_path(self, port_address): + for idx, port in enumerate(self._ports): + if port.address == port_address: + return idx + raise ValueError("Port not found") + + def __getitem__(self, idx): + return self._ports[idx] + +class ConfigMock: + def get_id(self): + return 0 + def get_name(self): + return "name" + def get_label(self): + return "label" + def get_type(self): + return 0 + def get_value(self): + return "value" + def get_choices(self): + return [] + def count_children(self): + return 0 + def get_child(self): + return ConfigMock() + + +class CameraAbilitiesList: + def __init__(self) -> None: + self.abilities = [] + def load(self): + return + def lookup_model(self, name): + return 1 + def get_abilities(self, abilities_index): + return 0 class Camera: def __init__(self): @@ -62,3 +131,18 @@ class Camera: self._frame_counter += 1 return CameraFileMock(frame) + + def set_port_info(self, obj): + return False + + def get_config(self): + return ConfigMock() + + def set_single_config(self, name, widget): + return True + +def gp_camera_autodetect(): + return CameraListMock() + +def check_result(obj): + return obj \ No newline at end of file diff --git a/core/camera/gphoto_camera.py b/core/camera/gphoto_camera.py index 46b1f88..003bfd5 100644 --- a/core/camera/gphoto_camera.py +++ b/core/camera/gphoto_camera.py @@ -1,11 +1,15 @@ 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 +try: + import gphoto2 as gp # type: ignore +except: + import controllers.mock_gphoto as gp + camera_widget_types = { gp.GP_WIDGET_WINDOW: "GP_WIDGET_WINDOW", # type: ignore gp.GP_WIDGET_SECTION: "GP_WIDGET_SECTION", # type: ignore @@ -18,6 +22,16 @@ camera_widget_types = { gp.GP_WIDGET_DATE: "GP_WIDGET_DATE", # type: ignore } +operations = [ + ("GP_OPERATION_NONE", gp.GP_OPERATION_NONE), # type: ignore + ("GP_OPERATION_CAPTURE_IMAGE", gp.GP_OPERATION_CAPTURE_IMAGE), # type: ignore + ("GP_OPERATION_CAPTURE_VIDEO", gp.GP_OPERATION_CAPTURE_VIDEO), # type: ignore + ("GP_OPERATION_CAPTURE_AUDIO", gp.GP_OPERATION_CAPTURE_AUDIO), # type: ignore + ("GP_OPERATION_CAPTURE_PREVIEW", gp.GP_OPERATION_CAPTURE_PREVIEW), # type: ignore + ("GP_OPERATION_CONFIG", gp.GP_OPERATION_CONFIG), # type: ignore + ("GP_OPERATION_TRIGGER_CAPTURE", gp.GP_OPERATION_TRIGGER_CAPTURE), # type: ignore +] + class GPhotoCamera(BaseCamera): def __init__(self) -> None: @@ -33,11 +47,21 @@ class GPhotoCamera(BaseCamera): if not cameras or cameras.count() == 0: # type: ignore return {} + abilities_list = gp.CameraAbilitiesList() # type: ignore + abilities_list.load() camera_list = {} for i in range(cameras.count()): # type: ignore name = cameras.get_name(i) # type: ignore port = cameras.get_value(i) # type: ignore - camera_list[i] = {"name": name, "port": port} + + abilities_index = abilities_list.lookup_model(name) + abilities = abilities_list.get_abilities(abilities_index) + abilities_name = [] + for name, bit in operations: + if abilities.operations & bit: # type: ignore + abilities_name.append(name) + + camera_list[i] = {"name": name, "port": port, "abilities": abilities_name} return camera_list def connect(self, index: int | None = None) -> bool: @@ -117,7 +141,7 @@ class GPhotoCamera(BaseCamera): if not self.camera: return False - self.camera.set_single.config(config['name'], config['widget']) + self.camera.set_single_config(config['name'], config['widget']) return True def parse_config(self, config): diff --git a/core/camera/opencv_camera.py b/core/camera/opencv_camera.py index edcb678..06f9016 100644 --- a/core/camera/opencv_camera.py +++ b/core/camera/opencv_camera.py @@ -28,24 +28,18 @@ class OpenCvCamera(BaseCamera): def detect(): camera_list = enumerate_cameras(cv2.CAP_ANY) result = {} - seen_ports = set() for camera in camera_list: - # unikamy duplikatów tego samego /dev/videoX albo tej samej ścieżki na Windows/macOS - if camera.path in seen_ports: - continue - - cap = cv2.VideoCapture(camera.index, camera.backend) # próbujemy otworzyć - ret, frame = cap.read() + cap = cv2.VideoCapture(camera.index, camera.backend) + # ret, frame = cap.read() cap.release() - if ret and frame is not None and frame.size > 0: - result[camera.index] = { - "name": camera.name, - "port": camera.path, - "backend": camera.backend, - } - seen_ports.add(camera.path) + # if ret and frame is not None and frame.size > 0: + result[camera.index] = { + "name": camera.name, + "port": camera.path, + "backend": camera.backend, + } return result