跳转至

使用相机

几乎每一台现代计算机都配备某种类型的摄像头。在本教程中,我们将编写一个新程序,使该程序能够请求访问摄像头、拍摄照片,然后在这个新的应用程序当中显示所拍摄的照片。

本教程并不适用于所有平台!

遗憾的是,目前本教程仅适用于 macOS 系统和 Android。

虽然所有 iPhone 都有摄像头,iOS 虚拟机\ 没有正常工作的摄像头。Windows 与 Linux 设备也有摄像头,但 Toga 目前不提供在这些平台上访问摄像头的能力。

这里展示的代码可以在 Windows 或 Linux 上运行,但是当您尝试照相时,它会报错。

这段代码在 iOS 设备上会正常工作,但部署到 iOS 虚拟器时,它将无法正常拍照。

启动新项目

在本教程中,我们不会在核心教程中的应用程序上进行构建,而是要启动一个全新的项目。您可以使用与第一个项目相同的虚拟环境;但我们需要重新运行 "新建公文包 "向导。

返回包含 helloworld 项目文件夹的目录,并启动名为 "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

添加拍摄照片的代码

向导已经生成了一个新的空 Toga 项目。现在我们可以添加代码来拍摄并显示照片。编辑新应用程序的 app.py,使其包含以下内容:

import toga
from toga.style.pack import COLUMN, ROW


class HelloCamera(toga.App):
    def startup(self):
        main_box = toga.Box()

        self.photo = toga.ImageView(height=300, margin=5)
        camera_button = toga.Button(
            "Take photo",
            on_press=self.take_photo,
            margin=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()

与 Briefcase 生成的默认应用程序相比,这段代码有两处改动。上面突出显示了这些新增内容:

  1. 第一个突出显示的代码块(在 startup() 方法中)添加了控制摄像头所需的两个部件:显示照片的 ImageView 和触发摄像头的 Button
  2. 第二个突出显示的代码块("take_photo() "方法)定义了按下按钮时的事件处理程序。该处理程序首先会确认应用程序是否有拍照权限;如果没有权限,则会请求权限。然后拍摄照片。请求权限和请求拍照都是异步请求,因此需要使用 await;当应用程序等待用户确认权限或拍照时,应用程序的事件循环可以在后台继续。

如果相机成功拍摄了照片,它将返回一个 Image 对象,该对象可被分配为 ImageView 的内容。如果用户取消了拍照请求,self.camera.take_photo() 调用将返回None,结果可以忽略。如果用户未授予使用相机的权限,或当前平台未实现相机,则会引发错误,并向用户显示对话框。

添加设备权限

我们刚刚添加的代码中有一部分要求获得使用摄像头的权限。这是现代应用程序平台的一个共同特点–如果不事先明确征求用户许可,就无法访问硬件功能。

不过,这种请求分为两部分。第一部分在我们刚刚看到的代码中;但在应用程序请求权限之前,它需要声明要请求的权限。

每个平台所需的权限略有不同,但 Briefcase 为许多常见硬件权限提供了跨平台表示。在应用程序的 pyproject.toml 文件的 [tool.briefcase.app.hellocamera]配置部分,添加以下内容(就在 sources 声明的上方):

permission.camera = "App will take mugshots."

这将声明您的应用程序需要访问摄像头,并提供一个简短的说明,说明为什么需要使用摄像头。某些平台(尤其是 macOS 和 iOS)需要此说明,并将在权限对话框显示时作为附加信息显示给用户。

我们现在可以生成并运行应用程序:

(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

程序运行时,会出现一个按钮。按下按钮后,平台的默认相机对话框就会显示。拍照后,相机对话框将消失,照片将显示在应用程序中,就在按钮上方。然后,您可以再拍一张照片;这张照片将取代第一张照片。

添加设备权限

权限声明在最初调用 briefcase create 时生成的文件中。不幸的是,Briefcase 无法在最初生成这些文件后对其进行更新;因此,如果您想在应用程序中添加新权限或修改现有权限,则需要重新创建应用程序。您可以通过重新运行 briefcase create 来做到这一点;这将警告您现有的应用程序将被覆盖,然后用新的权限重新生成应用程序。