一、项目整体设计
1.1 我们要做什么?
创建一个视频播放器:
- 界面:用 QML 做(好看)
- 视频处理:用 OpenCV 做(强大)
- 连接桥梁:用 C++ 做(高效)
1.2 技术选型
| Qt QML | 用户界面 | 界面漂亮,开发快 |
| OpenCV | 视频处理 | 功能强大,跨平台 |
| C++ | 核心逻辑 | 性能好,连接 QML 和 OpenCV |
| CMake | 项目构建 | 跨平台,管理依赖 |
二、项目环境搭建
2.1 安装必要的软件
- Windows:MinGW 或 Visual Studio
- macOS:Xcode Command Line Tools
- Linux:GCC
2.2 目录结构准备
video_player/
├── src/ # 源代码
│ ├── main.cpp # 程序入口
│ ├── main.qml # QML界面
│ ├── XVideo.cpp # 视频组件
│ ├── XVideo.h # 视频组件头文件
│ └── CMakeLists.txt # 项目配置
├── lib/ # 第三方库
│ └── opencv/
│ ├── mingw/ # MinGW版本
│ └── msvc/ # VS版本
└── bin/ # 输出目录
三、第一步:创建基础项目
3.1 创建 CMakeLists.txt
# CMakeLists.txt
# 1. 告诉CMake我们需要的最低版本
cmake_minimum_required(VERSION 3.20)
# 2. 给项目起名字
project(video_player)
# 3. 查找并加载 Qt
find_package(Qt6 COMPONENTS Quick REQUIRED)
# 4. 自动处理Qt的特殊功能
set(CMAKE_AUTOMOC ON) # 自动处理信号槽
set(CMAKE_AUTORCC ON) # 自动处理资源文件
# 5. 创建可执行文件
add_executable(${PROJECT_NAME} main.cpp)
# 6. 链接Qt库
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Quick)
3.2 创建 main.cpp
// main.cpp – 程序入口
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
// 1. 创建Qt应用程序
QGuiApplication app(argc, argv);
// 2. 创建QML引擎
QQmlApplicationEngine engine;
// 3. 加载QML文件
engine.load(QUrl("qrc:/main.qml"));
// 4. 检查是否加载成功
if (engine.rootObjects().isEmpty())
return –1; // 失败
// 5. 运行程序
return app.exec();
}
3.3 创建 main.qml
// main.qml – 界面文件
import QtQuick
// 创建一个窗口
Window {
// 窗口大小
width: 800
height: 600
// 窗口标题
title: "视频播放器"
// 让窗口可见(重要!)
visible: true
// 在窗口中添加文字
Text {
text: "Hello, Video Player!"
font.pixelSize: 30
anchors.centerIn: parent
}
}
3.4 创建资源文件(qml.qrc)
四、第二步:添加 OpenCV 支持
4.1 修改 CMakeLists.txt
# CMakeLists.txt – 完整版
# 基础配置
cmake_minimum_required(VERSION 3.20)
project(video_player)
# 1. 判断当前编译器类型
if(MINGW)
message("使用 MinGW 编译器")
set(TOOL "mingw") # 用于区分输出目录
set(OPENCVPATH "${CMAKE_CURRENT_LIST_DIR}/../lib/opencv/mingw")
elseif(MSVC)
message("使用 MSVC 编译器")
set(TOOL "msvc")
set(OPENCVPATH "${CMAKE_CURRENT_LIST_DIR}/../lib/opencv/msvc")
else()
message(FATAL_ERROR "不支持的编译器")
endif()
# 2. 加载 Qt
find_package(Qt6 COMPONENTS Quick REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# 3. 加载 OpenCV
find_package(OpenCV REQUIRED PATHS ${OPENCVPATH})
# 4. 设置输出目录
set(RUNTIME "${CMAKE_CURRENT_LIST_DIR}/../bin/${TOOL}")
set_target_properties(${PROJECT_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${RUNTIME}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${RUNTIME}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${RUNTIME}"
)
# 5. 添加源文件
add_executable(${PROJECT_NAME}
main.cpp
XVideo.cpp
qml.qrc
)
# 6. 链接库
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt6::Quick
${OpenCV_LIBS}
)
# 7. 添加头文件路径
include_directories(${OpenCV_INCLUDE_DIRS})
五、第三步:创建视频组件 XVideo
5.1 创建 XVideo.h
// XVideo.h – 视频组件头文件
#ifndef XVIDEO_H
#define XVIDEO_H
// 包含必要的头文件
#include <QQuickPaintedItem> // QML绘制组件基类
#include <QImage> // Qt图像类
#include <QTimer> // 定时器
#include <opencv2/opencv.hpp> // OpenCV
// 创建视频组件类
class XVideo : public QQuickPaintedItem
{
Q_OBJECT // 必须添加这个宏!
QML_ELEMENT // 让QML能识别这个类
// 定义属性(QML可以访问)
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(bool playing READ playing WRITE setPlaying NOTIFY playingChanged)
public:
// 构造函数
XVideo(QQuickItem *parent = nullptr);
// 析构函数
~XVideo();
// 重写绘制函数(必须实现)
void paint(QPainter *painter) override;
// 属性访问函数
QString source() const; // 获取视频路径
void setSource(const QString &source); // 设置视频路径
bool playing() const; // 是否正在播放
void setPlaying(bool playing); // 设置播放状态
// QML可以调用的函数
Q_INVOKABLE void play(); // 播放
Q_INVOKABLE void pause(); // 暂停
Q_INVOKABLE void stop(); // 停止
signals:
// 信号(事件)
void sourceChanged(); // 视频路径改变
void playingChanged(); // 播放状态改变
void errorOccurred(const QString &message); // 错误发生
void readEnd(); // 视频播放结束
private slots:
// 槽函数(响应事件)
void updateFrame(); // 更新视频帧
private:
// 私有成员变量
cv::VideoCapture m_capture; // OpenCV视频捕获对象
cv::Mat m_frame; // 当前视频帧
QImage m_image; // Qt图像(用于显示)
QTimer m_timer; // 定时器(控制播放速度)
QString m_source; // 视频文件路径
bool m_playing = false; // 播放状态
int m_fps = 30; // 视频帧率
};
#endif // XVIDEO_H
5.2 创建 XVideo.cpp
// XVideo.cpp – 视频组件实现
#include "XVideo.h"
#include <QDebug> // 调试输出
#include <QPainter> // 绘制
// 构造函数
XVideo::XVideo(QQuickItem *parent)
: QQuickPaintedItem(parent) // 调用父类构造函数
{
qDebug() << "XVideo 组件被创建";
// 连接定时器和更新函数
// 当定时器超时时,调用 updateFrame
connect(&m_timer, &QTimer::timeout, this, &XVideo::updateFrame);
}
// 析构函数
XVideo::~XVideo()
{
// 释放视频捕获资源
if (m_capture.isOpened()) {
m_capture.release();
}
}
// 绘制函数 – 在界面上显示视频
void XVideo::paint(QPainter *painter)
{
// 如果没有图像,就不绘制
if (m_image.isNull()) {
return;
}
// 绘制图像
// 参数:位置和大小,图像
QRect rect(0, 0, width(), height());
painter->drawImage(rect, m_image);
}
// 获取视频路径
QString XVideo::source() const
{
return m_source;
}
// 设置视频路径
void XVideo::setSource(const QString &source)
{
// 如果新路径和旧路径一样,就不做任何事
if (m_source == source) {
return;
}
// 停止当前视频
stop();
// 更新路径
m_source = source;
// 如果路径不为空,尝试打开视频
if (!source.isEmpty()) {
// 使用OpenCV打开视频
m_capture.open(source.toStdString());
// 检查是否打开成功
if (!m_capture.isOpened()) {
emit errorOccurred("无法打开视频: " + source);
return;
}
// 获取视频帧率
m_fps = m_capture.get(cv::CAP_PROP_FPS);
if (m_fps <= 0) {
m_fps = 30; // 默认30帧
}
qDebug() << "打开视频成功:" << source << ",帧率:" << m_fps;
// 发出信号,告诉QML视频路径改变了
emit sourceChanged();
}
}
// 是否正在播放
bool XVideo::playing() const
{
return m_playing;
}
// 设置播放状态
void XVideo::setPlaying(bool playing)
{
// 如果状态没变,就不做任何事
if (m_playing == playing) {
return;
}
// 更新状态
m_playing = playing;
// 根据状态执行操作
if (playing) {
play(); // 播放
} else {
pause(); // 暂停
}
// 发出信号
emit playingChanged();
}
// 播放视频
void XVideo::play()
{
// 检查视频是否已打开
if (!m_capture.isOpened()) {
emit errorOccurred("请先打开视频文件");
return;
}
// 设置播放状态
m_playing = true;
// 启动定时器
// 1000ms / 帧率 = 每帧间隔时间
m_timer.start(1000 / m_fps);
qDebug() << "开始播放视频";
}
// 暂停视频
void XVideo::pause()
{
m_playing = false;
m_timer.stop(); // 停止定时器
qDebug() << "暂停播放";
}
// 停止视频
void XVideo::stop()
{
// 先暂停
pause();
// 回到视频开头
if (m_capture.isOpened()) {
m_capture.set(cv::CAP_PROP_POS_FRAMES, 0);
}
// 清空图像
m_image = QImage();
// 通知界面重绘
update();
qDebug() << "停止播放";
}
// 更新视频帧
void XVideo::updateFrame()
{
// 检查视频是否已打开
if (!m_capture.isOpened()) {
return;
}
// 读取下一帧
if (!m_capture.read(m_frame) || m_frame.empty()) {
// 视频播放完了,回到开头
m_capture.set(cv::CAP_PROP_POS_FRAMES, 0);
// 重新读取第一帧
if (!m_capture.read(m_frame)) {
// 读取失败,发出结束信号
emit readEnd();
return;
}
}
// 将OpenCV的BGR格式转换为RGB格式
// OpenCV默认是BGR,Qt需要RGB
cv::Mat rgbFrame;
cv::cvtColor(m_frame, rgbFrame, cv::COLOR_BGR2RGB);
// 创建QImage
// 参数:数据指针,宽度,高度,每行字节数,格式
m_image = QImage(rgbFrame.data,
rgbFrame.cols,
rgbFrame.rows,
rgbFrame.step,
QImage::Format_RGB888);
// 通知界面重绘
update();
}
六、第四步:修改 main.cpp 注册组件
// main.cpp – 完整版
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "XVideo.h" // 包含我们的视频组件
int main(int argc, char *argv[])
{
// 1. 创建Qt应用程序
QGuiApplication app(argc, argv);
// 2. 创建QML引擎
QQmlApplicationEngine engine;
// 3. 注册自定义组件到QML
// 参数说明:
// – "XVideo":模块名(在QML中import的名字)
// – 1, 0:版本号(主版本.次版本)
// – "XVideo":QML中的类型名
qmlRegisterType<XVideo>("XVideo", 1, 0, "XVideo");
// 4. 加载QML文件
engine.load(QUrl("qrc:/main.qml"));
// 5. 检查是否加载成功
if (engine.rootObjects().isEmpty()) {
return –1;
}
// 6. 运行程序
return app.exec();
}
七、第五步:完善 QML 界面
// main.qml – 完整界面
import QtQuick
import QtQuick.Controls // 按钮等控件
import XVideo 1.0 // 导入我们的视频组件
// 主窗口
Window {
id: window
width: 1280
height: 720
visible: true
title: "视频播放器 – OpenCV + QML"
// 背景颜色
color: "#333333"
// 视频播放组件
XVideo {
id: videoPlayer
anchors.fill: parent // 填满整个窗口
// 当发生错误时
onErrorOccurred: function(message) {
errorDialog.text = message
errorDialog.open()
}
// 当视频播放结束时
onReadEnd: {
console.log("视频播放结束")
videoPlayer.playing = false
}
}
// 控制面板
Rectangle {
id: controlPanel
width: parent.width
height: 100
color: "#80000000" // 半透明黑色
anchors.bottom: parent.bottom
Row {
spacing: 20
anchors.centerIn: parent
// 播放/暂停按钮
Button {
text: videoPlayer.playing ? "⏸ 暂停" : "▶ 播放"
onClicked: {
videoPlayer.playing = !videoPlayer.playing
}
background: Rectangle {
color: "#4CAF50"
radius: 5
}
font.pixelSize: 16
implicitWidth: 100
implicitHeight: 40
}
// 停止按钮
Button {
text: "⏹ 停止"
onClicked: videoPlayer.stop()
background: Rectangle {
color: "#F44336"
radius: 5
}
font.pixelSize: 16
implicitWidth: 100
implicitHeight: 40
}
// 打开文件按钮
Button {
text: "📁 打开视频"
onClicked: fileDialog.open()
background: Rectangle {
color: "#2196F3"
radius: 5
}
font.pixelSize: 16
implicitWidth: 120
implicitHeight: 40
}
// 当前状态显示
Text {
text: videoPlayer.playing ? "正在播放…" : "已暂停"
color: "white"
font.pixelSize: 16
anchors.verticalCenter: parent.verticalCenter
}
}
}
// 文件选择对话框
FileDialog {
id: fileDialog
title: "选择视频文件"
nameFilters: [
"视频文件 (*.mp4 *.avi *.mov *.mkv)",
"所有文件 (*.*)"
]
onAccepted: {
// 设置视频路径并播放
videoPlayer.source = selectedFile
videoPlayer.play()
}
}
// 错误提示对话框
Dialog {
id: errorDialog
title: "错误"
modal: true
standardButtons: Dialog.Ok
property alias text: errorText.text
Text {
id: errorText
wrapMode: Text.WordWrap
}
}
}
八、第六步:编译和运行
8.1 使用 Qt Creator
8.2 使用命令行
# 进入项目目录
cd video_player/src
# 创建构建目录
mkdir build
cd build
# 配置项目(MinGW)
cmake .. -G "MinGW Makefiles"
# 或配置项目(MSVC)
cmake .. -G "Visual Studio 17 2022"
# 编译项目
cmake –build .
# 运行程序
cd ../../bin/mingw # 或 msvc
./video_player
九、第七步:项目发布
9.1 为什么要发布?
- 用户没有 Qt 和 OpenCV 环境
- 需要把程序发给别人使用
- 制作安装包
9.2 Windows 发布步骤
方法一:手动复制
方法二:使用 windeployqt(推荐)
创建批处理文件 deploy.bat:
@echo off
echo 开始发布程序…
REM 设置路径
set QT_PATH=F:\\Qt\\6.6.2\\mingw_64
set OPENCV_PATH=..\\lib\\opencv\\mingw\\bin
set OUTPUT=..\\dist
REM 创建输出目录
if not exist %OUTPUT% mkdir %OUTPUT%
REM 复制可执行文件
copy .\\bin\\mingw\\video_player.exe %OUTPUT%
REM 复制 OpenCV DLL
copy %OPENCV_PATH%\\*.dll %OUTPUT%
REM 使用 windeployqt 复制 Qt 依赖
%QT_PATH%\\bin\\windeployqt.exe %OUTPUT%\\video_player.exe –qmldir .
echo 发布完成!
pause
9.3 macOS 发布
# 使用 macdeployqt
macdeployqt video_player.app -qmldir=.
9.4 Linux 发布
# 使用 linuxdeployqt
linuxdeployqt video_player -qmldir=.
十、常见问题解决
问题1:找不到 OpenCV
错误信息:Could NOT find OpenCV 解决方法:
# 1. 检查路径是否正确
message("OpenCV路径: ${OPENCVPATH}")
# 2. 确保 OpenCVConfig.cmake 文件存在
# 3. 清理 build 目录重新编译
问题2:QML 导入失败
错误信息:module "XVideo" is not installed 解决方法:
问题3:视频无法播放
可能原因:
// 在 QML 中添加错误处理
onErrorOccurred: function(message) {
console.error("错误:", message)
}
问题4:画面卡顿
优化方法:
十一、扩展功能
11.1 添加进度条
// 在控制面板中添加
Slider {
width: 400
value: 0
onValueChanged: {
// 跳转到指定位置
}
}
11.2 添加音量控制
Slider {
width: 100
value: 0.5
onValueChanged: {
// 设置音量
}
}
11.3 添加全屏功能
Button {
text: "全屏"
onClicked: {
if (window.visibility === Window.FullScreen) {
window.showNormal()
} else {
window.showFullScreen()
}
}
}
11.4 添加视频滤镜
// 在 XVideo.cpp 中添加
void XVideo::applyFilter(const QString &filterType)
{
if (filterType == "灰度") {
cv::cvtColor(m_frame, m_frame, cv::COLOR_BGR2GRAY);
} else if (filterType == "模糊") {
cv::GaussianBlur(m_frame, m_frame, cv::Size(5, 5), 0);
}
}
十二、总结
12.1 学到的知识
12.2 项目亮点
- ✅ 界面美观,响应迅速
- ✅ 代码结构清晰,易于维护
- ✅ 跨平台支持
- ✅ 易于扩展新功能






