diff --git a/Analyser/CMakeLists.txt b/Analyser/CMakeLists.txt index d33c4ea..0f5d90c 100644 --- a/Analyser/CMakeLists.txt +++ b/Analyser/CMakeLists.txt @@ -27,6 +27,8 @@ qt_add_qml_module(Analyser qml/ConnectionItem.qml qml/OperationItem.qml qml/OtaPage.qml + qml/EnrollVerifyOperations.qml + qml/ExtendedOperations.qml RESOURCES resources/successfull.svg resources/warning.svg diff --git a/Analyser/VideoFrameProvider.cpp b/Analyser/VideoFrameProvider.cpp index 32ae5ed..db6d460 100644 --- a/Analyser/VideoFrameProvider.cpp +++ b/Analyser/VideoFrameProvider.cpp @@ -1,7 +1,7 @@ #include "VideoFrameProvider.h" VideoFrameProvider::VideoFrameProvider() - : QQuickImageProvider(QQuickImageProvider::Image), m_image(800, 600, QImage::Format_RGB32) { + : QQuickImageProvider(QQuickImageProvider::Image), m_image(600, 800, QImage::Format_RGB32) { m_image.fill(Qt::black); } @@ -21,6 +21,6 @@ void VideoFrameProvider::setImage(const QImage &image) { } void VideoFrameProvider::reset() { - m_image = QImage(800, 600, QImage::Format_RGB32); + m_image = QImage(600, 800, QImage::Format_RGB32); m_image.fill(Qt::black); } diff --git a/Analyser/VideoPlayer.cpp b/Analyser/VideoPlayer.cpp index 80f9b74..4769e5d 100644 --- a/Analyser/VideoPlayer.cpp +++ b/Analyser/VideoPlayer.cpp @@ -70,6 +70,9 @@ void VideoPlayer::run() { if (status == 0) { QImage image; image.loadFromData(packet->data, packet->size); + QTransform transform; + transform.rotate(90); + image = image.transformed(transform); if (!image.isNull() && m_callback) m_callback(image); } else { char message[256] = {0}; diff --git a/Analyser/qml/EnrollVerifyOperations.qml b/Analyser/qml/EnrollVerifyOperations.qml new file mode 100644 index 0000000..8bd1a23 --- /dev/null +++ b/Analyser/qml/EnrollVerifyOperations.qml @@ -0,0 +1,127 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Analyser + +RowLayout { + id: control + GroupBox { + title: "注册用户" + GridLayout { + columns: 2 + Label { + text: qsTr("用户姓名") + } + TextField { + id: enrollName + implicitWidth: 100 + } + Label { + text: qsTr("严格模式") + } + Switch { + id: strictMode + } + Label { + text: qsTr("互斥模式") + } + ComboBox { + id: excludeMode + model: ["无", "仅比对", "互斥"] + } + Label { + text: qsTr("超时时间") + } + TextField { + id: enrollTimeout + implicitWidth: 100 + text: "30" + } + + Label { + text: qsTr("持久化") + } + Switch { + id: persistence + checked: true + } + + Label { + text: qsTr("保存图片") + } + Switch { + id: extendedMode + } + + Button { + property bool enrolling: App.module ? (App.module.currentMessageId + === ModuleCommunication.EnrollSingle) + || (App.module.currentMessageId === ModuleCommunication.EnrollExtended) : false + text: enrolling ? "取消" : "注册" + onClicked: { + if (enrolling) { + App.module.reset() + } else if (extendedMode.checked) { + App.enrollExtended(enrollName.text, strictMode.checked, + excludeMode.currentIndex, + persistence.checked, + parseInt(enrollTimeout.text)) + } else { + App.enroll(enrollName.text, strictMode.checked, + excludeMode.currentIndex, + persistence.checked, + parseInt(enrollTimeout.text)) + } + } + } + } + } + + GroupBox { + title: "识别用户" + GridLayout { + columns: 2 + Label { + text: qsTr("超时时间(s)") + } + TextField { + id: verifyTimeout + implicitWidth: 80 + text: "10" + } + Label { + text: qsTr("持续识别") + } + Switch { + checked: App.persistenceMode + onToggled: App.persistenceMode = !App.persistenceMode + } + Label { + text: qsTr("保存图片") + } + Switch { + id: extendedVerifyMode + } + Label { + text: qsTr("识别间隔(s)") + } + TextField { + id: verifyIntetval + implicitWidth: 80 + text: App.persistenceVerifyInterval + } + Item {} + Button { + text: App.isVerifying ? "停止" : "识别" + onClicked: { + if (App.isVerifying) { + App.module.reset() + } else { + App.persistenceVerifyInterval = parseInt(verifyIntetval.text) + App.verify(extendedVerifyMode.checked, parseInt(verifyTimeout.text)) + } + } + } + } + } +} diff --git a/Analyser/qml/ExtendedOperations.qml b/Analyser/qml/ExtendedOperations.qml new file mode 100644 index 0000000..0957c1b --- /dev/null +++ b/Analyser/qml/ExtendedOperations.qml @@ -0,0 +1,129 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Analyser + +Item { + id: control + RowLayout { + anchors.fill: parent + enabled: App.connected + Column { + GroupBox { + title: "删除用户" + GridLayout { + columns: 2 + Label { + text: qsTr("用户ID") + } + TextField { + id: deleteUserId + implicitWidth: 100 + } + Button { + text: "删除" + onClicked: App.deleteUser(parseInt(deleteUserId.text)) + } + Button { + text: "删除所有" + onClicked: App.deleteAll() + } + } + } + GroupBox { + title: "其它" + enabled: App.connected + GridLayout { + columns: 2 + Button { + text: "复位" + onClicked: App.module.reset() + } + Button { + text: "状态查询" + onClicked: App.module.requestCurrentStatus() + } + Button { + text: "ID查询" + onClicked: App.module.requestUniqueId() + } + Button { + id: otaButton + text: "OTA升级" + onClicked: loader.active = true + } + Row { + Label { + text: qsTr("日志") + } + Switch { + id: debugMode + onToggled: App.module.setDebugEnabled(debugMode.checked) + } + } + } + } + } + + GroupBox { + title: "图片注册" + visible: true + GridLayout { + columns: 2 + TextField { + id: imagePath + Layout.columnSpan: 2 + implicitWidth: 200 + placeholderText: "请选择图片" + onPressed: { + fileDialog.open() + } + } + Label { + text: qsTr("用户姓名") + } + TextField { + id: imageEnrollName + implicitWidth: 100 + text: "测试下发" + } + Label { + text: qsTr("循环") + } + Switch { + checked: App.imageUploadPersistenceMode + onToggled: { + App.imageUploadPersistenceMode = checked + } + } + Button { + text: "注册" + onClicked: App.uploadImage(imagePath.text, imageEnrollName.text, 0) + } + Button { + text: "识别" + onClicked: App.uploadImage(imagePath.text, imageEnrollName.text, 1) + } + } + } + } + + MouseArea { + id: otaMouseArea + width: otaButton.width + height: otaButton.height + enabled: !App.connected + onClicked: { + loader.active = true + } + } + + onVisibleChanged: { + if (!visible) + return + Qt.callLater(() => { + otaMouseArea.x = otaButton.mapToItem(control, 0, 0).x + otaMouseArea.y = otaButton.mapToItem(control, 0, 0).y + }) + } +} diff --git a/Analyser/qml/Main.qml b/Analyser/qml/Main.qml index 294358e..31b8978 100644 --- a/Analyser/qml/Main.qml +++ b/Analyser/qml/Main.qml @@ -6,8 +6,8 @@ import Analyser Window { id: window - width: 1220 - height: 890 + width: 1120 + height: 630 visible: true title: qsTr(Qt.application.name + " " + Qt.application.version) @@ -17,44 +17,9 @@ Window { anchors.top: parent.top } - Item { - id: resultGroupBox - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: parent.bottom - width: 220 - Text { - id: resultGroupBoxTitle - text: "识别结果" - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - height: 30 - } - ScrollView { - id: resultView - anchors.left: parent.left - anchors.right: parent.right - anchors.top: resultGroupBoxTitle.bottom - anchors.bottom: parent.bottom - TextArea { - id: resultBrowser - font.pixelSize: 14 - readOnly: true - wrapMode: TextArea.WordWrap - } - } - Button { - text: "清空" - anchors.right: parent.right - anchors.bottom: parent.bottom - onClicked: resultBrowser.clear() - } - } - ColumnLayout { anchors.left: operationItem.right - anchors.right: resultGroupBox.left + anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom TabBar { @@ -72,13 +37,21 @@ Window { StackLayout { width: parent.width currentIndex: bar.currentIndex - clip: true - Image { - id: image - cache: false - fillMode: Image.PreserveAspectFit - rotation: 90 - source: "image://videoframe/" + Item { + Image { + id: image + property real aspectRatio: 600 / 800 + anchors.centerIn: parent + width: Math.min(parent.width, parent.height * aspectRatio) + height: width / aspectRatio + cache: false + fillMode: Image.PreserveAspectFit + source: "image://videoframe/" + } + Image { + anchors.fill: image + source: "qrc:/qt/qml/Analyser/resources/palm-middle.png" + } } Item { ScrollView { diff --git a/Analyser/qml/OperationItem.qml b/Analyser/qml/OperationItem.qml index a060ed6..834976c 100644 --- a/Analyser/qml/OperationItem.qml +++ b/Analyser/qml/OperationItem.qml @@ -20,225 +20,25 @@ Item { text: "OTA版本: "+(App.module!==null?App.module.otaVerison:"") } } - - GroupBox { - id: commandBox - title: "命令" - Layout.columnSpan: 2 - enabled: App.connected - GridLayout { - columns: 2 - GroupBox { - title: "注册用户" - GridLayout { - columns: 2 - Label { - text: qsTr("用户姓名") - } - TextField { - id: enrollName - implicitWidth: 100 - } - Label { - text: qsTr("严格模式") - } - Switch { - id: strictMode - } - Label { - text: qsTr("互斥模式") - } - ComboBox { - id: excludeMode - model: ["无","仅比对","互斥"] - } - Label { - text: qsTr("超时时间") - } - TextField { - id: enrollTimeout - implicitWidth: 100 - text: "30" - } - - Label { - text: qsTr("持久化") - } - Switch { - id: persistence - checked: true - } - - Label { - text: qsTr("保存图片") - } - Switch { - id: extendedMode - } - - Button { - property bool enrolling: App.module ? (App.module.currentMessageId === ModuleCommunication.EnrollSingle) || (App.module.currentMessageId === ModuleCommunication.EnrollExtended) : false - text: enrolling ? "取消" : "注册" - onClicked: { - if (enrolling) { - App.module.reset() - } else if (extendedMode.checked) { - App.enrollExtended(enrollName.text, strictMode.checked, excludeMode.currentIndex, persistence.checked, parseInt(enrollTimeout.text)) - } else { - App.enroll(enrollName.text, strictMode.checked, excludeMode.currentIndex, persistence.checked, parseInt(enrollTimeout.text)) - } - } - } - } - } - GroupBox { - title: "识别用户" - GridLayout { - columns: 2 - Label { - text: qsTr("超时时间(s)") - } - TextField { - id: verifyTimeout - implicitWidth: 80 - text: "10" - } - Label { - text: qsTr("持续识别") - } - Switch { - checked: App.persistenceMode - onToggled: App.persistenceMode = !App.persistenceMode - } - Label { - text: qsTr("保存图片") - } - Switch { - id: extendedVerifyMode - } - Label { - text: qsTr("识别间隔(s)") - } - TextField { - id: verifyIntetval - implicitWidth: 80 - text: App.persistenceVerifyInterval - } - Item {} - Button { - text: App.isVerifying ? "停止" : "识别" - onClicked: { - if (App.isVerifying) { - App.module.reset() - } else { - App.persistenceVerifyInterval = parseInt( - verifyIntetval.text) - App.verify(extendedVerifyMode.checked, - parseInt(verifyTimeout.text)) - } - } - } - } - } - - Column { - GroupBox { - title: "删除用户" - GridLayout { - columns: 2 - Label { - text: qsTr("用户ID") - } - TextField { - id: deleteUserId - implicitWidth: 100 - } - Button { - text: "删除" - onClicked: App.deleteUser(parseInt( - deleteUserId.text)) - } - Button { - text: "删除所有" - onClicked: App.deleteAll() - } - } - } - GroupBox { - GridLayout { - columns: 2 - Button { - text: "复位" - onClicked: App.module.reset() - } - Button { - text: "状态查询" - onClicked: App.module.requestCurrentStatus() - } - Button { - text: "ID查询" - onClicked: App.module.requestUniqueId() - } - Button { - id: otaButton - text: "OTA升级" - onClicked: loader.active = true - } - Row { - Label { - text: qsTr("日志") - } - Switch { - id: debugMode - onToggled: App.module.setDebugEnabled(debugMode.checked) - } - } - } - } - } - GroupBox { - title: "图片注册" - visible: true - GridLayout { - columns: 2 - TextField { - id: imagePath - Layout.columnSpan: 2 - implicitWidth: 200 - placeholderText: "请选择图片" - onPressed: { - fileDialog.open() - } - } - Label { - text: qsTr("用户姓名") - } - TextField { - id: imageEnrollName - implicitWidth: 100 - text: "测试下发" - } - Label { - text: qsTr("循环") - } - Switch { - checked: App.imageUploadPersistenceMode - onToggled: { - App.imageUploadPersistenceMode = checked - } - } - Button { - text: "注册" - onClicked: App.uploadImage(imagePath.text,imageEnrollName.text, 0) - } - Button { - text: "识别" - onClicked: App.uploadImage(imagePath.text,imageEnrollName.text, 1) - } - } - } + TabBar { + id: operationBar + Layout.fillWidth: true + TabButton { + text: "注册/识别" + } + TabButton { + text: "删除/其它" } } + StackLayout { + currentIndex: operationBar.currentIndex + EnrollVerifyOperations { + enabled: App.connected + } + ExtendedOperations { + } + } + } FileDialog { id: fileDialog @@ -253,19 +53,6 @@ Item { } } - MouseArea { - width: otaButton.width - height: otaButton.height - enabled: !commandBox.enabled - onClicked: { - loader.active = true - } - Component.onCompleted: { - x = Qt.binding(() => otaButton.mapToItem(root, 0, 0).x) - y = Qt.binding(() => otaButton.mapToItem(root, 0, 0).y) - } - } - Loader { id: loader source: "OtaPage.qml"