捕捉图像(Capturing Images)

Camera元素一个关键特性就是可以用来拍照。我们将在一个简单的定格动画程序中使用到它。在这章中,你将学习如何显示一个视图查找器,截图和追踪拍摄的图片。

用户界面如下所示。它由三部分组成,背景是一个视图查找器,右边有一列按钮,底部有一连串拍摄的图片。我们想要拍摄一系列的图片,然后点击Play Sequence按钮。这将回放图片,并创建一个简单的定格电影。

相机的视图查找器部分是在VideoOutput中使用一个简单的Camera元素作为资源。这将给用户显示一个来自相机的流媒体视频。

    VideoOutput {
        anchors.fill: parent
        source: camera
    }

    Camera {
        id: camera
    }

使用一个水平放置的ListView显示来自ListModel的图片,这个部件叫做imagePaths。在背景中使用一个半透明的Rectangle。

    ListModel {
        id: imagePaths
    }

    ListView {
        id: listView

        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 10

        height: 100

        orientation: ListView.Horizontal
        spacing: 10

        model: imagePaths

        delegate: Image { source: path; fillMode: Image.PreserveAspectFit; height: 100; }

        Rectangle {
            anchors.fill: parent
            anchors.topMargin: -10

            color: "black"
            opacity: 0.5
        }
    }

为了拍摄图像,你需要知道Camera元素包含了一组子对象用来完成各种工作。使用Camera.imageCapture用来捕捉图像。当你调用capture方法时,一张图片就被拍摄下来了。Camera.imageCapture的结果将会发送imageCaptured信号,接着发送imageSaved信号。

        Button {
            id: shotButton

            width: 200
            height: 75

            text: "Take Photo"
            onClicked: {
                camera.imageCapture.capture();
            }
        }

为了拦截子元素的信号,需要一个Connections元素。在这个例子中,我们不需要显示预览图片,仅仅只是将结果图片加入底部的ListView中。就如下面的例子展示的一样,图片保存的路径由信号的path参数提供。

    Connections {
        target: camera.imageCapture

        onImageSaved: {
            imagePaths.append({"path": path})
            listView.positionViewAtEnd();
        }
    }

为了显示预览,连接imageCaptured信号,并且使用preview信号参数作为Image元素的source。requestId信号参数与imageCaptured和imageSaved一起发送。这个值由capture方法返回。这样,就可以完整的跟踪拍摄的图片了。预览的图片首先被使用,然后替换为保存的图片。然而在这个例子中我们不需要这样做。

最后是自动回放的部分。使用Timer元素来驱动它,并且加上一些JavaScript。_imageIndex变量被用来跟踪当前显示的图片。当最后一张图片被显示时,回放停止。在例子中,当播放序列时,root.state被用来隐藏用户界面。

    property int _imageIndex: -1

    function startPlayback()
    {
        root.state = "playing";
        setImageIndex(0);
        playTimer.start();
    }

    function setImageIndex(i)
    {
        _imageIndex = i;

        if (_imageIndex >= 0 && _imageIndex < imagePaths.count)
            image.source = imagePaths.get(_imageIndex).path;
        else
            image.source = "";
    }

    Timer {
        id: playTimer

        interval: 200
        repeat: false

        onTriggered: {
            if (_imageIndex + 1 < imagePaths.count)
            {
                setImageIndex(_imageIndex + 1);
                playTimer.start();
            }
            else
            {
                setImageIndex(-1);
                root.state = "";
            }
        }
    }