refactor: new mock gphoto

This commit is contained in:
2025-10-09 21:35:50 +02:00
parent 71a55843c1
commit cc37d7054c
2 changed files with 205 additions and 1 deletions

View File

@@ -8,7 +8,7 @@ from .base_camera import BaseCamera
try: try:
import gphoto2 as gp # type: ignore import gphoto2 as gp # type: ignore
except: except:
import controllers.mock_gphoto as gp import core.camera.mock_gphoto as gp
camera_widget_types = { camera_widget_types = {
gp.GP_WIDGET_WINDOW: "GP_WIDGET_WINDOW", # type: ignore gp.GP_WIDGET_WINDOW: "GP_WIDGET_WINDOW", # type: ignore

204
core/camera/mock_gphoto.py Normal file
View File

@@ -0,0 +1,204 @@
"""
Mock gphoto2 module for Windows testing and development.
Simulates gphoto2 API behavior to allow the app to run without a camera.
"""
import numpy as np
import cv2
from dataclasses import dataclass
# --- Constants (simulate gphoto2 enums) ---
GP_WIDGET_WINDOW = 0
GP_WIDGET_SECTION = 1
GP_WIDGET_TEXT = 2
GP_WIDGET_RANGE = 3
GP_WIDGET_TOGGLE = 4
GP_WIDGET_RADIO = 5
GP_WIDGET_MENU = 6
GP_WIDGET_BUTTON = 7
GP_WIDGET_DATE = 8
GP_OPERATION_NONE = 0x00
GP_OPERATION_CAPTURE_IMAGE = 0x01
GP_OPERATION_CAPTURE_VIDEO = 0x02
GP_OPERATION_CAPTURE_AUDIO = 0x04
GP_OPERATION_CAPTURE_PREVIEW = 0x08
GP_OPERATION_CONFIG = 0x10
GP_OPERATION_TRIGGER_CAPTURE = 0x20
# --- Error class ---
class GPhoto2Error(Exception):
pass
# --- Mock camera configuration widget ---
class MockWidget:
def __init__(self, name, label, wtype, value=None, choices=None):
self._name = name
self._label = label
self._type = wtype
self._value = value
self._choices = choices or []
self._children = []
def get_id(self):
return id(self)
def get_name(self):
return self._name
def get_label(self):
return self._label
def get_type(self):
return self._type
def get_value(self):
return self._value
def set_value(self, value):
if self._choices and value not in self._choices:
raise GPhoto2Error(f"Invalid value '{value}' for widget '{self._name}'")
self._value = value
def get_choices(self):
return self._choices
def count_children(self):
return len(self._children)
def get_child(self, i):
return self._children[i]
def add_child(self, child):
self._children.append(child)
# --- Mock classes for detection / abilities ---
@dataclass
class MockCameraInfo:
name: str
port: str
class MockCameraList:
def __init__(self):
self._cameras = [
MockCameraInfo("Mock Camera 1", "usb:001,002"),
MockCameraInfo("Mock Camera 2", "usb:001,003")
]
def count(self):
return len(self._cameras)
def get_name(self, i):
return self._cameras[i].name
def get_value(self, i):
return self._cameras[i].port
class CameraAbilities:
def __init__(self, operations):
self.operations = operations
class CameraAbilitiesList:
def load(self):
pass
def lookup_model(self, name):
return 0
def get_abilities(self, index):
return CameraAbilities(
GP_OPERATION_CAPTURE_IMAGE
| GP_OPERATION_CAPTURE_PREVIEW
| GP_OPERATION_CONFIG
)
class PortInfoList:
def load(self):
pass
def lookup_path(self, path):
return 0
def __getitem__(self, index):
return f"MockPortInfo({index})"
# --- Mock Camera class ---
class Camera:
def __init__(self):
self.initialized = False
self.port_info = None
def init(self):
self.initialized = True
def exit(self):
self.initialized = False
def set_port_info(self, info):
self.port_info = info
def get_config(self):
# Simulate config tree
root = MockWidget("root", "Root", GP_WIDGET_WINDOW)
iso = MockWidget("iso", "ISO", GP_WIDGET_MENU, "100", ["100", "200", "400", "800"])
shutter = MockWidget("shutter", "Shutter Speed", GP_WIDGET_MENU, "1/60", ["1/30", "1/60", "1/125"])
wb = MockWidget("whitebalance", "White Balance", GP_WIDGET_RADIO, "Auto", ["Auto", "Daylight", "Tungsten"])
root.add_child(iso)
root.add_child(shutter)
root.add_child(wb)
return root
def set_single_config(self, name, widget):
# Simulate saving a setting
print(f"[mock_gphoto] Setting '{name}' = '{widget.get_value()}'")
def capture_preview(self):
# Generate a fake image (OpenCV compatible)
frame = np.zeros((480, 640, 3), dtype=np.uint8)
cv2.putText(frame, "Mock Preview", (150, 240), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
_, buf = cv2.imencode(".jpg", frame)
class MockFile:
def get_data_and_size(self_inner):
return buf.tobytes()
return MockFile()
# --- Mock detection functions ---
def gp_camera_autodetect():
return MockCameraList()
def check_result(value):
# gphoto2.check_result usually raises error if return < 0
return value
# --- API aliases to match gphoto2 ---
GP_ERROR = -1
gp_camera_autodetect = gp_camera_autodetect
check_result = check_result
Camera = Camera
CameraAbilitiesList = CameraAbilitiesList
PortInfoList = PortInfoList
GPhoto2Error = GPhoto2Error