Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtMultimedia
import QtQuick.Controls
import QtQuick.Layouts
ColumnLayout {
id: top
required property CaptureSession captureSession
readonly property ImageCaptureHelper imageCapture: captureSession.imageCapture
required property CameraHelper camera
required property MediaDevicesHelper mediaDevices
CheckBox {
text: "Camera attached to capture session"
checked: top.captureSession.camera
onClicked: () => {
if (top.captureSession.camera)
top.captureSession.camera = null;
else
top.captureSession.camera = top.camera;
}
}
CameraDeviceSelector {
camera: top.camera
mediaDevices: top.mediaDevices
}
RowLayout {
Label {
text: "Format"
}
Label {
text: "unavailable"
color: "red"
function checkFormatIsAvailable(videoInputs, cameraDevice, format): bool {
// First check if the device is available in QMediaDevices
let foundDevice = videoInputs.find((item) => String(item.id) === String(cameraDevice.id))
if (foundDevice === undefined)
return false
let foundFormat = foundDevice.videoFormats.find((item) =>
top.camera.cameraFormatToString(item) === top.camera.cameraFormatToString(format))
return foundFormat !== undefined
}
function calcOpacity(videoInputs, cameraDevice, format) {
let formatIsAvailable = checkFormatIsAvailable(videoInputs, cameraDevice, format)
if (formatIsAvailable)
return 0.1
else
return 1
}
opacity: calcOpacity(mediaDevices.videoInputs, top.camera.cameraDevice, top.camera.cameraFormat)
}
ComboBox {
implicitContentWidthPolicy: ComboBox.WidestText
model:
top.camera.cameraDevice.videoFormats
.slice()
.sort((a, b) => top.camera.customCameraFormatSortDelegate(a, b))
.map((item) => { return { value: item, text: top.camera.cameraFormatToString(item) } })
textRole: "text"
displayText: top.camera.cameraFormatToString(top.camera.cameraFormat)
currentIndex: model.findIndex(
(item) => item.text === top.camera.cameraFormatToString(top.camera.cameraFormat))
onActivated: index => {
top.camera.cameraFormat = model[index].value
}
}
Button {
text: "Default"
onClicked: top.camera.cameraFormat = undefined
}
}
CheckBox {
text: "Active"
checked: top.camera.active
onToggled: () => {
top.camera.active = checked
checked = Qt.binding(() => top.camera.active)
}
}
ColumnLayout {
enabled: top.camera.minimumZoomFactor < top.camera.maximumZoomFactor
RowLayout {
Label {
text: "Zoom: " + top.camera.zoomFactor.toFixed(1)
font.bold: true
}
Label { text: "min: " + top.camera.minimumZoomFactor.toFixed(1) + " max: " + top.camera.maximumZoomFactor.toFixed(1) }
}
Slider {
Layout.fillWidth: true
from: top.camera.minimumZoomFactor
to: Math.min(top.camera.maximumZoomFactor, 10)
value: top.camera.zoomFactor
onMoved: top.camera.zoomFactor = value
}
Button {
text: "Ramp zoom slowly to max"
onClicked: top.camera.zoomTo(Math.min(top.camera.maximumZoomFactor,
4), 0.1)
}
}
RowLayout {
enabled: top.camera.supportedFocusModes.length > 1
Label {
text: "Focus mode"
font.bold: true
}
ComboBox {
model: top.camera.supportedFocusModes
.map(item => {
return {
"value": item,
"text": top.camera.focusModeToString(item)
}
})
valueRole: "value"
textRole: "text"
displayText: top.camera.focusModeToString(top.camera.focusMode)
currentIndex: model.findIndex(
item => item.value === top.camera.focusMode)
onActivated: index => {
if (top.camera.isFocusModeSupported(currentValue))
top.camera.focusMode = currentValue
else
console.log("Selected unsupported focus mode")
}
}
} // Focus mode
RowLayout {
// Flash mode
enabled: top.camera.supportedFlashModes.length > 1
Label {
text: "Flash mode"
font.bold: true
}
ComboBox {
Layout.alignment: Qt.AlignRight
model: top.camera.supportedFlashModes
.map(item => {
return {
"value": item,
"text": top.camera.flashModeToString(item) } })
textRole: "text"
valueRole: "value"
currentIndex: model.findIndex(item => item.value === top.camera.flashMode)
onActivated: index => {
if (top.camera.isFlashModeSupported(currentValue))
top.camera.flashMode = currentValue
else
console.log("Selected unsupported flash mode")
}
}
} // Flash mode
RowLayout {
enabled: top.camera.supportedTorchModes.length > 1
Label {
text: "Torch mode"
font.bold: true
}
ComboBox {
Layout.alignment: Qt.AlignRight
model:
top.camera.supportedTorchModes
.map(item => { return { "value": item, "text": top.camera.torchModeToString(item) } })
textRole: "text"
valueRole: "value"
onActivated: index => {
if (top.camera.isTorchModeSupported(currentValue))
top.camera.torchMode = currentValue
else
console.log("Selected unsupported torch mode")
}
}
}
ColumnLayout {
enabled: top.camera.supportedFocusModes.includes(Camera.FocusModeManual)
&& top.camera.supportedFeatures & Camera.FocusDistance
Label {
text: "focusDistance " + top.camera.focusDistance.toFixed(1)
}
Slider {
Layout.fillWidth: true
from: 0
to: 1
value: top.camera.focusDistance
onMoved: top.camera.focusDistance = value
}
}
RowLayout {
Label {
text: "Image file format"
}
ComboBox {
implicitContentWidthPolicy: ComboBox.WidestText
model: imageCapture.supportedFormats
.map((item) => { return { displayText: imageCapture.fileFormatToString(item), value: item } })
valueRole: "value"
textRole: "displayText"
currentIndex: model.findIndex(
item => item.value === imageCapture.fileFormat)
displayText: imageCapture.fileFormatToString(imageCapture.fileFormat)
onActivated: index => {
imageCapture.fileFormat = currentValue
currentIndex = model.findIndex(
item => item.value === imageCapture.fileFormat)
}
}
Button {
text: "Default"
onClicked: imageCapture.fileFormat = undefined
}
}
RowLayout {
Label {
text: "Error: "
+ top.camera.errorEnumToString(top.camera.error)
+ " ("
+ top.camera.error
+ ")"
}
}
RowLayout {
Label {
text: "Error string: '" + top.camera.errorString + "'"
}
}
}