QMLカメラアプリケーション

このQt Quickベースのアプリケーションでは、APIを使って静止画や動画をキャプチャする方法を紹介します。

この例では、QML経由でカメラの機能にアクセスする方法を示します。設定を変更し、画像や動画をキャプチャする方法を示します。

例の実行

Qt Creator からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Example を参照してください。

アプリケーションの構造

このサンプルに含まれるQMLコードのほとんどは、ユーザーインターフェースをサポートしています。要件をサポートするカスタムタイプは、既存の Qt Quick コントロールを使用して実装されています。

画面の向きを使ってレイアウトを選択する

オリエンテーションとコントロールレイアウトのステートロジックは、controlLayout のような別のItemにカプセル化されています:

    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)
        }
    }

stillControls に示すように、stillControlsvideoControls オブジェクトは、どちらもこのItemのstatebuttonsWidth プロパティにバインドされます:

    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 、コントロール・ボタンが使用する共通のボタンの寸法とマージンを定義し、ボタンを宣言する。

これにより、画面の右側に列が生成され、その列には、上から下へ、次のコントロールが含まれます:

  • キャプチャを開始するCapture またはRecord ボタン。
  • 現在選択されているホワイトバランスモードのアイコンを表示し、押されるとポップアップを使用して次のオプションのアイコンを表示するcapture properties ボタン:
    • フラッシュモード(使用可能な場合)
    • ホワイトバランスモード
    • 露出補正
  • View 、撮影が完了すると表示されます。
  • 現在選択されているキャプチャデバイスを表示し、押すとポップアップで切り替え可能なデバイスのリストを表示するボタン。
  • Switch To 、現在アクティブな選択に応じて代替キャプチャモード(ビデオまたは写真)を表示し、押すとモードが切り替わります。
  • Quit 、アプリケーションを終了します。

画像キャプチャ

このトリガーとなるボタンはCameraButton.qmlで定義されていますが、カメラとのインタラクションはコントロールの種類にあります:

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

ズームコントロール

ZoomControl.qml で実装されているZoomControlタイプはItemをベースにしており、ズームレベルを表すバーを作成します。また、ドラッグすることもできます。grove の位置を指定すると、指数計算メソッドを使用してズーム倍率を決定します。

これは、現在アクティブなカメラにズーム機能があることを意味します。

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 は、ZoomControl バーを更新するだけでなく、選択されたカメラのzoomFactor プロパティを計算されたtarget 値に設定します。

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

        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

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。