refactor: new mock gphoto
This commit is contained in:
@@ -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
204
core/camera/mock_gphoto.py
Normal 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
|
||||||
Reference in New Issue
Block a user