文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT QML 开发(四)复杂控件--Listview
开源 C++ QT QML 开发(五)复杂控件--Gridview
推荐链接:
开源 C# 快速开发(十六)数据库--sqlserver增删改查
本章节主要内容是:演示了基于qt和qml的多媒体应用中,笔记本电脑摄像头的拍照功能。
1.代码分析
2.所有源码
3.效果演示
一、代码分析
- Backend.h 详细分析
构造函数分析
explicit Backend(QObject *parent = nullptr) : QObject(parent) {
// 创建保存图片的目录
QString picturesPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
m_saveDir = QDir(picturesPath).filePath("CameraDiagnosticTool");
if (!QDir().exists(m_saveDir)) {
QDir().mkpath(m_saveDir);
}
qDebug() << "图片保存目录:" << m_saveDir;
}
功能细节:
QStandardPaths::writableLocation(QStandardPaths::PicturesLocation):获取系统标准的图片存储路径
Windows: C:\Users\Username\Pictures
macOS: /Users/Username/Pictures
Linux: /home/Username/Pictures
QDir(picturesPath).filePath("CameraDiagnosticTool"):构建应用专属目录路径
QDir().mkpath(m_saveDir):递归创建目录,确保父目录存在
输出目录信息便于调试
onImageCaptured 函数
void onImageCaptured(const QString &previewPath) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "[" << timestamp << "] 图像已捕获,预览路径:" << previewPath;
emit statusMessageChanged("照片已捕获,显示预览");
}
参数分析:
const QString &previewPath:常量引用传递,避免拷贝开销
预览路径通常是临时文件路径
时间格式化:
"yyyy-MM-dd hh:mm:ss":年-月-日 时:分:秒 格式
使用24小时制建议改为 "yyyy-MM-dd HH:mm:ss"
onImageSaved 函数
void onImageSaved(int requestId, const QString &filePath) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "[" << timestamp << "] 图像已保存,请求ID:" << requestId << "路径:" << filePath;
emit statusMessageChanged(QString("照片已保存到: %1").arg(filePath));
}
参数说明:
requestId:异步操作标识符,用于匹配请求和响应
filePath:最终保存的完整文件路径
showMessage 函数
void showMessage(const QString &message) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "[" << timestamp << "]" << message;
emit statusMessageChanged(message);
}
设计模式:Facade模式,统一消息处理接口
captureImage 函数
Q_INVOKABLE void captureImage(const QImage &image) {
if (image.isNull()) {
emit statusMessageChanged("错误: 捕获的图像为空");
return;
}
// 生成文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz");
QString fileName = QString("capture_%1.jpg").arg(timestamp);
QString filePath = QDir(m_saveDir).filePath(fileName);
// 保存图片
if (image.save(filePath, "JPG", 90)) {
emit statusMessageChanged(QString("照片已保存: %1").arg(fileName));
emit imageSaved(filePath);
qDebug() << "图片保存成功:" << filePath;
} else {
emit statusMessageChanged("错误: 保存图片失败");
qDebug() << "图片保存失败:" << filePath;
}
}
关键点分析:
Q_INVOKABLE:使C++函数在QML中可调用
image.isNull():健壮性检查
时间戳格式:"yyyyMMdd_hhmmss_zzz" 包含毫秒,避免重名
图片质量:JPG格式,90%质量平衡文件大小和清晰度
双信号发射:状态消息 + 保存完成通知
getSaveDirectory 函数
Q_INVOKABLE QString getSaveDirectory() const {
return m_saveDir;
}
const 修饰符:线程安全,不会修改对象状态
简单的getter函数,提供目录信息给QML
- main.qml 详细分析
属性定义
property string lastCapturedImage: ""
property var cameraList: QtMultimedia.availableCameras || []
property int currentCameraIndex: 0
属性分析:
lastCapturedImage:记录最后捕获的图片路径
cameraList:使用 || [] 提供默认值,防止undefined
currentCameraIndex:当前选中摄像头的索引
摄像头信息区域
Label {
text: {
if (cameraList.length > 0) {
var cam = cameraList[currentCameraIndex]
return "当前摄像头: " + (cam.displayName || "未知摄像头")
} else {
return "未检测到摄像头"
}
}
color: "#ecf0f1"
}
绑定表达式:使用JavaScript代码块动态计算文本内容
Camera 组件分析
Camera {
id: camera
deviceId: cameraList.length > 0 ? cameraList[currentCameraIndex].deviceId : ""
position: cameraList.length > 0 ? cameraList[currentCameraIndex].position : Camera.UnspecifiedPosition
onError: {
var errorMsg = "摄像头错误: " + errorString + " (错误代码: " + error + ")"
console.log(errorMsg)
backend.showMessage(errorMsg)
}
}
错误处理机制:
errorString:人类可读的错误描述
error:错误代码,用于程序化处理
同时输出到控制台和用户界面
VideoOutput 中的图像捕获
VideoOutput {
id: videoOutput
// ... 其他属性
property var imageCapture: camera.imageCapture
function captureImage() {
if (camera.cameraStatus === Camera.ActiveStatus) {
backend.showMessage("正在拍照...")
camera.imageCapture.captureToLocation(backend.getSaveDirectory() + "/capture_" +
new Date().toLocaleString(Qt.locale(), "yyyyMMdd_hhmmss_zzz") + ".jpg")
} else {
backend.showMessage("错误: 请先启动摄像头")
}
}
}
关键函数:
camera.imageCapture:Qt 5.14中访问ImageCapture的方式
captureToLocation():直接保存到指定路径的异步方法
路径构建:与C++端保持一致的命名规则
状态覆盖层函数
qml
function getStatusText() {
switch(camera.cameraStatus) {
case Camera.ActiveStatus:
return "摄像头运行正常";
case Camera.StartingStatus:
return "摄像头启动中...";
case Camera.StoppingStatus:
return "摄像头停止中...";
case Camera.StandbyStatus:
return "摄像头待机中";
case Camera.LoadedStatus:
return "摄像头已加载";
case Camera.LoadingStatus:
return "摄像头加载中...";
case Camera.UnloadingStatus:
return "摄像头卸载中...";
case Camera.UnloadedStatus:
return "摄像头未加载";
case Camera.UnavailableStatus:
return "摄像头不可用";
default:
return "摄像头状态未知: " + camera.cameraStatus;
}
}
状态机处理:完整覆盖Camera的所有可能状态信号连接系统
// 连接后端信号
Connections {
target: backend
onStatusMessageChanged: {
var timestamp = new Date().toLocaleTimeString()
statusText.text = "[" + timestamp + "] " + message + "\n" + statusText.text
}
onImageSaved: {
photoPreview.source = "file:///" + filePath
}
}
// 连接摄像头图像捕获信号
Connections {
target: camera.imageCapture
onImageCaptured: {
photoPreview.source = preview
backend.onImageCaptured(preview)
}
onImageSaved: {
backend.onImageSaved(requestId, path)
backend.showMessage("照片已保存: " + path)
}
}
多目标连接:
后端状态消息:更新日志显示
图像保存完成:更新预览图片
摄像头捕获事件:处理预览和保存
组件初始化
Component.onCompleted: {
backend.showMessage("应用程序启动完成")
backend.showMessage("检测到 " + cameraList.length + " 个摄像头")
backend.showMessage("照片保存目录: " + backend.getSaveDirectory())
if (cameraList.length === 0) {
backend.showMessage("警告: 未检测到任何摄像头设备")
backend.showMessage("请检查摄像头连接和驱动程序")
} else {
for (var i = 0; i < cameraList.length; i++) {
var cam = cameraList[i]
backend.showMessage("摄像头 " + i + ": " + (cam.displayName || "未知") +
" (位置: " + getPositionName(cam.position) + ")")
}
}
}
启动流程:
应用启动通知
摄像头数量检测
目录信息显示
设备详情枚举
- main.cpp 详细分析
主函数结构
int main(int argc, char *argv[])
{
// 1. 应用程序属性设置
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// 2. 应用程序实例化
QGuiApplication app(argc, argv);
// 3. 应用程序元数据
app.setApplicationName("笔记本电脑摄像头诊断工具");
app.setApplicationVersion("1.1");
app.setOrganizationName("YourCompany");
// 4. 后端对象创建
Backend backend;
// 5. QML引擎初始化
QQmlApplicationEngine engine;
// 6. 上下文属性设置
engine.rootContext()->setContextProperty("backend", &backend);
// 7. QML文件加载
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// 8. 启动检查
if (engine.rootObjects().isEmpty()) {
return -1;
}
// 9. 调试信息输出
qDebug() << "照片保存目录:" << backend.getSaveDirectory();
// 10. 事件循环启动
return app.exec();
}
关键技术点分析
高DPI支持:
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
自动适配高分辨率显示器
解决在高DPI设备上界面元素过小的问题
应用程序元数据:
app.setApplicationName("笔记本电脑摄像头诊断工具");
app.setApplicationVersion("1.1");
app.setOrganizationName("YourCompany");
用于系统集成(如设置存储、关于对话框等)
遵循Qt应用程序最佳实践
上下文属性绑定:
engine.rootContext()->setContextProperty("backend", &backend);
将C++对象注入QML上下文
在QML中通过 backend 标识符访问
资源加载机制:
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
使用Qt资源系统(qrc)
编译时嵌入资源,避免运行时文件依赖
健全性检查:
if (engine.rootObjects().isEmpty()) {
return -1;
}
确保QML文件正确加载
防止空窗口应用程序
整体架构总结
数据流向
text
用户操作 → QML界面 → 信号发射 → C++后端处理 → 文件系统操作
↓
状态反馈 ← 信号发射 ← QML更新 ←───┘
二、所有源码
.pro文件添加
QT += quick quickcontrols2 multimedia
backend.h文件源码
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QString>
#include <QDebug>
#include <QDateTime>
#include <QImage>
#include <QDir>
#include <QStandardPaths>
class Backend : public QObject
{
Q_OBJECT
public:
explicit Backend(QObject *parent = nullptr) : QObject(parent) {
// 创建保存图片的目录
QString picturesPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
m_saveDir = QDir(picturesPath).filePath("CameraDiagnosticTool");
if (!QDir().exists(m_saveDir)) {
QDir().mkpath(m_saveDir);
}
qDebug() << "图片保存目录:" << m_saveDir;
}
public slots:
// 处理图像捕获事件
void onImageCaptured(const QString &previewPath) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "[" << timestamp << "] 图像已捕获,预览路径:" << previewPath;
emit statusMessageChanged("照片已捕获,显示预览");
}
// 处理图像保存事件
void onImageSaved(int requestId, const QString &filePath) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "[" << timestamp << "] 图像已保存,请求ID:" << requestId << "路径:" << filePath;
emit statusMessageChanged(QString("照片已保存到: %1").arg(filePath));
}
// 显示消息
void showMessage(const QString &message) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "[" << timestamp << "]" << message;
emit statusMessageChanged(message);
}
// 拍照并保存
Q_INVOKABLE void captureImage(const QImage &image) {
if (image.isNull()) {
emit statusMessageChanged("错误: 捕获的图像为空");
return;
}
// 生成文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz");
QString fileName = QString("capture_%1.jpg").arg(timestamp);
QString filePath = QDir(m_saveDir).filePath(fileName);
// 保存图片
if (image.save(filePath, "JPG", 90)) {
emit statusMessageChanged(QString("照片已保存: %1").arg(fileName));
emit imageSaved(filePath);
qDebug() << "图片保存成功:" << filePath;
} else {
emit statusMessageChanged("错误: 保存图片失败");
qDebug() << "图片保存失败:" << filePath;
}
}
// 获取保存目录
Q_INVOKABLE QString getSaveDirectory() const {
return m_saveDir;
}
signals:
void statusMessageChanged(const QString &message);
void imageSaved(const QString &filePath);
private:
QString m_saveDir;
};
#endif // BACKEND_H
main.qml文件源码
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtMultimedia 5.12
ApplicationWindow {
id: mainWindow
width: 800
height: 600
title: "笔记本电脑摄像头诊断工具"
visible: true
property string lastCapturedImage: ""
property var cameraList: QtMultimedia.availableCameras || []
property int currentCameraIndex: 0
// 背景
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#2c3e50" }
GradientStop { position: 1.0; color: "#34495e" }
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
spacing: 10
// 标题
Label {
text: "摄像头诊断工具"
Layout.alignment: Qt.AlignHCenter
color: "white"
font.pixelSize: 24
font.bold: true
}
// 摄像头信息区域
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 100
color: "#34495e"
radius: 5
border.color: "#7f8c8d"
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
Label {
text: "摄像头信息:"
color: "white"
font.bold: true
}
Label {
text: "检测到的摄像头数量: " + cameraList.length
color: "#ecf0f1"
}
Label {
text: {
if (cameraList.length > 0) {
var cam = cameraList[currentCameraIndex]
return "当前摄像头: " + (cam.displayName || "未知摄像头")
} else {
return "未检测到摄像头"
}
}
color: "#ecf0f1"
}
// 摄像头选择(如果有多个摄像头)
ComboBox {
id: cameraSelector
Layout.fillWidth: true
model: cameraList
visible: cameraList.length > 1
textRole: "displayName"
onCurrentIndexChanged: {
if (camera.cameraStatus === Camera.ActiveStatus) {
camera.stop()
}
currentCameraIndex = currentIndex
backend.showMessage("已选择摄像头: " + currentText)
}
}
}
}
// 摄像头预览区域
Rectangle {
id: previewContainer
Layout.fillWidth: true
Layout.fillHeight: true
color: "#1a1a1a"
border.color: camera.cameraStatus === Camera.ActiveStatus ? "#27ae60" : "#e74c3c"
border.width: 2
radius: 8
Camera {
id: camera
deviceId: cameraList.length > 0 ? cameraList[currentCameraIndex].deviceId : ""
position: cameraList.length > 0 ? cameraList[currentCameraIndex].position : Camera.UnspecifiedPosition
onError: {
var errorMsg = "摄像头错误: " + errorString + " (错误代码: " + error + ")"
console.log(errorMsg)
backend.showMessage(errorMsg)
}
onCameraStateChanged: {
var stateMsg = "摄像头状态: "
switch(camera.cameraState) {
case Camera.ActiveState:
stateMsg += "运行中";
break;
case Camera.LoadedState:
stateMsg += "已加载";
break;
case Camera.UnloadedState:
stateMsg += "未加载";
break;
default:
stateMsg += "未知状态";
}
backend.showMessage(stateMsg)
}
onCameraStatusChanged: {
var statusMsg = "摄像头状态码: " + camera.cameraStatus
backend.showMessage(statusMsg)
}
}
VideoOutput {
id: videoOutput
anchors.fill: parent
anchors.margins: 5
source: camera
fillMode: VideoOutput.PreserveAspectCrop
focus: visible
// 在 Qt 5.x 中,ImageCapture 需要这样使用
property var imageCapture: camera.imageCapture
function captureImage() {
if (camera.cameraStatus === Camera.ActiveStatus) {
backend.showMessage("正在拍照...")
camera.imageCapture.captureToLocation(backend.getSaveDirectory() + "/capture_" +
new Date().toLocaleString(Qt.locale(), "yyyyMMdd_hhmmss_zzz") + ".jpg")
} else {
backend.showMessage("错误: 请先启动摄像头")
}
}
}
// 状态覆盖层
Rectangle {
anchors.fill: parent
color: "#80000000"
visible: camera.cameraStatus !== Camera.ActiveStatus
Column {
anchors.centerIn: parent
spacing: 10
Label {
text: getStatusText()
color: "white"
font.pixelSize: 16
anchors.horizontalCenter: parent.horizontalCenter
}
BusyIndicator {
running: camera.cameraStatus === Camera.StartingStatus
anchors.horizontalCenter: parent.horizontalCenter
visible: running
}
}
}
function getStatusText() {
switch(camera.cameraStatus) {
case Camera.ActiveStatus:
return "摄像头运行正常";
case Camera.StartingStatus:
return "摄像头启动中...";
case Camera.StoppingStatus:
return "摄像头停止中...";
case Camera.StandbyStatus:
return "摄像头待机中";
case Camera.LoadedStatus:
return "摄像头已加载";
case Camera.LoadingStatus:
return "摄像头加载中...";
case Camera.UnloadingStatus:
return "摄像头卸载中...";
case Camera.UnloadedStatus:
return "摄像头未加载";
case Camera.UnavailableStatus:
return "摄像头不可用";
default:
return "摄像头状态未知: " + camera.cameraStatus;
}
}
}
// 控制按钮区域
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 20
Button {
id: startButton
text: "启动摄像头"
Layout.preferredWidth: 140
Layout.preferredHeight: 40
background: Rectangle {
color: parent.down ? "#27ae60" :
parent.hovered ? "#2ecc71" : "#27ae60"
radius: 5
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 14
font.bold: true
}
onClicked: {
backend.showMessage("正在启动摄像头...")
console.log("尝试启动摄像头,设备ID:", camera.deviceId)
camera.start()
}
}
Button {
id: stopButton
text: "停止摄像头"
Layout.preferredWidth: 140
Layout.preferredHeight: 40
background: Rectangle {
color: parent.down ? "#c0392b" :
parent.hovered ? "#e74c3c" : "#c0392b"
radius: 5
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 14
font.bold: true
}
onClicked: {
camera.stop()
backend.showMessage("摄像头已停止")
}
}
// 拍照按钮
Button {
id: captureButton
text: "拍照"
Layout.preferredWidth: 140
Layout.preferredHeight: 40
enabled: camera.cameraStatus === Camera.ActiveStatus
background: Rectangle {
color: parent.down ? "#f39c12" :
parent.hovered ? "#f1c40f" :
parent.enabled ? "#f39c12" : "#95a5a6"
radius: 5
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 14
font.bold: true
}
onClicked: {
videoOutput.captureImage()
}
}
Button {
id: refreshButton
text: "刷新摄像头列表"
Layout.preferredWidth: 140
Layout.preferredHeight: 40
background: Rectangle {
color: parent.down ? "#2980b9" :
parent.hovered ? "#3498db" : "#2980b9"
radius: 5
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 14
font.bold: true
}
onClicked: {
// 强制刷新摄像头列表
cameraList = QtMultimedia.availableCameras || []
backend.showMessage("已刷新摄像头列表,检测到 " + cameraList.length + " 个摄像头")
}
}
}
// 图片预览区域
Rectangle {
id: previewSection
Layout.fillWidth: true
Layout.preferredHeight: 150
color: "transparent"
visible: photoPreview.source !== ""
Label {
text: "最新照片预览:"
color: "white"
font.bold: true
anchors.top: parent.top
anchors.left: parent.left
}
RowLayout {
anchors.fill: parent
anchors.topMargin: 25
spacing: 10
// 图片预览
Image {
id: photoPreview
Layout.preferredWidth: 120
Layout.preferredHeight: 120
fillMode: Image.PreserveAspectFit
cache: false
}
// 照片信息
ColumnLayout {
Layout.fillWidth: true
spacing: 5
Label {
text: "尺寸: " + (photoPreview.sourceSize.width + "x" + photoPreview.sourceSize.height)
color: "#ecf0f1"
font.pixelSize: 12
}
Label {
text: "保存目录:"
color: "#ecf0f1"
font.pixelSize: 12
font.bold: true
}
Label {
text: backend.getSaveDirectory()
color: "#bdc3c7"
font.pixelSize: 10
wrapMode: Text.Wrap
Layout.maximumWidth: 400
}
Button {
text: "打开保存文件夹"
Layout.preferredWidth: 120
Layout.preferredHeight: 30
visible: Qt.platform.os !== "android" && Qt.platform.os !== "ios"
background: Rectangle {
color: parent.down ? "#16a085" :
parent.hovered ? "#1abc9c" : "#16a085"
radius: 3
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 11
}
onClicked: {
Qt.openUrlExternally("file:///" + backend.getSaveDirectory())
}
}
}
}
}
// 诊断信息区域
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 80
color: "#2c3e50"
radius: 5
border.color: "#7f8c8d"
ScrollView {
anchors.fill: parent
anchors.margins: 5
TextArea {
id: statusText
readOnly: true
wrapMode: Text.Wrap
color: "#ecf0f1"
font.pixelSize: 11
background: null
text: "应用程序已启动\n" +
"正在初始化摄像头系统..."
}
}
}
}
// 连接后端信号
Connections {
target: backend
onStatusMessageChanged: {
var timestamp = new Date().toLocaleTimeString()
statusText.text = "[" + timestamp + "] " + message + "\n" + statusText.text
}
onImageSaved: {
photoPreview.source = "file:///" + filePath
}
}
// 连接摄像头图像捕获信号
Connections {
target: camera.imageCapture
onImageCaptured: {
photoPreview.source = preview
backend.onImageCaptured(preview)
}
onImageSaved: {
backend.onImageSaved(requestId, path)
backend.showMessage("照片已保存: " + path)
}
}
// 应用启动时自动检测
Component.onCompleted: {
backend.showMessage("应用程序启动完成")
backend.showMessage("检测到 " + cameraList.length + " 个摄像头")
backend.showMessage("照片保存目录: " + backend.getSaveDirectory())
if (cameraList.length === 0) {
backend.showMessage("警告: 未检测到任何摄像头设备")
backend.showMessage("请检查摄像头连接和驱动程序")
} else {
for (var i = 0; i < cameraList.length; i++) {
var cam = cameraList[i]
backend.showMessage("摄像头 " + i + ": " + (cam.displayName || "未知") +
" (位置: " + getPositionName(cam.position) + ")")
}
}
}
function getPositionName(position) {
switch(position) {
case Camera.BackFace: return "后置";
case Camera.FrontFace: return "前置";
case Camera.UnspecifiedPosition: return "未指定";
default: return "未知";
}
}
// 应用退出时停止摄像头
onClosing: {
camera.stop()
}
}
main.cpp文件源码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QIcon>
#include "backend.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// 设置应用程序信息
app.setApplicationName("笔记本电脑摄像头诊断工具");
app.setApplicationVersion("1.1");
app.setOrganizationName("YourCompany");
// 注册后端类
Backend backend;
QQmlApplicationEngine engine;
// 将后端对象暴露给QML
engine.rootContext()->setContextProperty("backend", &backend);
// 加载QML文件
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty()) {
return -1;
}
// 显示保存目录信息
qDebug() << "照片保存目录:" << backend.getSaveDirectory();
return app.exec();
}
三、效果演示
点击启动摄像头,进行拍照,照片自动保存,同时具有预览功能。
