文章目录
- 前言
- 一、在QLabel上显示图片并绘制矩形框
- 二、保存矩形框数据为CSV文件
- 三、保存截取图像
- 四、将截取图像填充到表格
- 五、图形视图框架显示图像
- 六、示例完整代码
- 总结
前言
本文主要讲述了在Qt下使用OpenCV截取绘制的矩形框图像,并将矩形框数据保存为CSV文件,以及在QtableWidget表格上显示截取的图像,其中也使用到了Qt的图形视图框架,下面是示例的详细内容展示,以便大家学习,如有错误之处,欢迎大家批评指正。
项目效果
提示:以下是本篇文章正文内容,下面案例可供参考
一、在QLabel上显示图片并绘制矩形框
本文中使用事件过滤器(eventFilter)来处理界面上QLabel部件的绘图事件,使用QPainter在其上显示图片以及绘制矩形框,矩形框的绘制重写了一些QMouseEvent,具体实现见下文示例完整代码。在我之前的文章中还有直线、多边形等绘制的实现,可以查看文章Qt实现在QLabel上显示图片并进行线条/矩形框/多边形的绘制
//更新界面绘图
void MainWindow::updateShowPaint()
{//绘制图像QPainter painter(ui->lb_showImage);if(m_isImageFlag){painter.drawPixmap(0,0,m_fitPixmap);}painter.setFont(QFont(font().family(),12));//红色绘制区域矩形框if(m_drawRectFlag){QPen pen(Qt::red); //默认实线画笔for(int i=0;i<m_rectList.size();i++){const QRect &rect = m_rectList.at(i);if(i == m_selectedRectIndex){pen.setStyle(Qt::DashLine); //虚线画笔painter.setPen(pen);}else{pen.setStyle(Qt::SolidLine); //实线画笔painter.setPen(pen);}painter.drawRect(rect);}if(m_isPressedFlag){pen.setStyle(Qt::DashLine); //虚线画笔painter.setPen(pen);painter.drawRect(m_currentRect);}}
}
二、保存矩形框数据为CSV文件
示例中使用了QList容器来存储绘制的矩形,所以要遍历容器中的矩形数据并保存为CSV文件(逗号分隔符文件),下面是读写CSV文件的实现:
//保存矩形框数据为CSV文件
bool MainWindow::saveRectListToCSV(const QList<QRect> &rectList, const QString &fileName)
{QFile file(fileName);if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){LOGDEBUG<<fileName<<"打开失败!";return false;}QTextStream out(&file);out<<"x,y,width,height"<<endl; //写入CSV文件的标题行for(const auto &rect : rectList){out<<rect.x()<<","<<rect.y()<<","<<rect.width()<<","<<rect.height()<<endl;}file.close();return true;
}//读取CSV文件数据转换为矩形框
QList<QRect> MainWindow::loadRectListFromCSV(const QString &fileName)
{QList<QRect> rectList;QFile file(fileName);if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){LOGDEBUG<<fileName<<"打开失败!";return rectList;}QTextStream in(&file);QString line = in.readLine(); //跳过标题行while(!in.atEnd()){line = in.readLine();if(!line.isEmpty()){QStringList fields = line.split(",");if(fields.size() == 4){bool ok;int x = fields[0].toInt(&ok);int y = fields[1].toInt(&ok);int width = fields[2].toInt(&ok);int height = fields[3].toInt(&ok);if(ok){rectList.append(QRect(x,y,width,height));}else{LOGDEBUG<<"矩形转换失败!";}}else{LOGDEBUG<<"字段数量不正确!";}}}file.close();return rectList;
}
三、保存截取图像
要保存截取图像,首先在程序运行目录创建了一个保存截取图像的文件夹,然后每次保存时会先删除文件夹内原先的图像文件,再建新的截取文件图像。使用OpenCV来进行截取图像的保存,所以需要将绘制的QRect转换为OpenCV的Rect:
//保存截取的图像
void MainWindow::saveCroppedImage()
{//创建保存截取图像文件夹QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";QDir dir(imageDir);if(!dir.exists()){LOGDEBUG<<"保存截取图像文件夹不存在!";if(!dir.mkpath(imageDir)){LOGDEBUG<<imageDir + "文件夹创建失败!";return;}}//判断当前显示图像是否正确int rows = m_showMat.rows;int cols = m_showMat.cols;LOGDEBUG<<"图像高:"<<rows<<" 图像宽:"<<cols;if((rows == 648) && (cols == 820)) //测试图像分辨率{//获取目录下所有图像文件的列表QStringList filters;filters<<"*.jpg";QStringList imageFiles = dir.entryList(filters,QDir::Files);//遍历列表并删除文件for(const QString &file : imageFiles){QString filePath = dir.filePath(file);QFile::remove(filePath);}//遍历矩形列表for(const QRect &rect : m_rectList){//将QRect转换为OpenCV的Rectcv::Rect roi(rect.x()*m_scaleNum,rect.y()*m_scaleNum,rect.width()*m_scaleNum,rect.height()*m_scaleNum);//截取图像cv::Mat croppedImage = m_showMat(roi);//保存图像到文件QString saveName = QString("saveImage_%1.jpg").arg(m_rectList.indexOf(rect)+1);if(!saveName.isEmpty()){QString saveImage = imageDir + saveName;bool success = cv::imwrite(saveImage.toStdString(),croppedImage);if(success){LOGDEBUG<<saveName<<"保存成功!";}else{LOGDEBUG<<saveName<<"保存失败!";}}}QMessageBox::information(this,"提示","截取图像保存成功!");}else{LOGDEBUG<<"保存失败,当前显示图像错误!";QMessageBox::information(this,"提示","截取图像保存失败!");}
}
四、将截取图像填充到表格
获取保存截取图像的文件夹内的图像,创建一个QLabel来显示图像并将其设置为单元格的小部件,这样就实现了表格显示图像的效果:
//将截取图像填充到表格
void ShowImage::fillTableWidgetWithImages(QTableWidget *tableWidget)
{//清除表格内容tableWidget->clear();tableWidget->setRowCount(0);//设置表头和列宽tableWidget->setColumnCount(2);tableWidget->setHorizontalHeaderLabels(QStringList() << "截取图像" << "时间");tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); //设置列宽拉伸模式tableWidget->resizeColumnsToContents(); //列宽自适应内容//获取文件夹内文件QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";QDir dir(imageDir);QStringList filters;filters<<"*.jpg";QStringList imageFiles = dir.entryList(filters,QDir::Files);for(const QString &fileName : imageFiles){//获取最后修改时间QString filePath = dir.filePath(fileName);QFileInfo fileInfo(filePath);QDateTime fileTime = fileInfo.lastModified();//获取图像cv::Mat image = cv::imread(filePath.toStdString());if(!image.empty()){//插入新行int rowCount = tableWidget->rowCount();tableWidget->insertRow(rowCount);//创建一个QLabel来显示图像QLabel *imageLabel = new QLabel(tableWidget);QPixmap pixmap = cvMatToQPixmap(image);imageLabel->setPixmap(pixmap);imageLabel->setAlignment(Qt::AlignCenter);tableWidget->setCellWidget(rowCount,0,imageLabel); //将QLabel设置为单元格的小部件//设置时间文本QTableWidgetItem *timeItem = new QTableWidgetItem(fileTime.toString("yyyy-MM-dd HH:mm:ss"));timeItem->setTextAlignment(Qt::AlignCenter);tableWidget->setItem(rowCount,1,timeItem); //将时间添加到表格的下一列//设置图像文件路径到表格项的data角色,方便后续删除操作QTableWidgetItem *pathItem = new QTableWidgetItem();pathItem->setData(Qt::UserRole,filePath);tableWidget->setItem(rowCount,0,pathItem);}}
}
五、图形视图框架显示图像
双击表格某行实现跳转,可以对截取图像进行缩放查看,在这里有场景、视图和图形项的使用,关于图形视图框架的更多内容可以查看文章Qt学习:图形视图框架的使用
//双击表格跳转
void ShowImage::on_tw_image_cellDoubleClicked(int row,int column)
{//检查列数,确保不是点击的表头或其他非数据列if(column < ui->tw_image->columnCount()){//从表格项中获取图像文件路径QVariant imagePathVariant = ui->tw_image->item(row,0)->data(Qt::UserRole);QString imagePath = imagePathVariant.toString();LOGDEBUG<<"选中的图像文件名:"<<imagePath;//加载图像QPixmap pixmap(imagePath);if(!pixmap.isNull()){//清除场景中的旧内容scene->clear();//创建QGraphicsPixmapItem并添加到场景中QGraphicsPixmapItem *pixmapItem = scene->addPixmap(pixmap);//调整场景大小以匹配图像scene->setSceneRect(pixmapItem->boundingRect());//确保视图适应新内容ui->gw_image->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);}else{//处理图像加载失败的情况LOGDEBUG<<"加载图像失败!";}//切换页面ui->sw_tableImage->setCurrentIndex(1);}
}
六、示例完整代码
1.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include "showimage.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();void initWidget();void saveCroppedImage();bool saveRectListToCSV(const QList<QRect> &rectList, const QString &fileName);QList<QRect> loadRectListFromCSV(const QString &fileName);void setShowImage();void setWidgetEnabled(bool flag);void setCurPoint(QPoint *getPoint);void updateShowPaint();protected:void mousePressEvent(QMouseEvent *event);void mouseMoveEvent(QMouseEvent *event);void mouseReleaseEvent(QMouseEvent *event);bool eventFilter(QObject *watched, QEvent *event);private slots:void on_pb_drawRect_clicked();void on_pb_resetRect_clicked();void on_pb_set_clicked();void on_pb_open_clicked();void on_pb_view_clicked();void on_pb_save_clicked();private:Ui::MainWindow *ui;bool m_isImageFlag; //图片是否存在bool m_isRegionFlag; //是否绘图区域bool m_isPressedFlag; //鼠标是否按压bool m_isMoveFlag; //鼠标是否移动bool m_drawRectFlag; //绘制区域标志double m_scaleNum; //缩放比例int m_differNumX; //x偏移值int m_differNumY; //y偏移值int m_getNumX; //x最大值int m_getNumY; //y最大值cv::Mat m_showMat; //界面显示图像QPixmap m_fitPixmap; //界面绘制图像QPoint m_startPoint; //矩形绘制的起点QRect m_currentRect; //当前正在绘制的矩形int m_selectedRectIndex; //表示选中的矩形序号QList<QRect> m_rectList; //存储绘制区域的矩形ShowImage *m_showImage; //截取图像列表界面};#endif // MAINWINDOW_H
2.mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->initWidget();
}MainWindow::~MainWindow()
{delete ui;delete m_showImage;
}//初始化界面
void MainWindow::initWidget()
{//初始化m_isImageFlag = false;m_isRegionFlag = false;m_isPressedFlag = false;m_isMoveFlag = false;m_drawRectFlag = false;m_scaleNum = 2; //根据图像与显示界面的大小变化,显示界面是410*324,测试图像大小是820*648m_differNumX = 20;m_differNumY = 20;m_getNumX = 410;m_getNumY = 324;m_showMat = NULL;m_startPoint = QPoint(0,0);m_currentRect = QRect();m_selectedRectIndex = -1;m_rectList.clear();//设置界面尺寸this->setFixedSize(this->width(),this->height());//添加事件过滤器ui->lb_showImage->installEventFilter(this);//截取图像列表界面m_showImage = new ShowImage();//设置界面显示图像setShowImage();
}//保存截取的图像
void MainWindow::saveCroppedImage()
{//创建保存截取图像文件夹QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";QDir dir(imageDir);if(!dir.exists()){LOGDEBUG<<"保存截取图像文件夹不存在!";if(!dir.mkpath(imageDir)){LOGDEBUG<<imageDir + "文件夹创建失败!";return;}}//判断当前显示图像是否正确int rows = m_showMat.rows;int cols = m_showMat.cols;LOGDEBUG<<"图像高:"<<rows<<" 图像宽:"<<cols;if((rows == 648) && (cols == 820)) //测试图像分辨率{//获取目录下所有图像文件的列表QStringList filters;filters<<"*.jpg";QStringList imageFiles = dir.entryList(filters,QDir::Files);//遍历列表并删除文件for(const QString &file : imageFiles){QString filePath = dir.filePath(file);QFile::remove(filePath);}//遍历矩形列表for(const QRect &rect : m_rectList){//将QRect转换为OpenCV的Rectcv::Rect roi(rect.x()*m_scaleNum,rect.y()*m_scaleNum,rect.width()*m_scaleNum,rect.height()*m_scaleNum);//截取图像cv::Mat croppedImage = m_showMat(roi);//保存图像到文件QString saveName = QString("saveImage_%1.jpg").arg(m_rectList.indexOf(rect)+1);if(!saveName.isEmpty()){QString saveImage = imageDir + saveName;bool success = cv::imwrite(saveImage.toStdString(),croppedImage);if(success){LOGDEBUG<<saveName<<"保存成功!";}else{LOGDEBUG<<saveName<<"保存失败!";}}}QMessageBox::information(this,"提示","截取图像保存成功!");}else{LOGDEBUG<<"保存失败,当前显示图像错误!";QMessageBox::information(this,"提示","截取图像保存失败!");}
}//保存矩形框数据为CSV文件
bool MainWindow::saveRectListToCSV(const QList<QRect> &rectList, const QString &fileName)
{QFile file(fileName);if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){LOGDEBUG<<fileName<<"打开失败!";return false;}QTextStream out(&file);out<<"x,y,width,height"<<endl; //写入CSV文件的标题行for(const auto &rect : rectList){out<<rect.x()<<","<<rect.y()<<","<<rect.width()<<","<<rect.height()<<endl;}file.close();return true;
}//读取CSV文件数据转换为矩形框
QList<QRect> MainWindow::loadRectListFromCSV(const QString &fileName)
{QList<QRect> rectList;QFile file(fileName);if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){LOGDEBUG<<fileName<<"打开失败!";return rectList;}QTextStream in(&file);QString line = in.readLine(); //跳过标题行while(!in.atEnd()){line = in.readLine();if(!line.isEmpty()){QStringList fields = line.split(",");if(fields.size() == 4){bool ok;int x = fields[0].toInt(&ok);int y = fields[1].toInt(&ok);int width = fields[2].toInt(&ok);int height = fields[3].toInt(&ok);if(ok){rectList.append(QRect(x,y,width,height));}else{LOGDEBUG<<"矩形转换失败!";}}else{LOGDEBUG<<"字段数量不正确!";}}}file.close();return rectList;
}//设置界面显示图像
void MainWindow::setShowImage()
{//读取CSV文件数据转换为矩形框QString csvFile = QCoreApplication::applicationDirPath() + "/regionRect.csv";m_rectList.clear();m_rectList = loadRectListFromCSV(csvFile);//判断图像文件是否存在QImage showImage;QString imageName = QCoreApplication::applicationDirPath() + "/testShow.jpg";LOGDEBUG<<"图像文件名:"<<imageName;QFileInfo fileInfo(imageName);if(!fileInfo.isFile()){LOGDEBUG<<"图像文件不存在";return;}m_showMat = cv::imread(imageName.toStdString());showImage.load(imageName);QPixmap showPixmap = QPixmap::fromImage(showImage);if(showPixmap.isNull()){LOGDEBUG<<"界面显示图像为空";m_isImageFlag = false;return;}else{m_isImageFlag = true;m_fitPixmap = showPixmap.scaled(QSize(m_getNumX,m_getNumY),Qt::KeepAspectRatio,Qt::SmoothTransformation); //按比例缩放}//更新界面this->update();
}//设置界面按钮使能
void MainWindow::setWidgetEnabled(bool flag)
{m_selectedRectIndex = -1;m_drawRectFlag = !flag;ui->pb_drawRect->setEnabled(flag);ui->pb_resetRect->setEnabled(flag);ui->pb_set->setEnabled(flag);ui->pb_open->setEnabled(flag);ui->pb_view->setEnabled(flag);ui->pb_save->setEnabled(flag);
}//设置绘图超限点
void MainWindow::setCurPoint(QPoint *getPoint)
{if(getPoint->x() < 0){getPoint->setX(0);}else if(getPoint->x() > m_getNumX){getPoint->setX(m_getNumX);}if(getPoint->y() < 0){getPoint->setY(0);}else if(getPoint->y() > m_getNumY){getPoint->setY(m_getNumY);}
}//更新界面绘图
void MainWindow::updateShowPaint()
{//绘制图像QPainter painter(ui->lb_showImage);if(m_isImageFlag){painter.drawPixmap(0,0,m_fitPixmap);}painter.setFont(QFont(font().family(),12));//红色绘制区域矩形框if(m_drawRectFlag){QPen pen(Qt::red); //默认实线画笔for(int i=0;i<m_rectList.size();i++){const QRect &rect = m_rectList.at(i);if(i == m_selectedRectIndex){pen.setStyle(Qt::DashLine); //虚线画笔painter.setPen(pen);}else{pen.setStyle(Qt::SolidLine); //实线画笔painter.setPen(pen);}painter.drawRect(rect);}if(m_isPressedFlag){pen.setStyle(Qt::DashLine); //虚线画笔painter.setPen(pen);painter.drawRect(m_currentRect);}}
}//鼠标按压,开始绘制
void MainWindow::mousePressEvent(QMouseEvent *event)
{//判断是否开始绘制if(!m_drawRectFlag){return;}//判断鼠标是否在绘图区域内int xMax = m_getNumX + m_differNumX;int yMax = m_getNumY + m_differNumY;if((event->pos().x() < 30) || (event->pos().x() > xMax) ||(event->pos().y() < 30) || (event->pos().y() > yMax)){m_isRegionFlag = false;return;}else{m_isRegionFlag = true;}//鼠标左键按压开始绘制if(event->button() == Qt::LeftButton){m_isPressedFlag = true;m_isMoveFlag = false;//获取起点m_startPoint = QPoint(event->pos().x()-m_differNumX,event->pos().y()-m_differNumY);m_currentRect = QRect(m_startPoint,QSize());this->update();}
}//鼠标移动
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{//判断是否开始绘制if(!m_drawRectFlag){return;}//判断是否在绘图区域内if(!m_isRegionFlag){return;}if(m_isPressedFlag){m_isMoveFlag = true;QPoint movePoint = QPoint(event->pos().x()-m_differNumX,event->pos().y()-m_differNumY);setCurPoint(&movePoint);int w = movePoint.x() - m_startPoint.x();int h = movePoint.y() - m_startPoint.y();m_currentRect.setSize(QSize(w,h));this->update();}
}//鼠标松开,绘制完成
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{//判断是否开始绘制if(!m_drawRectFlag){return;}//判断是否在绘图区域内if(!m_isRegionFlag){return;}//鼠标左键松开if(event->button() == Qt::LeftButton){m_isPressedFlag = false;if(!m_isMoveFlag){//判断是否选中矩形QVector<int> v_index;QPoint m_endPoint = QPoint(event->pos().x()-m_differNumX,event->pos().y()-m_differNumY);QList<QRect> curRectList;if(m_drawRectFlag){curRectList = m_rectList;}for(int i=0;i<curRectList.size();i++){//需要考虑大矩形包含小矩形的情况if(curRectList.at(i).contains(m_endPoint)){//将矩形号保存起来v_index.push_back(i);}}//通过面积来判断选中的矩形int vSize = v_index.size();if(vSize == 0){m_selectedRectIndex = -1;}else{if(vSize == 1){m_selectedRectIndex = v_index[0];}else{m_selectedRectIndex = v_index[0];int smallArea = curRectList[v_index[0]].width() * curRectList[v_index[0]].width();for(int i=0;i<vSize;i++){int area = curRectList[v_index[i]].width() * curRectList[v_index[i]].width();if(area < smallArea){m_selectedRectIndex = v_index[i];}}}}}//判断矩形大小是否正常int width = qAbs(m_currentRect.width());int height = qAbs(m_currentRect.height());//LOGDEBUG<<"width:"<<width<<" height:"<<height;if((width < 10) && (height < 10)){this->update();return;}if((width == 0) || (height == 0)){this->update();return;}//矩形if(m_drawRectFlag){m_rectList.append(m_currentRect);}this->update();}else if(event->button() == Qt::RightButton){//界面使能并更新setWidgetEnabled(true);this->update();}
}//事件过滤
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{if((watched == ui->lb_showImage) && (event->type() == QEvent::Paint)){updateShowPaint();}return QWidget::eventFilter(watched,event);
}//绘制矩形
void MainWindow::on_pb_drawRect_clicked()
{//界面使能setWidgetEnabled(false);ui->pb_resetRect->setEnabled(true);this->update();
}//重置矩形
void MainWindow::on_pb_resetRect_clicked()
{if(m_selectedRectIndex >= 0){if(m_drawRectFlag){m_rectList.removeAt(m_selectedRectIndex);}m_selectedRectIndex = -1;this->update();}else{QMessageBox::information(this,"提示","未选择绘制的矩形!");}
}//设置截取图像
void MainWindow::on_pb_set_clicked()
{//保存截取的图像saveCroppedImage();
}//打开截取图像列表
void MainWindow::on_pb_open_clicked()
{m_showImage->updateWidget();m_showImage->show();
}//查看绘制好的矩形
void MainWindow::on_pb_view_clicked()
{m_selectedRectIndex = -1;m_drawRectFlag = true;this->update();
}//保存
void MainWindow::on_pb_save_clicked()
{//保存矩形框数据为CSV文件QString csvFile = QCoreApplication::applicationDirPath() + "/regionRect.csv";LOGDEBUG<<"矩形框数据文件名:"<<csvFile;if(saveRectListToCSV(m_rectList,csvFile)){QMessageBox::information(this,"提示","文件保存成功!");}
}
3.mainwindow.ui
4.showimage.h
#ifndef SHOWIMAGE_H
#define SHOWIMAGE_H#include <QWidget>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QTime>
#include <QDebug>
#include <QLabel>
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QTableWidget>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include "opencv2/opencv.hpp"#define LOGDEBUG qDebug()<<QTime::currentTime().toString("[hh:mm:ss:zzz]")namespace Ui {
class ShowImage;
}class ShowImage : public QWidget
{Q_OBJECTpublic:explicit ShowImage(QWidget *parent = nullptr);~ShowImage();void initWidget();void updateWidget();QPixmap cvMatToQPixmap(const cv::Mat &mat);void fillTableWidgetWithImages(QTableWidget *tableWidget);void deleteSelectedImage(QTableWidget *tableWidget);protected:void wheelEvent(QWheelEvent *event);private slots:void on_pb_delete_clicked();void on_tw_image_cellDoubleClicked(int row, int column);void on_pb_retuen_clicked();private:Ui::ShowImage *ui;QGraphicsScene *scene; //场景};#endif // SHOWIMAGE_H
5.showimage.cpp
#include "showimage.h"
#include "ui_showimage.h"ShowImage::ShowImage(QWidget *parent) :QWidget(parent),ui(new Ui::ShowImage)
{ui->setupUi(this);this->initWidget();
}ShowImage::~ShowImage()
{delete ui;
}//初始化界面
void ShowImage::initWidget()
{this->setWindowTitle("图像列表");this->setWindowFlags(Qt::WindowCloseButtonHint | Qt::Dialog); //Qt::WindowStaysOnTopHint始终保持在顶层界面this->setWindowModality(Qt::ApplicationModal);this->setAttribute(Qt::WA_QuitOnClose,false);this->setFixedSize(this->width(),this->height());//创建场景scene = new QGraphicsScene(this);ui->gw_image->setScene(scene);ui->gw_image->setRenderHint(QPainter::Antialiasing);ui->gw_image->setDragMode(QGraphicsView::ScrollHandDrag);
}//更新界面显示
void ShowImage::updateWidget()
{ui->sw_tableImage->setCurrentIndex(0);fillTableWidgetWithImages(ui->tw_image);
}//将OpenCV图像转换为QPixmap
QPixmap ShowImage::cvMatToQPixmap(const cv::Mat &mat)
{QImage showImage;if(mat.channels() > 1){showImage = QImage((const unsigned char*)(mat.data),mat.cols,mat.rows,mat.step,QImage::Format_RGB888); //彩色图}else{showImage = QImage((const unsigned char*)(mat.data),mat.cols,mat.rows,mat.step,QImage::Format_Indexed8); //灰度图}//OpenCV使用BGR顺序,而Qt使用RGB顺序,因此需要交换颜色通道return QPixmap::fromImage(showImage.rgbSwapped().scaled(100,100,Qt::KeepAspectRatio,Qt::SmoothTransformation));
}//将截取图像填充到表格
void ShowImage::fillTableWidgetWithImages(QTableWidget *tableWidget)
{//清除表格内容tableWidget->clear();tableWidget->setRowCount(0);//设置表头和列宽tableWidget->setColumnCount(2);tableWidget->setHorizontalHeaderLabels(QStringList() << "截取图像" << "时间");tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); //设置列宽拉伸模式tableWidget->resizeColumnsToContents(); //列宽自适应内容//获取文件夹内文件QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";QDir dir(imageDir);QStringList filters;filters<<"*.jpg";QStringList imageFiles = dir.entryList(filters,QDir::Files);for(const QString &fileName : imageFiles){//获取最后修改时间QString filePath = dir.filePath(fileName);QFileInfo fileInfo(filePath);QDateTime fileTime = fileInfo.lastModified();//获取图像cv::Mat image = cv::imread(filePath.toStdString());if(!image.empty()){//插入新行int rowCount = tableWidget->rowCount();tableWidget->insertRow(rowCount);//创建一个QLabel来显示图像QLabel *imageLabel = new QLabel(tableWidget);QPixmap pixmap = cvMatToQPixmap(image);imageLabel->setPixmap(pixmap);imageLabel->setAlignment(Qt::AlignCenter);tableWidget->setCellWidget(rowCount,0,imageLabel); //将QLabel设置为单元格的小部件//设置时间文本QTableWidgetItem *timeItem = new QTableWidgetItem(fileTime.toString("yyyy-MM-dd HH:mm:ss"));timeItem->setTextAlignment(Qt::AlignCenter);tableWidget->setItem(rowCount,1,timeItem); //将时间添加到表格的下一列//设置图像文件路径到表格项的data角色,方便后续删除操作QTableWidgetItem *pathItem = new QTableWidgetItem();pathItem->setData(Qt::UserRole,filePath);tableWidget->setItem(rowCount,0,pathItem);}}
}//删除选中的截取图像
void ShowImage::deleteSelectedImage(QTableWidget *tableWidget)
{QList<QTableWidgetItem*> selectedItems = tableWidget->selectedItems();if(selectedItems.isEmpty()){QMessageBox::information(this,"提示","请选择要删除的图像!");return;}//获取选中的行索引int row = tableWidget->row(selectedItems.first());//从表格项中获取图像文件路径QVariant imagePathVariant = tableWidget->item(row,0)->data(Qt::UserRole);QString imagePath = imagePathVariant.toString();//删除图像文件QFile::remove(imagePath);//删除表格行tableWidget->removeRow(row);
}//滚轮事件
void ShowImage::wheelEvent(QWheelEvent *event)
{if(event->angleDelta().y() > 0){//滚轮向上滚动,放大ui->gw_image->scale(1.1, 1.1);}else{//滚轮向下滚动,缩小ui->gw_image->scale(1 / 1.1, 1 / 1.1);}
}//删除
void ShowImage::on_pb_delete_clicked()
{deleteSelectedImage(ui->tw_image);
}//双击表格跳转
void ShowImage::on_tw_image_cellDoubleClicked(int row,int column)
{//检查列数,确保不是点击的表头或其他非数据列if(column < ui->tw_image->columnCount()){//从表格项中获取图像文件路径QVariant imagePathVariant = ui->tw_image->item(row,0)->data(Qt::UserRole);QString imagePath = imagePathVariant.toString();LOGDEBUG<<"选中的图像文件名:"<<imagePath;//加载图像QPixmap pixmap(imagePath);if(!pixmap.isNull()){//清除场景中的旧内容scene->clear();//创建QGraphicsPixmapItem并添加到场景中QGraphicsPixmapItem *pixmapItem = scene->addPixmap(pixmap);//调整场景大小以匹配图像scene->setSceneRect(pixmapItem->boundingRect());//确保视图适应新内容ui->gw_image->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);}else{//处理图像加载失败的情况LOGDEBUG<<"加载图像失败!";}//切换页面ui->sw_tableImage->setCurrentIndex(1);}
}//返回
void ShowImage::on_pb_retuen_clicked()
{ui->sw_tableImage->setCurrentIndex(0);
}
6.showimage.ui
总结
在这个示例中,我们可以看到其中使用了事件过滤器来刷新绘图界面,使用了OPenCV来实现对图像的截取,使用图形视图框架来对图像进行扩大缩小等,其实有些功能在我之前的文章中就有实现过,但在这里发现了之前实现的需求存在一些不足,并对其进行了优化。在学习的过程中不仅可以发现过去的不足,而且能解锁新的知识,让我们一起加油努力呀~
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。