================ Using the camera ================ Almost every modern computing device has a camera of some sort. In this tutorial, we'll write new application that is able to request access to this camera, take a photograph, and then display that photograph in the app. new application that uses your device's camera. .. admonition:: This tutorial won't work on all platforms! Unfortunately, at present, this tutorial will only work on macOS and Android. Although iPhones all have cameras, the iOS *Simulator* doesn't have a working camera. Windows and Linux devices also have cameras, but Toga doesn't currently have the ability to access the camera on these platforms. The code presented here will *run* on Windows or Linux; but it will raise an error when you try to take a photograph. The code will work if it is run on an actual iOS device, but will fail to take a photograph if deployed to the iOS simulator. Start a new project =================== For this tutorial, we're not going to build onto the application from the core tutorial - we're going to start a fresh project. You can use the same virtual environment you used in the first project; but we need to re-run the ``briefcase new`` wizard. Change back to the directory that contains the ``helloworld`` project folder, and start a new project named "Hello Camera": .. code-block:: console (beeware-venv) $ cd .. (beeware-venv) $ briefcase new ... [hellocamera] Generated new application 'Hello Camera' To run your application, type: $ cd hellocamera $ briefcase dev (beeware-venv) $ cd hellocamera Add code to take a photo ======================== The wizard has generated a new empty Toga project. We can now add the code to take and display a photograph. Edit the ``app.py`` for the new application so that it has the following content: .. code-block:: python :emphasize-lines: 10-18, 24-41 import toga from toga.style import Pack from toga.style.pack import COLUMN, ROW class HelloCamera(toga.App): def startup(self): main_box = toga.Box() self.photo = toga.ImageView(style=Pack(height=300, padding=5)) camera_button = toga.Button( "Take photo", on_press=self.take_photo, style=Pack(padding=5) ) main_box.add(self.photo) main_box.add(camera_button) self.main_window = toga.MainWindow(title=self.formal_name) self.main_window.content = main_box self.main_window.show() async def take_photo(self, widget, **kwargs): try: if not self.camera.has_permission: await self.camera.request_permission() image = await self.camera.take_photo() if image: self.photo.image = image except NotImplementedError: await self.main_window.dialog( toga.InfoDialog( "Oh no!", "The Camera API is not implemented on this platform", ) ) except PermissionError: await self.main_window.dialog( toga.InfoDialog( "Oh no!", "You have not granted permission to take photos", ) ) def main(): return HelloCamera() This code has two changes over the default app that is generated by Briefcase. These additions are highlighted in yellow: 1. The first highlighted code block (in the ``startup()`` method) adds the two widgets needed to control the camera: an ``ImageView`` to display a photo; and a ``Button`` to trigger the camera. 2. The second highlighted code block (the ``take_photo()`` method) defines the event handler when the button is pressed. This handler first confirms if the application has permission to take a photo; if permission doesn't exist, it is requested. Then, a photo is taken. The request for permission and the request to take a photo are both asynchronous requests, so they require the use of ``await``; while the app is waiting for the user to confirm permissions or take the photo, the app's event loop can continue in the background. If the camera successfully takes a photo, it will return an ``Image`` object that can be assigned as the content of the ``ImageView``. If the photo request was canceled by the user, the ``self.camera.take_photo()`` call will return ``None``, and the result can be ignored. If the user doesn't grant permission to use the camera, or the camera isn't implemented on the current platform, an error will be raised, and a dialog will be shown to the user. Adding device permissions ========================= Part of this code we've just added asks for permission to use the camera. This is a common feature of modern app platforms - you can't access hardware features without explicitly asking the user's permission first. However, this request comes in two parts. The first is in the code we've just seen; but before the app can ask for permissions, it needs to declare the permissions it is going to ask for. The permissions required by each platform are slightly different, but Briefcase has a cross-platform representation for many common hardware permissions. In the ``[tool.briefcase.app.helloworld]`` configuration section of your app's ``pyproject.toml`` file, add the following (just above the ``sources`` declaration): .. code-block:: toml permission.camera = "App will take mugshots." This declares that your app needs to access the camera, and provides a short description why the camera is required. This description is needed on some platforms (most notably macOS and iOS) and will be displayed to the user as a additional information when the permission dialog is presented. We can now generate and run the app: .. tabs:: .. group-tab:: macOS .. code-block:: console (beeware-venv)$ briefcase create (beeware-venv)$ briefcase build (beeware-venv)$ briefcase run .. group-tab:: Android .. code-block:: console (beeware-venv)$ briefcase create android (beeware-venv)$ briefcase build android (beeware-venv)$ briefcase run android When the app runs, you'll be presented with a button. Press the button, and the platform's default camera dialog will be displayed. Take a photo; the camera dialog will disappear, and the photo will be displayed on in the app, just above the button. You could then take another photo; this will replace the first photo. Adding more permissions ======================= Permissions are declared in the files that are generated during the original call to ``briefcase create``. Unfortunately, Briefcase can't update these files once they've been initially generated; so if you want to add a new permission to your app, or modify existing permissions, you'll need to re-create the app. You can do this by re-running ``briefcase create``; this will warn you that the existing app will be overwritten, and then regenerate the application with the new permissions.