1. 计算机视觉的基本概念
计算机视觉是人工智能的一个重要分支,旨在让计算机能够“看”并理解图像和视频内容。
什么是QT?
QT是国外一家公司研发的,它使用C++语言来实现图形界面GUI(我们程序运行起来的的时候的窗口,按钮,文本框)。目前QT是C++语言中主流的GUI开发工具,我们很多软件都是QT写的。
什么是opencv?
是用C/C++语言编写的机器视觉软件库,人们通过这个opencv库来实现人脸识别、车辆识别等等其它应用。目前opencv是主流的机器视觉库。90%的机器视觉软件都是用它写。
qt和opencv 是两个不同的软件,要想让他们配合使用,就必须让QT知道,opencv安装在电脑的哪个位置
Qt与OpenCV的结合
Qt是一个跨平台的C++图形用户界面库。在这次活动中,我们学习了如何将OpenCV与Qt结合,开发一个简单的图像和视频处理应用。通过将OpenCV处理后的图像数据 Mat 转换为QImage,我们可以在Qt的界面组件中显示图像。
QImage img(camerImg.data, camerImg.cols, camerImg.rows, camerImg.step, QImage::Format_RGB888); ui->label_3->setPixmap(QPixmap::fromImage(img.scaled(ui->label_3->size(), Qt::KeepAspectRatio)));//调整为适合显示区域的尺寸,并将其设置到Qt的界面组件中。
信号槽机制
A.在所有的GUI界面中,我们都要处理按钮响应,文本框输入显示等等这些功能。每个GUI开发框架都有对应的技术来实现。在QT中它叫信号和槽: qt在设计的时候都会事先对所有的文本框,按钮都会设计好 键盘按键,鼠标点击这些事件。它在QT中叫信号,也就是说如果你按钮按下去了,系统就会触发有个叫cliecked的信号。那我们要做的事这些信号触发之后要具体做出什么响应,这个叫槽函数。
Qt 的信号和槽机制是其核心的事件驱动编程模型之一,它使得对象间的通信变得非常简单和高效。下面详细介绍信号和槽机制的工作原理、用法和特点。
信号和槽的基本概念
信号(Signal):
-
定义: 信号是由对象发出的消息,用于通知其他对象某个事件的发生。信号的定义在类中声明,不包含具体实现。
-
触发: 当对象的状态发生变化或某个特定事件发生时,可以发射信号。发射信号的语法是
emit signalName(args...)
,其中emit
是一个关键字,用于表示信号的发射。
槽(Slot):
-
定义: 槽是一个成员函数,用于响应信号的发射。槽的定义在类中声明并实现,用于处理信号发出的事件。
-
连接: 信号和槽通过连接操作建立关联,使得当信号发射时,槽函数被自动调用。
如何使用信号和槽
-
声明信号和槽:
-
信号: 在类的
signals
部分声明。 -
槽: 在类的
public slots
或private slots
部分声明。槽是普通的成员函数,可以接收信号发出的参数。
-
-
实现槽函数:
-
槽函数是处理信号的函数,可以对信号传递的参数进行处理。
-
-
连接信号和槽:
-
使用
QObject::connect
方法将信号和槽连接起来。当信号发射时,对应的槽函数被调用。
-
-
发射信号:
-
使用
emit
关键字发射信号,触发槽函数的调用。
-
代码示例
定义信号和槽
class MyClass : public QObject { Q_OBJECT public: MyClass() {} signals: void mySignal(int value); public slots: void mySlot(int value) { qDebug() << "Signal received with value:" << value; } };
连接信号和槽
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyClass obj1, obj2; // 连接信号和槽 QObject::connect(&obj1, SIGNAL(mySignal(int)), &obj2, SLOT(mySlot(int))); // 发射信号 emit obj1.mySignal(42); return a.exec(); }
信号槽机制的特点
-
解耦:
-
信号和槽机制使得发送信号的对象和接收信号的对象不需要直接了解彼此的存在,从而实现了松耦合。
-
-
自动连接:
-
当信号发射时,Qt 会自动调用连接的槽函数,无需手动调用。
-
-
多对多连接:
-
一个信号可以连接多个槽,多个信号也可以连接到一个槽。
-
-
类型安全:
-
信号和槽机制在编译时检查参数的类型是否匹配,保证了类型安全。
-
-
线程安全:
-
Qt 提供了线程间信号和槽机制,可以在不同线程间进行通信,支持线程安全的操作。
-
信号槽的高级特性
-
函数指针语法:
-
Qt 5 及更高版本提供了基于函数指针的连接语法,更加类型安全且易于使用。
connect(&obj1, &MyClass::mySignal, &obj2, &MyClass::mySlot);
-
-
Lambda 表达式:
-
Qt 5 引入了对 lambda 表达式的支持,可以直接在连接时定义槽函数,简化代码。
connect(&obj1, &MyClass::mySignal, [](int value) { qDebug() << "Lambda received value:" << value; });
-
-
信号参数:
-
信号可以携带参数,槽函数需要与信号参数一致的参数类型。
-
-
Qt 的元对象系统:
-
信号和槽机制基于 Qt 的元对象系统,使得运行时能够动态检查信号和槽的匹配情况。
-
总结
Qt 的信号和槽机制提供了一种灵活和高效的对象间通信方式,简化了事件处理和状态更新的实现。通过信号和槽,开发者可以轻松地实现不同对象间的互动,提升代码的模块化和可维护性。
2. 使用OpenCV进行图像处理
OpenCV是一个强大的开源计算机视觉库。我们学习了如何使用OpenCV进行各种图像处理任务,
untitled图像处理
#include <iostream> #include<opencv2/opencv.hpp> using namespace std; using namespace cv; //图片磨砂效果 RNG rng; int randonNum; void glass(Mat &mat) { //1.所有图像处理,其实都是基于每个图片的像素进行处理的。 //2.计算机在处理像素的时候RGB三元色是图像的基本元素 //3.一张图片是由宽和高组成的,实际上就是一个二维数组,可以通过两层for循环遍历每个像素 //4.图片的磨砂效果原理就是把一个像素的RGB值,用临近像素的RGB进行随机替换 for(int i=0;i<mat.rows-10;i++){ for(int j =0;j<mat.cols-10;j++){ randonNum=rng.uniform(0,10);//产生0-10之间的随机数 //Mat对象提供at方法,只要给定ij,通过at方法就能快速定位到具体像素中访问 //vec3b vector r g b mat.at<Vec3b>(i,j)[0]=mat.at<Vec3b>(i+randonNum,j+randonNum)[0]; mat.at<Vec3b>(i,j)[1]=mat.at<Vec3b>(i+randonNum,j+randonNum)[1]; mat.at<Vec3b>(i,j)[2]=mat.at<Vec3b>(i+randonNum,j+randonNum)[2]; } } } int main() { Mat img,blurImg,grayImg,threshold_img; img = imread("d://OIP-C.jpg"); imshow("ms",img); glass(img); imshow("glass img",img); blur(img,blurImg,Size(50,50)); imshow("blur Img",blurImg); cvtColor(img,grayImg,CV_BGR2GRAY);//实现图片黑白化,实现颜色空间转换的函数 imshow("gray img",grayImg); threshold(grayImg,threshold_img,50,255,THRESH_BINARY); imshow("threshold img",threshold_img); VideoCapture capture(0); if(!capture.isOpened()){ cout<<"open camera error"<<endl; return -1; } Mat frame,blurFrame,grayFrame,thersholdFrame; while(capture.read(frame)){ imshow("video",frame); blur(frame,blurFrame,Size(50,50)); imshow("blur Video",blurFrame); cvtColor(frame,grayFrame,COLOR_BGR2GRAY); imshow("gray video",grayFrame); threshold(grayFrame,thersholdFrame,150,255,THRESH_BINARY); imshow("threshold video",thersholdFrame); waitKey(10);//等待用户按键10ms } cout << "Hello World!" << endl; return 0; }
处理流程
-
加载和显示:
-
读取图像文件,显示原始图像。
-
-
图像效果应用:
-
磨砂效果: 模糊图像以创建磨砂效果。
-
模糊处理: 使用均值模糊减少图像细节和噪声。
-
灰度处理: 转换为灰度图像以简化信息。
-
阈值处理: 对灰度图像进行二值化处理,突出图像中的主要特征。
-
-
实时视频处理:
-
从摄像头捕获实时视频帧,对每帧应用相同的图像处理操作,并显示处理结果。
A.首先直接用opencv来实现视频的显示 B.再结合QT的窗口显示视频
视频是每秒连续播放不同的图片,一般来说一秒播放24张图片,我们人眼看起来就是视频。根据这个原理,我们的窗口要连续不断的显示不同的图片就能实现动态效果播视频了,那么QT中就必须有个功能就是能够每隔一段时间自动显示图片的功能,这个一般叫定时器。所有的开发库,基本上都会提供定时器功能。在QT中也是一样的 QTimer,定时器要想正常运作就要使用信号和槽,定时器时间到了就会产生一个timeout信号,我们要做的是手工设计一个槽函数,来和这个timeout信号进行捆绑。
思路:我们只需要打开定时器,让定时器每隔一段时间执行下显示图片的到label上就可以了。 我们的一个视频或者一个摄像头,都必须和一个定时器配合使用。那么我们实际智能监控摄像头有16个之多。我们就不可能定义16个定时器很不方便。后面我们就要使用线程来优化我们的代码。
-
untitlednew图像处理
实现了一个简单的图像选择和处理功能,主要包括以下几个步骤:
-
图像选择:
-
通过
QFileDialog::getOpenFileName
函数弹出一个文件选择对话框,让用户选择一张图片。支持的图片格式包括PNG、JPG、JPEG、WEBP和BMP。 -
如果用户没有选择文件或选择了空文件,函数返回,不做任何处理。
-
-
图像读取:
-
使用OpenCV的
imread
函数读取选中的图片文件。filename.toLatin1().data()
将Qt的QString
类型转换为标准的C字符串,供OpenCV使用。 -
如果图像读取失败(例如文件不存在或文件格式不支持),函数返回,不做任何处理。
-
-
颜色空间转换:
-
因为OpenCV默认使用BGR颜色空间,而Qt的
QImage
使用RGB颜色空间,因此需要使用cvtColor
函数将图像从BGR转换为RGB。
-
-
图像模糊处理:
-
使用OpenCV的
blur
函数对图像进行模糊处理,这里使用了一个50x50的卷积核大小。这可以用于演示图像处理中的卷积操作,也可以用于某些图像滤镜效果的实现。
-
-
图像复制:
-
使用
clone
函数对原始图像和模糊图像进行深度复制。深度复制确保原始图像和模糊图像在后续操作中不受影响。
-
-
图像显示:
-
将OpenCV的
Mat
对象转换为Qt的QImage
对象,以便在Qt界面上显示。使用QImage::Format_RGB888
格式指定图像的格式为RGB。 -
将
QImage
转换为QPixmap
并设置到label
控件上显示。distImage.scaled()
和blurDistImage.scaled()
函数确保图像适应控件大小,Qt::KeepAspectRatio保持其原始纵横比。
-
// 要出现一个可以选择图片的对话框 QString filename = QFileDialog::getOpenFileName(this, tr("open Image"), ".", tr("Image File(*.png *.jpg *.jpeg *.webp *.bmp)")); if (filename.isEmpty()) { return; } // 在QT中Qstring是个对象类型,不能直接转化成字符串必须使用toLatin1().data才能转化 Mat srcImg = imread(filename.toLatin1().data()); if (srcImg.empty()) { return; } // 在opencv显示图片使用BGR格式显示的,但是到了QT中,显示图片的格式是RGB格式,所以要做转换 cvtColor(srcImg, srcImg, COLOR_BGR2RGB); // 创建一个模糊图像 Mat blurImg; blur(srcImg, blurImg, Size(50, 50)); // 深度复制图像数据 Mat srcImgCopy = srcImg.clone(); Mat blurImgCopy = blurImg.clone(); //如何把图片显示到Label,我们之前只要调用下QT的imshow方法就可以了 //要把Mat格式转化成QImage对象 // 将srcImg转换为QImage QImage distImage(srcImgCopy.data, srcImgCopy.cols, srcImgCopy.rows, srcImgCopy.step, QImage::Format_RGB888); ui->label->setPixmap(QPixmap::fromImage(distImage.scaled(ui->label->size(), Qt::KeepAspectRatio))); // 将blurImg转换为QImage QImage blurDistImage(blurImgCopy.data, blurImgCopy.cols, blurImgCopy.rows, blurImgCopy.step, QImage::Format_RGB888); ui->label_2->setPixmap(QPixmap::fromImage(blurDistImage.scaled(ui->label_2->size(), Qt::KeepAspectRatio)));
3. 人脸检测
人脸检测介绍
是机器视觉中的一个常见应用。
如何使用opencv的人脸识别。 机器是如何识别人脸的:(机器不可能像人的眼睛一样识别人脸,它是通过人脸像素特征来识别。)所有的AI程序都必须通过以下步骤来实现 A.采集样本,我们必须采集足够多的人脸,和足够多的非人脸(人工智能标注工程 师) B.计算机会根据一定的算法来扫描这些采样,来获取包含人脸的特征的信息。并且把这些特征信息用文件保存起来。这个文件统称为模型文件。(人工智能算法工程师) C。我们的应用程序就要读取这个模型文件,然后根据模型文件来判断你给的视频或者图片是否包含人脸。(人工智能应用开发工程师) 所谓级联分类器就是包含人脸模型的文件。 计算机识别出人脸,其实就是记录下人脸在这张图片中的坐标位置,一张图片可能会有很多人脸,也就是说会有多个坐标。这些都要存起来,就必须推用类似数组的方式存储。
在本次活动中,我们使用了Haar特征分类器(通常指的是一种级联分类器)来检测图像中的人脸。具体实现包括加载预训练的Haar特征分类器文件,然后使用detectMultiScale()
函数进行人脸检测。
cascade.detectMultiScale(smallImg, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30));
人脸识别的流程
-
图像捕获(Capture Image):
-
使用摄像头或从视频文件中捕获图像帧。例如,使用
cv::VideoCapture
类从摄像头或视频文件中读取图像。
-
-
图像预处理(Preprocessing):
-
将捕获的图像转换为灰度图像,以减少计算复杂度并增强图像的特征。使用
cvtColor
函数将图像从BGR转换为灰度。 -
通过
resize
函数缩小图像尺寸,以加快处理速度。 -
使用
equalizeHist
函数对灰度图像进行直方图均衡化,以增强对比度,使得人脸特征更加明显。cvtColor(img, grayImg, COLOR_BGR2GRAY); cv::resize(grayImg, smallImg, smallImg.size()); equalizeHist(smallImg, smallImg);
-
-
人脸检测(Face Detection):
-
使用预训练的人脸检测模型(例如,Haar级联分类器)检测图像中的人脸。通过
CascadeClassifier
类的detectMultiScale
方法实现,该方法返回图像中检测到的人脸的坐标和尺寸。
-
-
标注人脸(Annotate Faces):
-
对检测到的人脸区域进行标注,通常使用矩形框或圆形。通过使用OpenCV的
rectangle
或circle
函数在图像上绘制检测到的人脸区域。
-
-
显示图像(Display Image):
-
将处理后的图像显示在界面上。通过将OpenCV的
cv::Mat
转换为Qt的QImage
,然后在Qt界面控件中显示。
//计算原始图片中人的时候,为方便识别效果并提高效率,首先要把图片转换成灰度图像,并且还可以缩小一半 Mat grayImg; std::vector<Rect> faces; //存放脸部检测的结果,因为 std::vector 提供了动态数组的功能,适合存储数量不固定的元素 Mat smallImg(cvRound(img.rows/2), cvRound(img.cols/2), CV_8UC1); cvtColor(img, grayImg, COLOR_BGR2GRAY); //灰度图像缩小到smallImg中 cv::resize(grayImg, smallImg, smallImg.size()); //把原图以缩小一半后的图像保存在smallImg图片中 equalizeHist(smallImg, smallImg); cascade.detectMultiScale(smallImg, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30)); //通过上面的函数执行后,正确的人脸数据就会被保存到faces这个向量中 //iterator即迭代器,它使用的遍历的工具 for(std::vector<Rect>::const_iterator r=faces.begin(); r!=faces.end(); r++) { //用来表示我们把人脸画成矩形,或者圆形来表示 Point center; int radius; //圆的半径 center.x = cvRound((r->x+r->width*0.5)*2); center.y = cvRound((r->y+r->height*0.5)*2); radius = (r->width+r->height)*0.25*2; circle(img, center, radius, Scalar(CV_RGB(255, 10, 80)), 3); }
-
多线程与视频处理
GUI介绍
什么是GUI界面?
GUI(图形用户界面) 是一种用户界面,通过图形元素(如按钮、窗口、图标等)与用户进行交互。与传统的命令行界面(CLI)相比,GUI提供了更直观和友好的用户体验,使用户可以通过点击、拖拽和其他图形操作来控制计算机程序。
GUI界面的特点
-
图形化操作: 使用图形界面元素(按钮、滑块、菜单等)与用户交互。
-
事件驱动: 响应用户的操作事件(如点击、输入、拖拽)来触发程序的行为。
-
实时更新: 实时反馈用户的操作和程序的状态。
为什么不能在GUI界面中写死循环?
在GUI应用程序中使用死循环(例如 while (true) { ... }
)是不推荐的,主要原因有以下几点:
-
阻塞主线程:
-
GUI界面的更新和用户交互通常运行在主线程(也称为UI线程)中。死循环会不断占用主线程的时间,导致主线程无法执行其他必要的操作,如处理用户输入、更新界面元素等。
-
结果是应用程序界面会变得无响应,用户无法与应用程序进行交互。
-
-
影响响应性:
-
主线程被阻塞时,所有的UI更新和事件处理都会被延迟或停止。这会导致用户界面卡顿、冻结,用户体验变差。
-
-
资源消耗:
-
死循环通常会消耗大量的CPU资源,导致应用程序的整体性能下降,影响系统的其他任务。
-
正确的处理方式:使用事件驱动和多线程
-
事件驱动编程:
-
GUI应用程序应该依赖事件驱动的编程模型。用户的操作(点击、输入等)触发事件,事件处理函数在主线程中运行。这样,主线程可以处理用户交互,而不需要死循环来不断检查状态。
-
-
多线程:
-
对于需要长时间运行的操作或耗时任务,应该将这些任务放在工作线程中运行。主线程保持响应,而工作线程处理计算密集型或IO操作,然后通过信号和槽机制将结果传递回主线程进行更新。
-
多线程优化
为了实现流畅的视频播放和实时处理,我们使用了Qt的多线程技术。
void CamerVideo::play()//这个方法会在一个工作线程中运行,不能在GUI界面中写死循环 {//如果直接在主线程中处理视频流,图像捕获和处理可能会导致UI线程阻塞,从而使UI界面变得卡顿或无响应。 while(camer.read(videoImg)){ cvtColor(videoImg,videoImg,COLOR_BGR2RGB); QImage img(videoImg.data,videoImg.cols,videoImg.rows,QImage::Format_RGB888); emit GetImage(img);//发射一个信号,传递处理后的图像数据。 Sleep(100);//为每帧图像提供一定的处理时间。 } }
多线程处理: 通过将视频流处理放入工作线程中,可以避免UI线程的阻塞,确保应用程序的流畅性和响应性。主线程通常负责处理UI和用户交互,而工作线程则负责处理图像和视频流数据。
信号槽机制: 发射信号而不是直接更新UI,利用Qt的线程安全机制进行数据传输和UI更新,保持了良好的代码组织和扩展性。
解耦合: 通过信号和槽,CamerVideo
类和UI代码分开,使得代码更加模块化和可维护。
在这个示例中,我们通过创建一个VideoThread
类,将视频捕获和处理任务放到一个单独的线程中运行。
void Widget::receiveImage(const QImage &image)//在主线程中接收并显示图像数据 //Qt 的 GUI 组件只能在主线程中安全地更新,因此接收到图像数据后,必须在主线程中更新 UI { ui->label_5->setPixmap(QPixmap::fromImage(image.scaled(ui->label_4->size(), Qt::KeepAspectRatio))); }
myCamerVideo = new CamerVideo("d:\\czvideo\\video.wmv"); videoThread =new VideoThread(); videoThread->setCamerVideo(myCamerVideo);//这行代码把CamerVideo和VideoThread关联在一起 connect(myCamerVideo,SIGNAL(GetImage(QImage)),this,SLOT(on_recveImge_clicked(QImage)));//当 myCamerVideo 发出 GetImage 信号时,会触发 on_recveImge_clicked(QImage) 槽函数。这个槽函数负责处理图像数据(如更新显示图像) videoThread->start();//启动线程的方法,调用start的时候,会调用run方法
实现界面优化
使用布局管理器来动态调整qlabel的大小,以及实现窗口美化。完成一个LABLE就用一个线程来实现视频显示。
在SmartVideoMainForm
类中,我们学习了如何通过Qt进行窗口管理和用户界面设计。
1. 布局管理器(Layout Managers)
布局管理器是 Qt 提供的用于自动排列和调整界面组件(如按钮、标签、输入框等)的位置和大小的工具。它可以帮助你实现响应式布局,使界面在窗口大小变化时能自动调整。
主要布局管理器类型:
-
QHBoxLayout: 水平布局管理器,按水平行排列子组件。
-
QVBoxLayout: 垂直布局管理器,按垂直列排列子组件。
-
QGridLayout: 网格布局管理器,按照网格方式排列子组件。
-
QFormLayout: 表单布局管理器,适用于表单样式的界面布局。
在 SmartVideoMainForm
类中使用了 QVBoxLayout
和 QHBoxLayout
,这些布局管理器将界面组件组织成一个可动态调整的网格,从而可以在窗口大小变化时自动调整组件的显示方式。
2. 样式表(Stylesheets)
Qt 的样式表类似于 HTML 和 CSS,用于定义界面组件的外观和样式。它允许你控制组件的颜色、边框、背景图像、字体等。
样式表的应用:
-
QPushButton: 定义按钮的背景图像和边框样式。
-
QLabel: 定义标签的边框图像和字体样式。
响应按钮点击事件
-
实现窗口的最小化、最大化和关闭功能。
void SmartVideoMainForm::on_btn_min_clicked() { if(!this->isMinimized()){ this->showMinimized(); } } void SmartVideoMainForm::on_btn_max_clicked() { if(this->isMaximized()){ this->showNormal(); } else { this->showMaximized(); } } void SmartVideoMainForm::on_btn_close_clicked() { qApp->exit(); }
图像显示与布局管理
在用户界面中,如何高效地管理和显示多个视频流是一个关键挑战。在SmartVideoMainForm
类中,我们学习了如何使用Qt的布局管理器(如QVBoxLayout和QHBoxLayout)来动态地添加和移除视频显示部件。
removeLayout()
函数展示了如何清空布局并隐藏视频显示部件,以便于重新排列或更新界面。
void SmartVideoMainForm::removeLayout() {//移除和隐藏组件 // 从 lay1 布局中移除视频标签并隐藏它们 ui->lay1->removeWidget(ui->labVideo1); ui->lay1->removeWidget(ui->labVideo2); ui->lay1->removeWidget(ui->labVideo3); ui->labVideo1->setVisible(false); ui->labVideo2->setVisible(false); ui->labVideo3->setVisible(false); // 从 lay2 布局中移除视频标签并隐藏它们 ui->lay2->removeWidget(ui->labVideo4); ui->lay2->removeWidget(ui->labVideo5); ui->lay2->removeWidget(ui->labVideo6); ui->labVideo4->setVisible(false); ui->labVideo5->setVisible(false); ui->labVideo6->setVisible(false); // 从 lay3 布局中移除视频标签并隐藏它们 ui->lay3->removeWidget(ui->labVideo7); ui->lay3->removeWidget(ui->labVideo8); ui->lay3->removeWidget(ui->labVideo9); ui->labVideo7->setVisible(false); ui->labVideo8->setVisible(false); ui->labVideo9->setVisible(false); }
实现细节:
-
removeWidget
: 从布局中移除指定的组件。移除组件后,组件的内存不会被释放,只是从布局管理器中删除。组件的可视性需要额外设置。 -
setVisible(false)
: 将组件设置为不可见,这不仅确保组件在界面上不显示,还能防止布局管理器尝试显示它们。
在on_btn4_clicked()
中,我们通过重新添加部件到布局中,实现了视频显示窗口的动态更新。
void SmartVideoMainForm::on_btn4_clicked() {//切换到 4 画面布局 // 调用 removeLayout() 方法来清除现有布局 this->removeLayout(); // 在 lay1 布局中添加 labVideo1 和 labVideo2 ui->lay1->addWidget(ui->labVideo1); ui->lay1->addWidget(ui->labVideo2); ui->labVideo1->setVisible(true); ui->labVideo2->setVisible(true); // 在 lay2 布局中添加 labVideo4 和 labVideo5 ui->lay2->addWidget(ui->labVideo4); ui->lay2->addWidget(ui->labVideo5); ui->labVideo4->setVisible(true); ui->labVideo5->setVisible(true); }
实现细节:
-
调用
removeLayout()
: 清除之前的布局,使界面准备好接受新的布局配置。 -
addWidget
: 将视频标签(如labVideo1
和labVideo2
)添加到新的布局(lay1
和lay2
)。addWidget
将组件添加到布局中,使它们在界面上显示。 -
setVisible(true)
: 使添加的组件可见,确保它们在界面上显示。
关于ui文件
是使用 Qt Designer 创建的 XML 文件,它定义了 Qt 界面设计的结构和属性。
1. XML 文件结构
-
<ui>
: 根节点,包含界面定义的版本和主控件。 -
<class>
: 定义了主控件的类名。 -
<widget>
: 定义了主控件的类型和属性,包括尺寸、标题等。 -
<property>
: 用于定义控件的属性,例如geometry
和windowTitle
。 -
<layout>
: 布局管理器,用于组织控件的排列。 -
<item>
: 布局中的子项,包含控件和布局的嵌套关系。
2. 控件和布局
-
QWidget
: 基础的界面控件,所有其他控件都是基于QWidget
的。 -
QVBoxLayout
和QHBoxLayout
: 垂直布局和水平布局,用于安排控件的排列方式。 -
QLabel
: 用于显示文本或图像的控件。 -
QPushButton
: 可点击的按钮控件。
3. 控件属性
-
geometry
: 控件的尺寸和位置。 -
windowTitle
: 窗口标题。 -
styleSheet
: 用于设置控件的样式,例如背景图片、边框、颜色等。
4. 样式表(QSS)
Qt 的样式表(类似于 CSS)允许你定义控件的外观:
-
QPushButton#btn_menu
: 选择 ID 为btn_menu
的按钮并设置其样式。 -
:hover
和:checked
: 状态伪类,用于设置不同状态下的样式。 -
border-image
: 背景图片样式。 -
color
和font
: 文本颜色和字体样式。
5. 信号和槽
-
虽然这个
.ui
文件本身不包含信号和槽的定义,但在实际代码中,控件可以通过QObject::connect
函数与信号和槽进行连接。
6. 布局管理
-
布局管理器(如
QVBoxLayout
和QHBoxLayout
)自动调整子控件的位置和大小,以便在主控件中进行适当的排列和对齐。
7. 资源文件
-
资源文件(如
:/image/btn_menu_normal.png
)在.ui
文件中引用了外部图像资源。它们通常被打包到 Qt 的资源系统中,并通过路径引用。
示例分析
布局部分
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,9,0">
-
class="QVBoxLayout"
: 使用垂直布局管理器。 -
name="verticalLayout_2"
: 布局的名称。 -
stretch="0,9,0"
: 布局的伸缩因子,控制子控件在布局中的扩展比例。
样式表部分
QPushButton#btn_menu{ background-image: url(:/image/btn_menu_normal.png); border: 0px; } QPushButton#btn_menu:hover{ background-image: url(:/image/btn_menu_hover.png); border: 0px; }
-
background-image
: 设置按钮的背景图片。 -
:hover
: 鼠标悬停时的样式。
总结
-
Qt 的
.ui
文件 是 Qt Designer 生成的 XML 文件,用于定义 UI 界面。 -
了解控件和布局的结构 对于 Qt 界面设计至关重要。
-
样式表 能够让你自定义控件的外观,提高界面的美观性。
-
资源管理 使得图像和其他资源可以被有效地引用和使用。