Skip to content

Camera

Usage

Cameras attached to a device running an app can be accessed using the camera attribute. This attribute exposes an API that allows you to check if you have have permission to access the camera device; and if permission exists, capture photographs.

The Camera API is asynchronous. This means the methods that have long-running behavior (such as requesting permissions and taking photographs) must be await-ed, rather than being invoked directly. This means they must be invoked from inside an asynchronous handler:

import toga

class MyApp(toga.App):
    ...
    async def time_for_a_selfie(self, widget, **kwargs):
        photo = await self.camera.take_photo()

Most platforms will require some form of device permission to access the camera. The permission APIs are paired with the specific actions performed on those APIs - that is, to take a photo, you require Camera.has_permission, which you can request using Camera.request_permission().

Toga will confirm whether the app has been granted permission to use the camera before invoking any camera API. If permission has not yet been granted, the platform may request access at the time of first camera access; however, this is not guaranteed to be the behavior on all platforms.

Notes

  • Apps that use a camera must be configured to provide permission to the camera device. The permissions required are platform specific:
    • iOS: NSCameraUsageDescription must be defined in the app's Info.plist file.
    • macOS: The com.apple.security.device.camera entitlement must be enabled, and NSCameraUsageDescription must be defined in the app's Info.plist file.
    • Android: The android.permission.CAMERA permission must be declared.
  • The iOS simulator implements the iOS Camera APIs, but is not able to take photographs. To test your app's Camera usage, you must use a physical iOS device.

Reference

Source code in core/src/toga/hardware/camera.py
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class Camera:
    def __init__(self, app: App):
        self.factory = get_factory()
        self._app = app
        self._impl = self.factory.Camera(self)

    @property
    def app(self) -> App:
        """The app with which the camera is associated"""
        return self._app

    @property
    def has_permission(self) -> bool:
        """Does the user have permission to use camera devices?

        If the platform requires the user to explicitly confirm permission, and
        the user has not yet given permission, this will return `False`.
        """
        return self._impl.has_permission()

    def request_permission(self) -> PermissionResult:
        """Request sufficient permissions to capture photos.

        If permission has already been granted, this will return without prompting the
        user.

        **This is an asynchronous method**. If you invoke this method in synchronous
        context, it will start the process of requesting permissions, but will return
        *immediately*. The return value can be awaited in an asynchronous context, but
        cannot be used directly.

        :returns: An asynchronous result; when awaited, returns True if the app has
            permission to take a photo; False otherwise.
        """
        result = PermissionResult(None)

        if has_permission := self.has_permission:
            result.set_result(has_permission)
        else:
            self._impl.request_permission(result)

        return result

    @property
    def devices(self) -> list[CameraDevice]:
        """The list of available camera devices."""
        return [CameraDevice(impl) for impl in self._impl.get_devices()]

    def take_photo(
        self,
        device: CameraDevice | None = None,
        flash: FlashMode = FlashMode.AUTO,
    ) -> PhotoResult:
        """Capture a photo using one of the device's cameras.

        If the platform requires permission to access the camera, and the user hasn't
        previously provided that permission, this will cause permission to be requested.

        **This is an asynchronous method**. If you invoke this method in synchronous
        context, it will start the process of taking a photo, but will return
        *immediately*. The return value can be awaited in an asynchronous context, but
        cannot be used directly.

        :param device: The initial camera device to use. If a device is *not* specified,
            a default camera will be used. Depending on the hardware available, the user
            may be able to change the camera used to capture the image at runtime.
        :param flash: The initial flash mode to use; defaults to "auto". Depending on
            the hardware available, this may be modified by the user at runtime.
        :returns: An asynchronous result; when awaited, returns the [`toga.Image`][]
            captured by the camera, or `None` if the photo was  cancelled.
        :raises PermissionError: if the app does not have permission to use the camera.
        """
        photo = PhotoResult(None)
        self._impl.take_photo(photo, device=device, flash=flash)
        return photo

app property

The app with which the camera is associated

devices property

The list of available camera devices.

has_permission property

Does the user have permission to use camera devices?

If the platform requires the user to explicitly confirm permission, and the user has not yet given permission, this will return False.

request_permission()

Request sufficient permissions to capture photos.

If permission has already been granted, this will return without prompting the user.

This is an asynchronous method. If you invoke this method in synchronous context, it will start the process of requesting permissions, but will return immediately. The return value can be awaited in an asynchronous context, but cannot be used directly.

:returns: An asynchronous result; when awaited, returns True if the app has permission to take a photo; False otherwise.

Source code in core/src/toga/hardware/camera.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def request_permission(self) -> PermissionResult:
    """Request sufficient permissions to capture photos.

    If permission has already been granted, this will return without prompting the
    user.

    **This is an asynchronous method**. If you invoke this method in synchronous
    context, it will start the process of requesting permissions, but will return
    *immediately*. The return value can be awaited in an asynchronous context, but
    cannot be used directly.

    :returns: An asynchronous result; when awaited, returns True if the app has
        permission to take a photo; False otherwise.
    """
    result = PermissionResult(None)

    if has_permission := self.has_permission:
        result.set_result(has_permission)
    else:
        self._impl.request_permission(result)

    return result

take_photo(device=None, flash=FlashMode.AUTO)

Capture a photo using one of the device's cameras.

If the platform requires permission to access the camera, and the user hasn't previously provided that permission, this will cause permission to be requested.

This is an asynchronous method. If you invoke this method in synchronous context, it will start the process of taking a photo, but will return immediately. The return value can be awaited in an asynchronous context, but cannot be used directly.

:param device: The initial camera device to use. If a device is not specified, a default camera will be used. Depending on the hardware available, the user may be able to change the camera used to capture the image at runtime. :param flash: The initial flash mode to use; defaults to "auto". Depending on the hardware available, this may be modified by the user at runtime. :returns: An asynchronous result; when awaited, returns the toga.Image captured by the camera, or None if the photo was cancelled. :raises PermissionError: if the app does not have permission to use the camera.

Source code in core/src/toga/hardware/camera.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def take_photo(
    self,
    device: CameraDevice | None = None,
    flash: FlashMode = FlashMode.AUTO,
) -> PhotoResult:
    """Capture a photo using one of the device's cameras.

    If the platform requires permission to access the camera, and the user hasn't
    previously provided that permission, this will cause permission to be requested.

    **This is an asynchronous method**. If you invoke this method in synchronous
    context, it will start the process of taking a photo, but will return
    *immediately*. The return value can be awaited in an asynchronous context, but
    cannot be used directly.

    :param device: The initial camera device to use. If a device is *not* specified,
        a default camera will be used. Depending on the hardware available, the user
        may be able to change the camera used to capture the image at runtime.
    :param flash: The initial flash mode to use; defaults to "auto". Depending on
        the hardware available, this may be modified by the user at runtime.
    :returns: An asynchronous result; when awaited, returns the [`toga.Image`][]
        captured by the camera, or `None` if the photo was  cancelled.
    :raises PermissionError: if the app does not have permission to use the camera.
    """
    photo = PhotoResult(None)
    self._impl.take_photo(photo, device=device, flash=flash)
    return photo
Source code in core/src/toga/hardware/camera.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class CameraDevice:
    def __init__(self, impl: Any):
        self._impl = impl

    @property
    def id(self) -> str:
        """A unique identifier for the device"""
        return self._impl.id()

    @property
    def name(self) -> str:
        """A human-readable name for the device"""
        return self._impl.name()

    @property
    def has_flash(self) -> bool:
        """Does the device have a flash?"""
        return self._impl.has_flash()

    def __eq__(self, other: Widget) -> bool:
        return self.id == other.id

    def __repr__(self) -> str:
        return f"<CameraDevice id={self.id} {self.name!r}>"

    def __str__(self) -> str:
        return self.name

has_flash property

Does the device have a flash?

id property

A unique identifier for the device

name property

A human-readable name for the device