Qt下使用OpenCV截取图像并在QtableWidget表格上显示

文章目录

  • 前言
  • 一、在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:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/3841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

235 基于matlab的时频盲源分离(TFBSS)算法

基于matlab的时频盲源分离&#xff08;TFBSS&#xff09;算法&#xff0c;TFBSS用空间频率分布来分离非平稳信号&#xff0c;可以分离具有不同时频分布的源信号&#xff0c;也能够分离具有相同谱密度但时频分布不同的高斯源。同时&#xff0c;该算法在时频域上局域化源信号能量…

vue 3 —— 笔记(模板语法,响应式变量)

模板语法&#xff1a; Vue 使用一种基于 html 的模板语法&#xff0c;使我们能声明式将其组件实例绑定到呈现的 dom 上 文本插值 基础数据绑定形式 双大括号 会替换相应组件实例 msg 属性的值 原始html 双大括号会将数据解释为纯文本 不是html 想插入html 使用 v-html 指令 &…

【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门

【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门 文章目录 【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门1. 概述2. 服务熔断服务降级(CircuitBreaker)2.1 案例说明2.1.1 基于计数的滑动窗口2.1.2 测试2.2.1 基于时间的滑动窗口2.2.2 测试 3. 隔离(B…

前端单元测试的艺术:专业化策略与Vue项目高效实践指南

单元测试是软件开发中的基石&#xff0c;尤其对于前端领域&#xff0c;它为保证代码质量、提升开发效率、强化项目稳定性提供了不可或缺的支持。本文将深入剖析单元测试的核心理念&#xff0c;揭示其在前端开发中的独特价值&#xff0c;并提炼出一套专业且高效的实践策略&#…

git 基础知识(全能版)

文章目录 一 、git 有三个分区二、git 基本操作1、克隆—git clone2、拉取—git fetch / git pull3、查看—git status / git diff3.1 多人开发代码暂存技巧 本地代码4、提交—git add / git commit / git push5、日志—git log / git reflog6、删除—git rm ‘name’7、撤销恢…

阿里云服务器(Ubuntu22)上的MySQL8更改为大小写不敏感

因为windows上默认的mysql8.0是大小写不敏感的&#xff0c;部署到服务器上之后发现ubuntu默认的是大小写敏感&#xff0c;所以为了不更改代码&#xff0c;需要将mysql数据库设置为大小写不敏感的。 &#xff01;&#xff01;&#xff01;重要一定要做好数据库的备份&#xff0…

使用JavaScript日历小部件和DHTMLX Gantt的应用场景(一)

DHTMLX Suite UI 组件库允许您更快地构建跨平台、跨浏览器 Web 和移动应用程序。它包括一组丰富的即用式 HTML5 组件&#xff0c;这些组件可以轻松组合到单个应用程序界面中。 DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表&#xff0c;可满足项目管理应用…

「51媒体」城市推介会,地方旅游推荐,怎么做好媒体宣传

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 城市推介会和地方旅游推荐是城市形象宣传的重要组成部分&#xff0c;通过有效的媒体宣传可以提升城市的知名度和吸引力。&#xff1a; 一&#xff0c;活动内容层面&#xff1a; 突出亮点…

pyqt设置标签显示图片并设置大小

pyqt设置标签显示图片并设置大小 标签显示图片效果代码 标签显示图片 使用 QPixmap 加载图片进行图片大小设置把图片对象设置到标签上 效果 代码 from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget from PyQt5.QtGui import QPixmap import sys from…

某赛通电子文档安全管理系统 多处 SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

采购数据分析驾驶舱分享,照着它抄作业

今天我们来看一张采购管理驾驶舱。这是一张充分运用了多种数据可视化图表、智能分析功能&#xff0c;从物料和供应商的角度全面分析采购情况的BI数据可视化报表&#xff0c;主要分为三个部分&#xff0c;接下来就分部分来了解一下。 第一部分&#xff1a;关键指标计算及颜色预…

从零入门区块链和比特币(第一期)

欢迎来到我的区块链与比特币入门指南&#xff01;如果你对区块链和比特币感兴趣&#xff0c;但不知道从何开始&#xff0c;那么你来对地方了。本博客将为你提供一个简明扼要的介绍&#xff0c;帮助你了解这个领域的基础知识&#xff0c;并引导你进一步探索这个激动人心的领域。…

rabbitmq集群配置

1&#xff0c;配置环境变量 MY_POD_NAME&#xff1a;当前Pod的名称 RABBITMQ_ERLANG_COOKIE&#xff1a;设置Erlang Cookie用于节点间通信安全验证&#xff0c;值来自/nfs/rabbitmq/lib/.erlang.cookie文件内容 RABBITMQ_NODENAME&#xff1a;根据Pod名称动态生成了RabbitMQ…

【GO】命令行解析 os 与 flag

目录 OS解析命令 简单用法 进阶用法 flag命令解析 基础实例 1. 自定义数据类型 2. 创建多个 FlagSet 3. 整合环境变量和配置文件 os与flag 关键点解析 程序的作用 示例命令行调用 在 Go 语言中&#xff0c;命令行解析是一项基本且常用的功能&#xff0c;它允许开发者…

微信小程序简单实现购物车功能

微信小程序简单实现购物车结算和购物车列表展示功能 实现在微信小程序中对每一个购物车界面的商品订单&#xff0c;进行勾选结算和取消结算的功能&#xff0c;相关界面截图如下&#xff1a; 具体实现示例代码为&#xff1a; 1、js代码&#xff1a; Page({/*** 页面的初始数…

K8s: 公有镜像中心和私有镜像中心的搭建

公有镜像中心的搭建和使用 1 &#xff09;在 官方docker镜像中心推送 在 hub.docker.com 上注册账号 (国内一般访问不了&#xff0c;原因不多说) 找到 Create Repository 按钮就行仓库的创建 这样就在官方创建了一个仓库&#xff0c;比如地址为: xx/y-y xx 是我的账户名y-y 是…

数之寻软件怎么样?

数之寻软件是一款功能强大的数据恢复和备份软件&#xff0c;以下是对其特点和功能的详细评价&#xff1a; 一、数据恢复方面&#xff1a; 高效的数据恢复能力&#xff1a;数之寻软件采用了先进的算法和数据恢复技术&#xff0c;能够快速有效地恢复丢失或损坏的数据。无论是文…

Python中matplotlib将多张遥感影像绘制为多个子图并分别设定子图标题的方法

本文介绍基于Python语言的matplotlib模块与gdal模块&#xff0c;读取大量长时间序列遥感影像&#xff0c;分别将其不同时相的图像作为子图&#xff0c;绘制在1个完整的大图中&#xff0c;并分别为每1个子图构建、显示标题的方法。 首先&#xff0c;我们明确一下本文的需求。现有…

Hadoop之路

hadoop更适合在liunx环境下运行&#xff0c;会节省后期很多麻烦&#xff0c;而用虚拟器就太占主机内存了&#xff0c;因此后面我们将把hadoop安装到wsl后进行学习,后续学习的环境是Ubuntu-16.04 &#xff08;windows上如何安装wsl&#xff09; 千万强调&#xff0c;有的命令一…

Web前端一套全部清晰 ② day2 HTML 标签之文字排版,图片、链接、音视频链接

虽然辛苦&#xff0c;我还是会选择那种滚烫的人生 —— 24.4.25 HTML初体验 1.HTML定义 HTML 超文本标记语言 超文本 —— 链接 标记 —— 标记也叫标签&#xff0c;带尖括号的文本 标签语法 开始标签 需要加粗的文字 结束标签 标签成对出现&#xff0c;中间包裹内容 <>里…