camera
系统相机拍照与 AppUI CameraPicker。
系统相机拍照:弹出相机界面,返回 PIL.Image 或 Base64 字符串。
边界:无独立import camera模块,拍照 API 在 photos 的capture_image()/capture_image_base64()。录像请用 video_recorder。统一 permission 的request("camera")暂不支持弹窗;首次拍照时系统可能提示,或在设置中开启相机权限。
#模块概览
| 项 | 说明 |
|---|---|
| 导入 | import photos |
| 适合做什么 | 拍照存档、视觉识别前置、现场采集图片 |
| 调用时机 | capture_image() 放在按钮回调;不要写在 AppUI body() 中 |
| 推荐顺序 | 用户点击 → capture_image() → 判空 → 处理或 save_image() |
| AppUI 替代 | 声明式界面可用 appui.CameraPicker(回调返回文件路径) |
#快速开始
下面脚本打开相机拍一张,打印尺寸;用户取消时友好退出:
已复制
import photos
image = photos.capture_image()
if image is None:
print("用户取消拍照")
else:
width, height = photos.get_image_size(image)
print(f"已拍摄 {width}×{height}")
ok = photos.save_image(image, format="JPEG", quality=0.85)
print("保存到相册:", "成功" if ok else "失败")
需要 Base64(如 vision_helper)时:
已复制
import photos
payload = photos.capture_image_base64(format="JPEG", quality=0.85)
if payload:
print("Base64 长度:", len(payload))
#AppUI 示例 {#appui-camera-picker}
脚本式拍照放按钮回调;界面内也可嵌入 CameraPicker。
已复制
import appui
import photos
state = appui.State(
status="等待操作",
size="—",
path="",
message="点击按钮或下方拍照控件",
)
def capture_with_photos():
image = photos.capture_image()
if image is None:
state.batch_update(
status="已取消",
size="—",
path="",
message="用户取消拍照",
)
return
width, height = photos.get_image_size(image)
state.batch_update(
status="已拍照",
size=f"{width}×{height}",
path="内存图像(PIL/bytes)",
message="可用 save_image 写入相册",
)
def on_camera_picked(path):
state.batch_update(
status="CameraPicker 完成",
size="—",
path=path or "",
message="已取消" if not path else "收到临时文件路径",
)
def body():
return appui.NavigationStack(
appui.Form([
appui.Section("脚本拍照", [
appui.Button("打开相机(photos)", action=capture_with_photos)
.button_style("bordered_prominent"),
]),
appui.Section("声明式拍照", [
appui.CameraPicker(
source="camera",
media_type="photo",
on_captured=on_camera_picked,
label=appui.Label("拍照", system_image="camera.fill"),
),
]),
appui.Section("结果", [
appui.LabeledContent("状态", value=state.status),
appui.LabeledContent("尺寸", value=state.size),
appui.Text(state.path).font("caption").foreground_color("secondaryLabel"),
appui.Text(state.message).foreground_color("secondaryLabel"),
], footer="真机测试最可靠;模拟器可能无相机。"),
]).navigation_title("相机")
)
appui.run(body, state=state)
#API 参考
#速查
| API | 作用 |
|---|---|
capture_image(...) | 系统相机拍照 → PIL.Image / None |
capture_image_base64(...) | 拍照 → Base64 字符串 / None |
save_image(image, ...) | 保存到相册 → bool |
get_image_size(image) | 尺寸 → (width, height) |
appui.CameraPicker(...) | AppUI 相机控件,回调文件路径 |
#拍照
capture_image(camera="back", show_preview=True, flash="auto", **kwargs)
已复制
image = photos.capture_image()
if image is None:
print("取消或失败")
camera、flash 等参数为 Pythonista 兼容入口,实际由系统相机界面控制。取消返回 None。
capture_image_base64(format="JPEG", quality=0.85) — 直接返回 Base64,适合传给 vision_helper。
#AppUI CameraPicker
| 参数 | 说明 |
|---|---|
source | camera(后置)/ front(前置) |
media_type | photo / video |
on_captured | 回调 (path: str),取消时可能为空 |
label | 自定义按钮内容 |
已复制
def on_selfie_captured(path):
print(path)
appui.CameraPicker(
source="front",
media_type="photo",
on_captured=on_selfie_captured,
label=appui.Label("自拍", system_image="camera"),
)
CameraPicker 把图片写到临时目录并返回路径;photos.capture_image() 返回内存图像对象。
#权限
已复制
import permission
entry = permission.status("camera") # 仅查询,不弹窗
permission.request("camera") 当前不会弹出授权框;直接调用 capture_image(),由系统在需要时提示。
#常见错误
| 错误写法 | 后果 | 修正 |
|---|---|---|
if permission.request("camera"): | 判断错误且暂不支持 | 直接 capture_image() 并判 None |
在 body() 里 capture_image() | 刷新时反复弹相机 | 放进按钮回调 |
不处理 None | 用户取消后崩溃 | if image is None: return |
录像用 capture_image() | 无法录视频 | 用 video_recorder 或 CameraPicker(media_type="video") |
#相关文档
| 文档 | 用途 |
|---|---|
| photos | 选图、相册读写、保存 |
| video_recorder | 摄像头录像 |
| vision_helper | Base64 检测 |
| permission | 相机权限状态查询 |
| AppUI 媒体参考 | CameraPicker 完整签名 |
#预期效果
运行示例后,界面应出现文档描述的目标结果;若与预期不符,先看「失败路径」并按返回值或日志排查。