import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import AntiClipSettings 1.0 Item { id: root property alias enabled: shieldedRow.enabled property int dargWidth: 12 property color openDoorAreaColor: "#0FF40D" // 绿色 property var openDoorAreaPoints: [] property int openDoorAreaWay: 0 property color shieldedAreaColor: "yellow" property var shieldedAreaPoints: [] property bool shieldedAreaEnabled: false property color antiClipAreaColor: "blue" property var antiClipAreaPoints: [] property bool antiClipAreaEnabled: false property int antiClipSensitivity: 1 property alias flip: flipSwitch.checked property alias videoRotation: rotateComboBox.currentIndex Item { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.bottom: controlBar.top Image { id: image anchors.centerIn: parent cache: false fillMode: Image.PreserveAspectFit source: "image://videoframe/" property real aspectRatio: 16 / 9 width: Math.min(parent.width, parent.height * aspectRatio) height: width / aspectRatio enabled: root.enabled Canvas { id: canvas anchors.fill: parent onPaint: { var ctx = canvas.getContext("2d") ctx.clearRect(0, 0, canvas.width, canvas.height) if(!root.enabled)return if(openDoorAreaWay == DeviceConnection.FullArea){ ctx.strokeStyle = openDoorAreaColor ctx.lineWidth = 8 ctx.strokeRect(0,0, canvas.width, canvas.height) } else if ((openDoorAreaWay>=DeviceConnection.Quadrangle) &&(openDoorAreaPoints.length > 0)) { ctx.strokeStyle = openDoorAreaColor ctx.lineWidth = 2 ctx.beginPath() let point = scaledPoint(openDoorAreaPoints[0], width, height) ctx.moveTo(point.x, point.y) for (var i = 1; i < openDoorAreaPoints.length; i++) { point = scaledPoint(openDoorAreaPoints[i], width, height) ctx.lineTo(point.x, point.y) } ctx.closePath() ctx.stroke() } if (antiClipAreaEnabled && (antiClipAreaPoints.length > 0)) { ctx.strokeStyle = antiClipAreaColor ctx.lineWidth = 2 ctx.beginPath() let point = scaledPoint(antiClipAreaPoints[0], width, height) ctx.moveTo(point.x, point.y) for (var i = 1; i < antiClipAreaPoints.length; i++) { point = scaledPoint(antiClipAreaPoints[i], width, height) ctx.lineTo(point.x, point.y) } ctx.closePath() ctx.stroke() } if (shieldedAreaEnabled &&(shieldedAreaPoints.length > 0)) { ctx.lineWidth = 2 ctx.strokeStyle = shieldedAreaColor let point0 = scaledPoint(shieldedAreaPoints[0], width, height) let point1 = scaledPoint(shieldedAreaPoints[1], width, height) let point2 = scaledPoint(shieldedAreaPoints[2], width, height) let point3 = scaledPoint(shieldedAreaPoints[3], width, height) ctx.strokeRect(point0.x, point0.y, point1.x - point0.x, point1.y - point0.y) ctx.strokeRect(point2.x, point2.y, point3.x - point2.x, point3.y - point2.y) } } } Repeater { id: repeater visible: openDoorAreaWay>=DeviceConnection.Quadrangle model: openDoorAreaPoints delegate: Rectangle { width: dargWidth height: dargWidth visible: root.enabled &&(openDoorAreaWay>=DeviceConnection.Quadrangle) color: openDoorAreaColor x: scaledPoint(modelData, canvas.width, canvas.height).x - width / 2 y: scaledPoint(modelData, canvas.width, canvas.height).y - height / 2 } } Repeater { id: shieldedAreaRepeater model: shieldedAreaPoints visible: shieldedAreaEnabled delegate: Rectangle { width: dargWidth height: dargWidth visible: root.enabled && shieldedAreaEnabled color: shieldedAreaColor x: scaledPoint(modelData, canvas.width, canvas.height).x - width / 2 y: scaledPoint(modelData, canvas.width, canvas.height).y - height / 2 } } Repeater { id: antiAreaRepeater visible: antiClipAreaEnabled model: antiClipAreaPoints delegate: Rectangle { visible: root.enabled && antiClipAreaEnabled width: dargWidth height: dargWidth color: antiClipAreaColor x: scaledPoint(modelData, canvas.width, canvas.height).x - width / 2 y: scaledPoint(modelData, canvas.width, canvas.height).y - height / 2 } } MouseArea { anchors.fill: parent enabled: root.enabled property int draggedOpenDoorAreaPointIndex: -1 property int draggedShieldedAreaPointIndex: -1 property int draggedAntiAreaPointIndex: -1 onPressed: mouse => { if(openDoorAreaWay == DeviceConnection.Quadrangle){ for (var i = 0; i < openDoorAreaPoints.length; i++) { let point = scaledPoint( openDoorAreaPoints[i], canvas.width, canvas.height) if (isInside(mouse.x, mouse.y, point)) { draggedOpenDoorAreaPointIndex = i break } } } if (draggedOpenDoorAreaPointIndex >= 0) return if(shieldedAreaEnabled){ for (let i = 0; i < shieldedAreaPoints.length; i++) { let point = scaledPoint( shieldedAreaPoints[i], canvas.width, canvas.height) if (isInside(mouse.x, mouse.y, point)) { draggedShieldedAreaPointIndex = i break } } } if (draggedShieldedAreaPointIndex >= 0) return if(antiClipAreaEnabled){ for (let i = 0; i < antiClipAreaPoints.length; i++) { let point = scaledPoint( antiClipAreaPoints[i], canvas.width, canvas.height) if (isInside(mouse.x, mouse.y, point)) { draggedAntiAreaPointIndex = i break } } } } onReleased: { if (draggedOpenDoorAreaPointIndex >= 0) { App.updateOpenDoorAreaPoints(openDoorAreaPoints) draggedOpenDoorAreaPointIndex = -1 } if (draggedShieldedAreaPointIndex >= 0) { App.updateShieldedAreaPoints(shieldedAreaPoints) draggedShieldedAreaPointIndex = -1 } if (draggedAntiAreaPointIndex >= 0) { App.updateAntiClipAreaPoints(antiClipAreaPoints) draggedAntiAreaPointIndex = -1 } } onPositionChanged: mouse => { if ((mouse.x < 0) || (mouse.x > canvas.width) || (mouse.y < 0) || (mouse.y > canvas.height)) return if (draggedOpenDoorAreaPointIndex >= 0) { openDoorAreaPoints[draggedOpenDoorAreaPointIndex] = standardPoint(Qt.point(mouse.x, mouse.y), canvas.width, canvas.height) canvas.requestPaint() repeater.model = openDoorAreaPoints } else if (draggedShieldedAreaPointIndex >= 0) { shieldedAreaPoints[draggedShieldedAreaPointIndex] = standardPoint(Qt.point(mouse.x, mouse.y), canvas.width, canvas.height) canvas.requestPaint() shieldedAreaRepeater.model = shieldedAreaPoints } else if (draggedAntiAreaPointIndex >= 0) { antiClipAreaPoints[draggedAntiAreaPointIndex] = standardPoint(Qt.point(mouse.x, mouse.y), canvas.width, canvas.height) canvas.requestPaint() antiAreaRepeater.model = antiClipAreaPoints } } function isInside(x, y, point) { let edge = dargWidth / 2 return x >= point.x - edge && x <= point.x + edge && y >= point.y - edge && y <= point.y + edge } } } } GridLayout { id: controlBar anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom columns: 4 Label { text: qsTr("图像: ") } Row { enabled: root.enabled Layout.columnSpan: 3 Label { anchors.verticalCenter: parent.verticalCenter text: qsTr("旋转") } Item {width: 10; height:10} ComboBox { id: rotateComboBox model: ["0°","90°","180°","270°"] onCurrentIndexChanged: { App.currentDeviceRotation = rotateComboBox.currentIndex } } Item {width: 70; height:10} Label { anchors.verticalCenter: parent.verticalCenter text: qsTr("翻转") } Item {width: 10; height:10} Switch { id: flipSwitch onToggled: App.currentDeviceFlip=flipSwitch.checked } } Label { text: qsTr("开门区域: ") } Row { enabled: root.enabled Layout.columnSpan: 3 RadioButton { text: "关闭" checked: App.currentOpenDoorAreaWay ==DeviceConnection.Diabled onToggled:{ App.currentOpenDoorAreaWay =DeviceConnection.Diabled } } RadioButton { text: "全区域" checked: App.currentOpenDoorAreaWay ==DeviceConnection.FullArea onToggled:{ App.currentOpenDoorAreaWay =DeviceConnection.FullArea } } RadioButton { text: "四边形" checked: App.currentOpenDoorAreaWay ==DeviceConnection.Quadrangle onToggled:{ App.currentOpenDoorAreaWay =DeviceConnection.Quadrangle } } } Label {text: qsTr("防夹区域: ")} Row { enabled: root.enabled Layout.columnSpan: 1 RadioButton { text: "关闭" checked: !App.currentAntiClipAreaEnabled onToggled: { App.currentAntiClipAreaEnabled=false } } RadioButton { text: "四边形" checked: App.currentAntiClipAreaEnabled onToggled: { App.currentAntiClipAreaEnabled=true } } } Label { text: qsTr("灵敏度: ") Layout.alignment: Qt.AlignRight } ComboBox { id: antiClipSensitivityComboBox enabled: root.enabled Layout.alignment: Qt.AlignLeft model: [1,2,3,4,5] currentIndex: root.antiClipSensitivity-1 onCurrentIndexChanged: { App.currentAntiClipSensitivity = antiClipSensitivityComboBox.currentIndex+1 } } Label {text: qsTr("屏蔽区域: ")} Row { id: shieldedRow RadioButton { checked: !shieldedAreaEnabled text: "关闭" onToggled: { App.currentShieldedAreaEnabled = false } } RadioButton { checked: shieldedAreaEnabled text: "开启" onToggled: { App.currentShieldedAreaEnabled = true } } } } // 转换为显示画点 function scaledPoint(point, width, height) { let x = point.x * width / App.DeviceWidth let y = point.y * height / App.DeviceHeight return Qt.point(x, y) } // 转换为设备画点(640x360) function standardPoint(point, width, height) { let x = point.x * App.DeviceWidth / width let y = point.y * App.DeviceHeight / height return Qt.point(x, y) } onEnabledChanged: { canvas.requestPaint() if(!enabled){ image.source = "image://videoframe/black" } } Connections { target: App function onNewVideoFrame() { image.source = "" image.source = "image://videoframe/" } function onCurrentOpenDoorAreaPointsChanged() { canvas.requestPaint() repeater.model = openDoorAreaPoints } function onCurrentShieldedAreaPointsChanged() { shieldedAreaRepeater.model = shieldedAreaPoints canvas.requestPaint() } function onCurrentAntiClipAreaPointsChanged() { antiAreaRepeater.model = antiClipAreaPoints canvas.requestPaint() } function onCurrentOpenDoorAreaWayChanged(){ canvas.requestPaint() } function onCurrentShieldedAreaEnabledChanged(){ canvas.requestPaint() } function onCurrentAntiClipAreaEnabledChanged(){ canvas.requestPaint() } } }