- Qt 6.5
- Qt Multimedia
- QML Camera Application
- Qt 6.5.2 Reference Documentation

# QML Camera Application

This Qt Quick based application shows how to use the API to capture a still image or video.

This example demonstrates how to access camera functions via QML. It shows how to change settings and capture images or video.

#### Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

#### Application structure

Most of the QML code in this example supports the user interface. Custom types that support the requirements have been implemented using existing Qt Quick controls.

#### Using screen orientation to select layout

In `declarative-camera.qml` we handle detecting the orientation we're in and select the appropriate layout while also catering for the limitations of small screen mobile devices like so:

     Rectangle {
         id : cameraUI

         width: 800
         height: 480
         color: "black"
         state: "PhotoCapture"

         property string platformScreen: ""
         property int buttonsPanelLandscapeWidth: 328
         property int buttonsPanelPortraitHeight: 180

         onWidthChanged: {
             setState()
         }
         function setState() {
             if (Qt.platform.os === "android" || Qt.platform.os === "ios") {
                 if (Screen.desktopAvailableWidth < Screen.desktopAvailableHeight) {
                     stillControls.state = "MobilePortrait";
                 } else {
                     stillControls.state  = "MobileLandscape";
                 }
             } else {
                 stillControls.state = "Other";
             }
             console.log("State: " + stillControls.state);
             stillControls.buttonsWidth = (stillControls.state === "MobilePortrait")
                     ? Screen.desktopAvailableWidth/3.4 : 144
         }

Here is the portrait layout:

[Missing image qml-declarative-portrait.png]

You can see the `state` property is initially set as `PhotoCapture`.

Then the `states` themselves are defined like so:

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

#### Controls for capturing

Controls for capturing are implemented in `PhotoCaptureControls.qml` and VideoCaptureControls.qml. They each are based on a FocusScope that defines common buttons dimensions and margins that are used by the control buttons and then declares the buttons.

This generates a column on the right hand side of the screen which includes, listed top to bottom, the following controls:

- A `Capture` or `Record` button, which initiates capturing.
- A `capture properties` button that displays the icon of the current white balance mode selected and when pressed uses a pop-up to displays the following option's icons:
  - Flash mode (if available)
  - White balance modes
  - Exposure compensation
- A `View` button, once something has been captured.
- A button which displays the currently selected capture device and when pressed provides a list of available devices to switch to, using a pop-up.
- A `Switch To` button that displays the alternate capture mode (video or photo) depending on the current active selection and switches the mode when pressed.
- A `Quit` button, that exits the application.

#### Image capturing

The button that triggers this is defined in CameraButton.qml: but its interaction with the camera is in the controls types, lets look at PhotoCaptureControls:

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

#### Zoom control

Implemented in `ZoomControl.qml` the ZoomControl type is based on an Item and creates a bar that represents the zoom level, which can also be dragged. It uses an exponential calculation method to determine the zoom factor given the position of the `grove`.

The bar is only visible if the initialZoom is greater than 1. This means the currently active camera has a zoom function.

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

         visible: zoomControl.maximumZoom > 1

         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(1, 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-1.0) / (zoomControl.maximumZoom-1.0))
                 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

In PhotoCaptureControls.qml and VideoCaptureControls.qml the signal `zoomTo` will set the selected camera's zoomFactor property to the calculated `target` value, as well as updating the ZoomControl bar.

         ZoomControl {
             x : 10
             y : captureControls.state === "MobilePortrait" ? -buttonPaneShadow.height : 0
             width : 100
             height: 500

             currentZoom: captureSession.camera.zoomFactor
             maximumZoom: captureSession.camera.maximumZoomFactor
             onZoomTo: captureSession.camera.zoomFactor = target
         }

         states: [
             State {
                 name: "MobilePortrait"
                 PropertyChanges {
                     target: buttonPaneShadow
                     width: parent.width
                     height: buttonsPanelPortraitHeight
                 }
                 PropertyChanges {
                     target: buttonsColumn
                     height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
                 }
                 PropertyChanges {
                     target: bottomColumn
                     height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
                 }
                 AnchorChanges {
                     target: buttonPaneShadow
                     anchors.bottom: parent.bottom
                     anchors.left: parent.left
                     anchors.right: parent.right
                 }
                 AnchorChanges {
                     target: buttonsColumn
                     anchors.left: parent.left
                     anchors.right: parent.right
                     anchors.top: parent.top
                 }
                 AnchorChanges {
                     target: bottomColumn;
                     anchors.bottom: parent.bottom
                     anchors.left: parent.left
                     anchors.right: parent.right
                 }
             },
             State {
                 name: "MobileLandscape"
                 PropertyChanges {
                     target: buttonPaneShadow
                     width: buttonsPanelWidth
                     height: parent.height
                 }
                 PropertyChanges {
                     target: buttonsColumn
                     height: parent.height
                     width: buttonPaneShadow.width / 2
                 }
                 PropertyChanges {
                     target: bottomColumn
                     height: parent.height
                     width: buttonPaneShadow.width / 2
                 }
                 AnchorChanges {
                     target: buttonPaneShadow
                     anchors.top: parent.top
                     anchors.right: parent.right
                 }
                 AnchorChanges {
                     target: buttonsColumn;
                     anchors.top: parent.top
                     anchors.bottom: parent.bottom
                     anchors.left: parent.left;
                 }
                 AnchorChanges {
                     target: bottomColumn;
                     anchors.top: parent.top;
                     anchors.bottom: parent.bottom;
                     anchors.right: parent.right;
                 }
             },
             State {
                 name: "Other"
                 PropertyChanges {
                     target: buttonPaneShadow;
                     width: bottomColumn.width + 16;
                     height: parent.height;
                 }
                 AnchorChanges {
                     target: buttonPaneShadow;
                     anchors.top: parent.top;
                     anchors.right: parent.right;
                 }
                 AnchorChanges {
                     target: buttonsColumn;
                     anchors.top: parent.top
                     anchors.right: parent.right
                 }
                 AnchorChanges {
                     target: bottomColumn;
                     anchors.bottom: parent.bottom;
                     anchors.right: parent.right;
                 }
             }
         ]
     }

#### Flash and torch control

[Missing image FlashControls.png]

Defined in `FlashControl.qml` this enables flash mode selection and torch functionality to be toggled via a Switch. As with the zoom control, the switches are only visible on top of the preview window if the active device supports these functions.

Here we check if the functions are supported:

Here we implement the `flashModeControl` switch, which also directly controls the Camera device.

Torch control is implemented in a similar way.

Example project @ code.qt.io