QML 相机应用程序

这个基于Qt Quick 的应用程序展示了如何使用 API 捕捉静态图像或视频。

该示例演示了如何通过 QML 访问相机功能。它展示了如何更改设置和捕捉图像或视频。

运行示例

要从 Qt Creator,打开Welcome 模式,并从Examples 选择示例。更多信息,请参阅Qt Creator: Tutorial:构建和运行

应用程序结构

本示例中的大部分 QML 代码都支持用户界面。使用现有的Qt Quick 控件实现了支持需求的自定义类型。

使用屏幕方向选择布局

方向和控件布局状态逻辑封装在一个单独的项目中,controlLayout

    Item {
        id: controlLayout

        readonly property bool isMobile: Qt.platform.os === "android" || Qt.platform.os === "ios"
        readonly property bool isLandscape: Screen.desktopAvailableWidth >= Screen.desktopAvailableHeight
        property int buttonsWidth: state === "MobilePortrait" ? Screen.desktopAvailableWidth / 3.4 : 114

        states: [
            State {
                name: "MobileLandscape"
                when: controlLayout.isMobile && controlLayout.isLandscape
            },
            State {
                name: "MobilePortrait"
                when: controlLayout.isMobile && !controlLayout.isLandscape
            },
            State {
                name: "Other"
                when: !controlLayout.isMobile
            }
        ]

        onStateChanged: {
            console.log("State: " + controlLayout.state)
        }
    }

stillControlsvideoControls 对象都绑定到该 Item 的statebuttonsWidth 属性,如stillControls 所示:

    PhotoCaptureControls {
        id: stillControls
        state: controlLayout.state
        anchors.fill: parent
        buttonsWidth: controlLayout.buttonsWidth
        buttonsPanelPortraitHeight: cameraUI.buttonsPanelPortraitHeight
        buttonsPanelWidth: cameraUI.buttonsPanelLandscapeWidth
        captureSession: captureSession
        visible: (cameraUI.state === "PhotoCapture")
        onPreviewSelected: cameraUI.state = "PhotoPreview"
        onVideoModeSelected: cameraUI.state = "VideoCapture"
        previewAvailable: imageCapture.preview.length !== 0
    }

为了支持调试,布局状态变化的信息会被记录下来。

下面是肖像布局:

可以看到state 属性最初设置为PhotoCapture

然后像这样定义states 本身:

    states: [
        State {
            name: "PhotoCapture"
            StateChangeScript {
                script: {
                    camera.start()
                }
            }
        },
        State {
            name: "PhotoPreview"
        },
        State {
            name: "VideoCapture"
            StateChangeScript {
                script: {
                    camera.start()
                }
            }
        },
        State {
            name: "VideoPreview"
            StateChangeScript {
                script: {
                    camera.stop()
                }
            }
        }
    ]

捕捉控件

用于捕捉的控件在PhotoCaptureControls.qml 和 VideoCaptureControls.qml 中实现。它们都基于一个FocusScope ,该 定义了控件按钮使用的通用按钮尺寸和边距,然后声明了按钮。

这将在屏幕右侧生成一列,从上到下包括以下控件:

  • CaptureRecord 按钮,用于启动捕捉。
  • capture properties 按钮,显示当前所选白平衡模式的图标,按下后会弹出一个窗口,显示以下选项的图标:
    • 闪光灯模式(如有)
    • 白平衡模式
    • 曝光补偿
  • View 按钮,一旦拍摄完成。
  • 一个按钮,显示当前选择的拍摄设备,按下后会弹出可用设备的切换列表。
  • Switch To 按钮,根据当前活动选择显示备用捕捉模式(视频或照片),按下后可切换模式。
  • Quit 按钮,用于退出应用程序。

图像捕捉

触发此操作的按钮在 CameraButton.qml 中定义:但它与摄像头的交互在控件类型中,让我们看看 PhotoCaptureControls:

            CameraButton {
                text: "Capture"
                implicitWidth: captureControls.buttonsWidth
                visible: captureControls.captureSession.imageCapture.readyForCapture
                onClicked: captureControls.captureSession.imageCapture.captureToFile("")
            }

缩放控件

ZoomControl.qml 中实现的 ZoomControl 类型基于 Item,并创建了一个表示缩放级别的条形图,该条形图还可以拖动。它使用指数计算方法来确定给定grove 位置的缩放系数。

只有当 initialZoom 大于 1 时,条形图才可见,这意味着当前活动相机具有变焦功能。

Item {
    id : zoomControl
    property real currentZoom : 1
    property real maximumZoom : 1
    property real minimumZoom : 1
    signal zoomTo(real target)

    visible: zoomControl.maximumZoom > zoomControl.minimumZoom

    MouseArea {
        id : mouseArea
        anchors.fill: parent

        property real initialZoom : 0
        property real initialPos : 0

        onPressed: {
            initialPos = mouseY
            initialZoom = zoomControl.currentZoom
        }

        onPositionChanged: {
            if (pressed) {
                var target = initialZoom * Math.pow(5, (initialPos-mouseY)/zoomControl.height);
                target = Math.max(zoomControl.minimumZoom, Math.min(target, zoomControl.maximumZoom))
                zoomControl.zoomTo(target)
            }
        }
    }

    Item {
        id : bar
        x : 16
        y : parent.height/4
        width : 24
        height : parent.height/2

        Rectangle {
            anchors.fill: parent

            smooth: true
            radius: 8
            border.color: "white"
            border.width: 2
            color: "black"
            opacity: 0.3
        }

        Rectangle {
            id: groove
            x : 0
            y : parent.height * (1.0 - (zoomControl.currentZoom-zoomControl.minimumZoom) / (zoomControl.maximumZoom-zoomControl.minimumZoom))
            width: parent.width
            height: parent.height - y
            smooth: true
            radius: 8
            color: "white"
            opacity: 0.5
        }

        Text {
            id: zoomText
            anchors {
                left: bar.right; leftMargin: 16
            }
            y: Math.min(parent.height - height, Math.max(0, groove.y - height / 2))
            text: "x" + Math.round(zoomControl.currentZoom * 100) / 100
            font.bold: true
            color: "white"
            style: Text.Raised; styleColor: "black"
            opacity: 0.85
            font.pixelSize: 18

在 PhotoCaptureControls.qml 和 VideoCaptureControls.qml 中,信号zoomTo 会将所选摄像机的zoomFactor 属性设置为计算出的target 值,同时更新 ZoomControl 栏。

    ZoomControl {
        x : 0
        y : 0
        width : 100
        height: parent.height - (flashControl.visible * flashControl.height) -
                (captureControls.state === "MobilePortrait" ? buttonPaneShadow.height : 0)

        currentZoom: captureControls.captureSession.camera.zoomFactor
        maximumZoom: captureControls.captureSession.camera.maximumZoomFactor
        minimumZoom: captureControls.captureSession.camera.minimumZoomFactor
        onZoomTo: (target) => captureControls.captureSession.camera.zoomFactor = target
    }

闪光灯和电筒控制

该功能在FlashControl.qml 中定义,可通过开关切换闪光灯模式选择和电筒功能。与缩放控制一样,只有当活动设备支持这些功能时,预览窗口顶部才会显示开关。

在此,我们将检查是否支持这些功能:

    property Camera cameraDevice
    property bool mIsFlashSupported: (cameraDevice && cameraDevice.active) ? cameraDevice.isFlashModeSupported(Camera.FlashOn) : false
    property bool mIsTorchSupported: (cameraDevice && cameraDevice.active) ? cameraDevice.isTorchModeSupported(Camera.TorchOn) : false

在这里,我们实现了flashModeControl 开关,它也可以直接控制相机设备。

        Switch {
            id: flashModeControl
            visible: flashControl.mIsFlashSupported
            opacity: checked ? 0.75 : 0.25
            text: "Flash"
            contentItem: Text {
                text: flashModeControl.text
                color: "white"
                leftPadding: flashModeControl.indicator.width + flashModeControl.spacing
            }

            onPositionChanged: {
                if (position) {
                    if (torchModeControl.checked)
                        torchModeControl.toggle();
                    flashControl.cameraDevice.flashMode = Camera.FlashOn

                } else {
                    flashControl.cameraDevice.flashMode = Camera.FlashOff
                }
            }
        }

火炬控制的实现方式与此类似。

示例项目 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.