QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PhotoVideoControl.qml
Go to the documentation of this file.
1import QtQuick
2import QtPositioning
3import QtQuick.Layouts
4import QtQuick.Controls
5import QtQuick.Dialogs
6
7import QGroundControl
8import QGroundControl.Controls
9import QGroundControl.FactControls
10
11Rectangle {
12 id: photoVideoControl
13 width: mainLayout.width + (_smallMargins * 2)
14 height: mainLayout.height + (_smallMargins * 2)
15 color: Qt.rgba(qgcPal.window.r, qgcPal.window.g, qgcPal.window.b, 0.5)
16 radius: _margins
17 visible: _camera.capturesVideo || _camera.capturesPhotos || _camera.hasTracking || _camera.hasVideoStream
18
19 property real _margins: ScreenTools.defaultFontPixelHeight / 2
20 property real _smallMargins: ScreenTools.defaultFontPixelWidth / 2
21 property var _activeVehicle: globals.activeVehicle
22 property var _cameraManager: _activeVehicle.cameraManager
23 property var _camera: _cameraManager.currentCameraInstance
24 property bool _cameraInPhotoMode: _camera.cameraMode === MavlinkCameraControlInterface.CAM_MODE_PHOTO || _camera.cameraMode === MavlinkCameraControlInterface.CAM_MODE_SURVEY
25 property bool _cameraInVideoMode: !_cameraInPhotoMode
26 property bool _videoCaptureIdle: _camera.captureVideoState === MavlinkCameraControlInterface.CaptureVideoStateIdle
27 property bool _photoCaptureIdle: _camera.capturePhotosState === MavlinkCameraControlInterface.CapturePhotosStateIdle
28
29 QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
30
31 DeadMouseArea { anchors.fill: parent }
32
33 RowLayout {
34 id: mainLayout
35 anchors.margins: _smallMargins
36 anchors.top: parent.top
37 anchors.left: parent.left
38 spacing: _margins
39
40 ColumnLayout {
41 Layout.fillHeight: true
42 spacing: 0
43 visible: _camera.hasZoom
44
45 QGCLabel {
46 Layout.alignment: Qt.AlignHCenter
47 text: qsTr("Zoom")
48 font.pointSize: ScreenTools.smallFontPointSize
49 }
50
51 QGCSlider {
52 objectName: "photoVideoControl_zoomSlider"
53 Layout.alignment: Qt.AlignHCenter
54 Layout.fillHeight: true
55 orientation: Qt.Vertical
56 to: 100
57 from: 0
58 value: _camera.zoomLevel
59 live: true
60 onMoved: _camera.zoomLevel = value
61 }
62 }
63
64 ColumnLayout {
65 spacing: _margins
66
67 // Camera name
68 QGCLabel {
69 Layout.alignment: Qt.AlignHCenter
70 text: _camera.modelName
71 visible: _cameraManager.cameras.length > 1
72 }
73
74 // Photo/Video Mode Selector
75 Rectangle {
76 Layout.alignment: Qt.AlignHCenter
77 width: ScreenTools.defaultFontPixelWidth * 10
78 height: width / 2
79 color: qgcPal.windowShadeLight
80 radius: height * 0.5
81 visible: _camera.hasModes
82
83 //-- Video Mode
84 Rectangle {
85 anchors.verticalCenter: parent.verticalCenter
86 width: parent.height
87 height: parent.height
88 color: _cameraInVideoMode ? qgcPal.window : qgcPal.windowShadeLight
89 radius: height * 0.5
90 anchors.left: parent.left
91 border.color: qgcPal.text
92 border.width: _cameraInPhotoMode ? 0 : 1
93
94 QGCColoredImage {
95 height: parent.height * 0.5
96 width: height
97 anchors.centerIn: parent
98 source: "/qmlimages/camera_video.svg"
99 fillMode: Image.PreserveAspectFit
100 sourceSize.height: height
101 color: _cameraInVideoMode ? qgcPal.colorGreen : qgcPal.text
102
103 MouseArea {
104 anchors.fill: parent
105 enabled: _cameraInPhotoMode ? _photoCaptureIdle : true
106 onClicked: _camera.setCameraModeVideo()
107 }
108 }
109 }
110
111 //-- Photo Mode
112 Rectangle {
113 anchors.verticalCenter: parent.verticalCenter
114 width: parent.height
115 height: parent.height
116 color: _cameraInPhotoMode ? qgcPal.window : qgcPal.windowShadeLight
117 radius: height * 0.5
118 anchors.right: parent.right
119 border.color: qgcPal.text
120 border.width: _cameraInPhotoMode ? 1 : 0
121
122 QGCColoredImage {
123 height: parent.height * 0.5
124 width: height
125 anchors.centerIn: parent
126 source: "/qmlimages/camera_photo.svg"
127 fillMode: Image.PreserveAspectFit
128 sourceSize.height: height
129 color: _cameraInPhotoMode ? qgcPal.colorGreen : qgcPal.text
130
131 MouseArea {
132 anchors.fill: parent
133 enabled: _cameraInVideoMode ? _videoCaptureIdle : true
134 onClicked: _camera.setCameraModePhoto()
135 }
136 }
137 }
138 }
139
140 ColumnLayout {
141 Layout.alignment: Qt.AlignHCenter
142 spacing: _smallMargins
143
144 // Start/Stop Video button
145 Rectangle {
146 id: videoCaptureButton
147 Layout.alignment: Qt.AlignHCenter
148 color: videoCaptureButtonPalette.button
149 width: ScreenTools.defaultFontPixelWidth * 6
150 height: width
151 radius: width * 0.5
152 border.width: 1
153 border.color: videoCaptureButtonPalette.buttonBorder
154 visible: (_camera.hasModes && _cameraInVideoMode) || (!_camera.hasModes && _camera.capturesVideo)
155 enabled: _camera.captureVideoState !== MavlinkCameraControlInterface.CaptureVideoStateDisabled
156
157 QGCPalette { id: videoCaptureButtonPalette; colorGroupEnabled: videoCaptureButton.enabled }
158
159 Rectangle {
160 anchors.centerIn: parent
161 anchors.alignWhenCentered: false // Prevents anchors.centerIn from snapping to integer coordinates, which can throw off centering.
162 color: videoCaptureButtonPalette.buttonBorder
163 width: parent.width * 0.75
164 height: width
165 radius: width * 0.5
166 }
167
168 Rectangle {
169 anchors.centerIn: parent
170 anchors.alignWhenCentered: false // Prevents anchors.centerIn from snapping to integer coordinates, which can throw off centering.
171 width: parent.width * (_isCapturing ? 0.5 : 0.75)
172 height: width
173 radius: _isCapturing ? ScreenTools.defaultFontPixelWidth * 0.5 : width * 0.5
174 color: videoCaptureButtonPalette.videoCaptureButtonColor
175 border.width: 1
176 border.color: videoCaptureButtonPalette.buttonBorder
177
178 property bool _isCapturing: _camera.captureVideoState === MavlinkCameraControlInterface.CaptureVideoStateCapturing
179 }
180
181 MouseArea {
182 anchors.fill: parent
183 onClicked: _camera.toggleVideoRecording()
184 }
185 }
186
187 QGCLabel {
188 Layout.alignment: Qt.AlignHCenter
189 text: qsTr("Video")
190 font.pointSize: ScreenTools.smallFontPointSize
191 visible: videoCaptureButton.visible && photoCaptureButton.visible
192 }
193
194 // Record time
195 Rectangle {
196 Layout.alignment: Qt.AlignHCenter
197 color: _videoCaptureIdle ? "transparent" : videoCaptureButtonPalette.videoCaptureButtonColor
198 Layout.preferredWidth: videoRecordTime.width + (_smallMargins * 2)
199 Layout.preferredHeight: videoRecordTime.height
200 radius: _smallMargins
201 visible: videoCaptureButton.visible
202
203 // Video record time
204 QGCLabel {
205 id: videoRecordTime
206 anchors.leftMargin: _smallMargins
207 anchors.left: parent.left
208 anchors.top: parent.top
209 text: _videoCaptureIdle ? "00:00:00" : _camera.recordTimeStr
210 }
211 }
212
213 Item {
214 Layout.alignment: Qt.AlignHCenter
215 width: 1
216 height: 1
217 visible: videoCaptureButton.visible && photoCaptureButton.visible
218 }
219
220 // Take Photo button
221 Rectangle {
222 id: photoCaptureButton
223 objectName: "photoVideoControl_photoCaptureButton"
224 Layout.alignment: Qt.AlignHCenter
225 color: photoCaptureButtonPalette.button
226 width: ScreenTools.defaultFontPixelWidth * 6
227 height: width
228 radius: width * 0.5
229 border.width: 1
230 border.color: photoCaptureButtonPalette.buttonBorder
231 visible: (_camera.hasModes && _cameraInPhotoMode) || (!_camera.hasModes && (_camera.hasVideoStream || _camera.capturesPhotos))
232 enabled: _camera.capturePhotosState !== MavlinkCameraControlInterface.CapturePhotosStateDisabled
233
234 QGCPalette { id: photoCaptureButtonPalette; colorGroupEnabled: photoCaptureButton.enabled }
235
236 Rectangle {
237 anchors.centerIn: parent
238 anchors.alignWhenCentered: false // Prevents anchors.centerIn from snapping to integer coordinates, which can throw off centering.
239 color: photoCaptureButtonPalette.buttonBorder
240 width: parent.width * 0.75
241 height: width
242 radius: width * 0.5
243 }
244
245 Rectangle {
246 anchors.centerIn: parent
247 anchors.alignWhenCentered: false // Prevents anchors.centerIn from snapping to integer coordinates, which can throw off centering.
248 width: parent.width * (_isCapturing ? 0.5 : 0.75)
249 height: width
250 radius: _isCapturing ? ScreenTools.defaultFontPixelWidth * 0.5 : width * 0.5
251 color: photoCaptureButtonPalette.photoCaptureButtonColor
252 border.width: 1
253 border.color: photoCaptureButtonPalette.buttonBorder
254
255 property bool _isCapturing: _camera.capturePhotosState === MavlinkCameraControlInterface.CapturePhotosStateCapturingSinglePhoto ||
256 _camera.capturePhotosState === MavlinkCameraControlInterface.CapturePhotosStateCapturingMultiplePhotos
257 }
258
259 MouseArea {
260 anchors.fill: parent
261 onClicked: {
262 if (_camera.capturePhotosState === MavlinkCameraControlInterface.CapturePhotosStateCapturingMultiplePhotos) {
263 _camera.stopTakePhoto()
264 } else if (_camera.capturePhotosState === MavlinkCameraControlInterface.CapturePhotosStateIdle) {
265 _camera.takePhoto()
266 }
267 }
268 }
269 }
270
271 QGCLabel {
272 Layout.alignment: Qt.AlignHCenter
273 text: qsTr("Photo")
274 font.pointSize: ScreenTools.smallFontPointSize
275 visible: videoCaptureButton.visible && photoCaptureButton.visible
276 }
277
278 // Capture count
279 Rectangle {
280 Layout.alignment: Qt.AlignHCenter
281 color: _photoCaptureIdle ? "transparent" : photoCaptureButtonPalette.photoCaptureButtonColor
282 Layout.preferredWidth: photoCaptureCount.width + (_smallMargins * 2)
283 Layout.preferredHeight: photoCaptureCount.height
284 radius: _smallMargins
285 visible: photoCaptureButton.visible
286
287 // Photo capture count
288 QGCLabel {
289 id: photoCaptureCount
290 anchors.leftMargin: _smallMargins
291 anchors.left: parent.left
292 anchors.top: parent.top
293 text: _activeVehicle ? ('00000' + _activeVehicle.cameraTriggerPoints.count).slice(-5) : "00000"
294 }
295 }
296 }
297
298 //-- Status Information
299 ColumnLayout {
300 Layout.alignment: Qt.AlignHCenter
301 spacing: 0
302 visible: storageStatus.visible || batteryStatus.visible
303
304 QGCLabel {
305 id: storageStatus
306 Layout.alignment: Qt.AlignHCenter
307 text: qsTr("Free: ") + _camera.storageFreeStr
308 font.pointSize: ScreenTools.defaultFontPointSize
309 visible: _camera.storageStatus === MavlinkCameraControlInterface.STORAGE_READY
310 }
311
312 QGCLabel {
313 id: batteryStatus
314 Layout.alignment: Qt.AlignHCenter
315 text: qsTr("Battery: ") + _camera.batteryRemainingStr
316 font.pointSize: ScreenTools.defaultFontPointSize
317 visible: _camera.batteryRemaining >= 0
318 }
319 }
320
321 ColumnLayout {
322 id: trackingControls
323 Layout.alignment: Qt.AlignHCenter
324 spacing: 0
325 visible: _camera.hasTracking
326
327 Rectangle {
328 Layout.alignment: Qt.AlignHCenter
329 color: _camera.trackingEnabled ? qgcPal.colorRed : qgcPal.windowShadeLight
330 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 6
331 Layout.preferredHeight: Layout.preferredWidth
332 border.color: qgcPal.buttonText
333 border.width: 3
334
335 QGCColoredImage {
336 height: parent.height * 0.5
337 width: height
338 anchors.centerIn: parent
339 source: "/qmlimages/TrackingIcon.svg"
340 fillMode: Image.PreserveAspectFit
341 sourceSize.height: height
342 color: qgcPal.text
343
344 MouseArea {
345 anchors.fill: parent
346 onClicked: {
347 _camera.trackingEnabled = !_camera.trackingEnabled;
348 if (!_camera.trackingEnabled) {
349 _camera.stopTracking()
350 }
351 }
352 }
353 }
354 }
355
356 QGCLabel {
357 Layout.alignment: Qt.AlignHCenter
358 text: qsTr("Camera Tracking")
359 font.pointSize: ScreenTools.smallFontPointSize
360 }
361 }
362
363 QGCColoredImage {
364 Layout.alignment: Qt.AlignHCenter
365 source: "/res/gear-black.svg"
366 mipmap: true
367 Layout.preferredHeight: ScreenTools.defaultFontPixelHeight * 1.5
368 Layout.preferredWidth: Layout.preferredHeight
369 sourceSize.height: Layout.preferredHeight
370 color: qgcPal.text
371 fillMode: Image.PreserveAspectFit
372
373 QGCMouseArea {
374 fillItem: parent
375 onClicked: settingsDialogFactory.open()
376 }
377 }
378 }
379
380 QGCPopupDialogFactory {
381 id: settingsDialogFactory
382
383 dialogComponent: settingsDialogComponent
384 }
385
386 Component {
387 id: settingsDialogComponent
388
389 QGCPopupDialog {
390 title: qsTr("Settings")
391 buttons: Dialog.Close
392
393 property bool _multipleMavlinkCameras: _cameraManager.cameras.count > 1
394 property bool _multipleMavlinkCameraStreams: _camera.streamLabels.length > 1
395 property bool _cameraStorageSupported: _camera.storageStatus !== MavlinkCameraControlInterface.STORAGE_NOT_SUPPORTED
396 property var _videoSettings: QGroundControl.settingsManager.videoSettings
397
398 ColumnLayout {
399 spacing: _margins
400
401 GridLayout {
402 id: gridLayout
403 flow: GridLayout.TopToBottom
404 rows: dynamicRows + _camera.activeSettings.length
405
406 property int dynamicRows: 10
407
408 // First column
409 QGCLabel {
410 text: qsTr("Camera")
411 visible: _multipleMavlinkCameras
412 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
413 }
414
415 QGCLabel {
416 text: qsTr("Video Stream")
417 visible: _multipleMavlinkCameraStreams
418 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
419 }
420
421 QGCLabel {
422 text: qsTr("Thermal View Mode")
423 visible: _camera.thermalStreamInstance
424 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
425 }
426
427 QGCLabel {
428 text: qsTr("Blend Opacity")
429 visible: _camera.thermalStreamInstance && _camera.thermalMode === MavlinkCameraControlInterface.THERMAL_BLEND
430 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
431 }
432
433 // Mavlink Camera Protocol active settings
434 Repeater {
435 model: _camera.activeSettings
436
437 QGCLabel {
438 text: _camera.getFact(modelData).shortDescription
439 }
440 }
441
442 QGCLabel {
443 text: qsTr("Photo Mode")
444 visible: _camera.capturesPhotos
445 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
446 }
447
448 QGCLabel {
449 text: qsTr("Photo Interval (seconds)")
450 visible: _camera.capturesPhotos && _camera.photoCaptureMode === MavlinkCameraControlInterface.PHOTO_CAPTURE_TIMELAPSE
451 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
452 }
453
454 QGCLabel {
455 text: qsTr("Video Grid Lines")
456 visible: _camera.hasVideoStream
457 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
458 }
459
460 QGCLabel {
461 text: qsTr("Video Screen Fit")
462 visible: _camera.hasVideoStream
463 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
464 }
465
466 QGCLabel {
467 text: qsTr("Reset Camera Defaults")
468 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
469 }
470
471 QGCLabel {
472 text: qsTr("Storage")
473 visible: _cameraStorageSupported
474 onVisibleChanged: gridLayout.dynamicRows += visible ? 1 : -1
475 }
476
477 // Second column
478 QGCComboBox {
479 Layout.fillWidth: true
480 sizeToContents: true
481 model: _cameraManager.cameraLabels
482 currentIndex: _cameraManager.currentCamera
483 visible: _multipleMavlinkCameras
484 onActivated: (index) => { _cameraManager.currentCamera = index }
485 }
486
487 QGCComboBox {
488 Layout.fillWidth: true
489 sizeToContents: true
490 model: _camera.streamLabels
491 currentIndex: _camera.currentStream
492 visible: _multipleMavlinkCameraStreams
493 onActivated: (index) => { _camera.currentStream = index }
494 }
495
496 QGCComboBox {
497 Layout.fillWidth: true
498 sizeToContents: true
499 model: [ qsTr("Off"), qsTr("Blend"), qsTr("Full"), qsTr("Picture In Picture") ]
500 currentIndex: _camera.thermalMode
501 visible: _camera.thermalStreamInstance
502 onActivated: (index) => { _camera.thermalMode = index }
503 }
504
505 QGCSlider {
506 Layout.fillWidth: true
507 to: 100
508 from: 0
509 value: _camera.thermalOpacity
510 live: true
511 visible: _camera.thermalStreamInstance && _camera.thermalMode === MavlinkCameraControlInterface.THERMAL_BLEND
512 onValueChanged: _camera.thermalOpacity = value
513 }
514
515 // Mavlink Camera Protocol active settings
516 Repeater {
517 model: _camera.activeSettings
518
519 RowLayout {
520 Layout.fillWidth: true
521 spacing: ScreenTools.defaultFontPixelWidth
522
523 property var _fact: _camera.getFact(modelData)
524 property bool _isBool: _fact.typeIsBool
525 property bool _isCombo: !_isBool && _fact.enumStrings.length > 0
526 property bool _isSlider: _fact && !isNaN(_fact.increment)
527 property bool _isEdit: !_isBool && !_isSlider && _fact.enumStrings.length < 1
528 property bool _isReadOnly: _fact && _fact.readOnly
529
530 FactComboBox {
531 Layout.fillWidth: true
532 sizeToContents: true
533 fact: parent._fact
534 indexModel: false
535 visible: parent._isCombo
536 enabled: !parent._isReadOnly
537 }
538 FactTextField {
539 Layout.fillWidth: true
540 fact: parent._fact
541 visible: parent._isEdit
542 enabled: !parent._isReadOnly
543 }
544 QGCSlider {
545 Layout.fillWidth: true
546 to: parent._fact.max
547 from: parent._fact.min
548 stepSize: parent._fact.increment
549 visible: parent._isSlider
550 enabled: !parent._isReadOnly
551 live: false
552 property bool initialized: false
553
554 onValueChanged: {
555 if (!initialized) {
556 return
557 }
558 parent._fact.value = value
559 }
560
561 Component.onCompleted: {
562 value = parent._fact.value
563 initialized = true
564 }
565 }
566 QGCCheckBoxSlider {
567 checked: parent._fact ? parent._fact.value : false
568 visible: parent._isBool
569 enabled: !parent._isReadOnly
570 onClicked: parent._fact.value = checked ? 1 : 0
571 }
572 }
573 }
574
575 QGCComboBox {
576 Layout.fillWidth: true
577 sizeToContents: true
578 model: [ qsTr("Single"), qsTr("Time Lapse") ]
579 currentIndex: _camera.photoCaptureMode
580 visible: _camera.capturesPhotos
581 onActivated: (index) => { _camera.photoCaptureMode = index }
582 }
583
584 QGCSlider {
585 Layout.fillWidth: true
586 to: 60
587 from: 1
588 stepSize: 1
589 value: _camera.photoLapse
590 displayValue: true
591 live: true
592 visible: _camera.capturesPhotos && _camera.photoCaptureMode === MavlinkCameraControlInterface.PHOTO_CAPTURE_TIMELAPSE
593 onValueChanged: _camera.photoLapse = value
594 }
595
596 QGCCheckBoxSlider {
597 checked: _videoSettings.gridLines.rawValue
598 visible: _camera.hasVideoStream
599 onClicked: _videoSettings.gridLines.rawValue = checked ? 1 : 0
600 }
601
602 FactComboBox {
603 Layout.fillWidth: true
604 sizeToContents: true
605 fact: _videoSettings.videoFit
606 indexModel: false
607 visible: _camera.hasVideoStream
608 }
609
610 QGCButton {
611 Layout.fillWidth: true
612 text: qsTr("Reset")
613 onClicked: resetPrompt.open()
614 MessageDialog {
615 id: resetPrompt
616 title: qsTr("Reset Camera to Factory Settings")
617 text: qsTr("Confirm resetting all settings?")
618 buttons: MessageDialog.Yes | MessageDialog.No
619
620 onButtonClicked: function (button, role) {
621 switch (button) {
622 case MessageDialog.Yes:
623 _camera.resetSettings()
624 resetPrompt.close()
625 break;
626 case MessageDialog.No:
627 resetPrompt.close()
628 break;
629 }
630 }
631 }
632 }
633
634 QGCButton {
635 Layout.fillWidth: true
636 text: qsTr("Format")
637 visible: _cameraStorageSupported
638 onClicked: formatPrompt.open()
639 MessageDialog {
640 id: formatPrompt
641 title: qsTr("Format Camera Storage")
642 text: qsTr("Confirm erasing all files?")
643 buttons: MessageDialog.Yes | MessageDialog.No
644
645 onButtonClicked: function (button, role) {
646 switch (button) {
647 case MessageDialog.Yes:
648 _camera.formatCard()
649 formatPrompt.close()
650 break;
651 case MessageDialog.No:
652 formatPrompt.close()
653 break;
654 }
655 }
656 }
657 }
658 }
659 }
660 }
661 }
662 }
663}