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.
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”:
(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:
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:
The first highlighted code block (in the
startup()
method) adds the two widgets needed to control the camera: anImageView
to display a photo; and aButton
to trigger the camera.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 ofawait
; 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):
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:
(beeware-venv)$ briefcase create
(beeware-venv)$ briefcase build
(beeware-venv)$ briefcase run
(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.