QT图片查看器封装-鼠标中心缩放、移动、截图、框选、切换播放

Qt图片查看器

目录

1       简介... 1

2       功能实现... 2

2.1      图片以鼠标为中心放大缩小功能... 2

2.2      图片移动功能... 4

2.3      图片框选人脸功能... 6

2.4      图片无感知切换播放界面... 10

2.5      图片截图功能... 11

3       总结... 13

4       封装类压缩文件... 14

 

 

1         简介

Qt程序开发中经常需要展示图片,要实现图片的放大缩小,而且放大缩小是以鼠标为中心进行缩放;还要实现图片的截图等功能;左右切换查看,列表选择查看;对图片进行拖动,框选出图片中的人林,图片的截图功能,图片直接无感知切换播放,这些都是常规的图片操作,每次编写图片查看程序时,为了避免重复开发,将图片查看封装成一个类,可以在多个项目中使用;开发效果图如下图所示。

 

 

 

界面的交互界面设计如下图所示:

 

2         功能实现

2.1   图片以鼠标为中心放大缩小功能

鼠标滚动时,以鼠标所在位置为中心,进行缩放图片;关键是要计算放大缩小之后的中心位置;先按比例缩放图片的宽高,然后再计算图片坐标偏移量,将鼠标移动到新的位置,保证是鼠标位置为中心进行缩放,不会发生偏移;

放大效果图如下所示

 

 

 

缩小效果如下图所示

 

 

 

实现代码如下所示:

void DetailShowSinglePic::wheelEvent(QWheelEvent *event)

{

    QRect rect = labelPicLeft.geometry();

    QPoint cursorpositon = QCursor().pos();

    QPoint pos = labelPicLeft.mapFromGlobal(cursorpositon);

    double scale = 0.15;

    if (event->delta() > 0)

    {

        m_zoomscale += scale;

        int x = rect.x() - scale*pos.x();

        int y = rect.y() - scale*pos.y();

        labelPicLeft.hide();//图片移动时,会有闪烁重叠的问题,所以先hide,再show;

        labelPicLeft.move(x, y);

        labelPicLeft.show();

        labelPicLeft.resize(rect.width()*(1 + scale), rect.height()*(1 + scale));

       

    }

    else

    {

        m_zoomscale -= scale;

        if (m_zoomscale < scale)

        {

            m_zoomscale = scale;

            return;

        }

        int x = rect.x() + scale*pos.x();

        int y = rect.y() + scale*pos.y();

        labelPicLeft.hide();

        labelPicLeft.move(x, y);

        labelPicLeft.show();

        labelPicLeft.resize(rect.width()*(1 - scale), rect.height()*(1 - scale));

       

    }

}

在开发过程中遇到移动Qlabel图片时,出现闪烁和重影问题,经过多次调试,还是有这样的情况,最后是移动之前隐藏,移动之后显示,解决了重影闪烁的问题;

2.2   图片移动功能

要实现图片在窗口中移动,需要实现鼠标按下、移动、释放三个函数;鼠标按下的时候保存起始位置,设置鼠标形状;鼠标移动响应函数中设置图片的位置,并重置起始位置;鼠标释放的时候,恢复鼠标形状;

 

 

 

void DetailShowSinglePic::mousePressEvent(QMouseEvent *event)

{

   

    if (ui.widgetmid->underMouse())

    {

        m_bmousepressed = true;

        QPoint cursorpositon = QCursor().pos();

        m_mouseStartPos = ui.widgetmid->mapFromGlobal(cursorpositon);

        setCursor(Qt::ClosedHandCursor);

    }

   

}

 

void DetailShowSinglePic::mouseMoveEvent(QMouseEvent *event)

{

    if (ui.widgetmid->underMouse())

    {

        if (m_bmousepressed)

        {

            QPoint cursorpositon = QCursor().pos();

            QPoint endmouse = ui.widgetmid->mapFromGlobal(cursorpositon);

            QPoint labelpicpos = labelPicLeft.pos();

            labelpicpos.setX(labelpicpos.x() + endmouse.x() - m_mouseStartPos.x());

            labelpicpos.setY(labelpicpos.y() + endmouse.y() - m_mouseStartPos.y());

            labelPicLeft.move(labelpicpos);

            m_mouseStartPos = endmouse;

        }

       

    }

}

 

void DetailShowSinglePic::mouseReleaseEvent(QMouseEvent *event)

{

    setCursor(Qt::ArrowCursor);

    m_bmousepressed = false;

}

2.3   图片框选人脸功能

如下图所示,需要在图片中画出一个四角矩形框,把目标的人脸给圈出来;让用户更加醒目的分辨出目标任务;而且能够随着图片的缩放而缩放,不会发生偏移;

实现方式新建一个类继承QLabel,然后实现paintEvent(QPaintEvent *)函数,在paintEvent(QPaintEvent *)函数用Qpaint画出图片,然后再图片上绘制矩形框;画图片要保持图片的宽高比,且最大占据Qlabel的空间;同时计算出人脸坐标再图片中位置,绘制出人脸图片;实现效果如下图所示:

 

 

 

实例代码也封装成了一个类,可以复用与其他项目:

头文件

#ifndef ASPECTRATIOPIXMAPLABEL_H

#define ASPECTRATIOPIXMAPLABEL_H

 

#include <QLabel>

#include<QPaintEvent>

#include"FaceDefine.h"

class AspectRatioPixmapLabel : public QLabel

{

    Q_OBJECT

public:

   

    AspectRatioPixmapLabel(QWidget *parent = 0);

    QPixmap scaledPixmap() const;

    void setPath(QString strPath)

    {

        this->clear();

        m_strPath = strPath;

        pix = QPixmap(m_strPath);

        QSize labelsize = this->size();

        pix = pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);

        setPixmap(pix);

        m_bFaceShow = false;

        update();

    }

    void setFaceRect(FaceRect rect)

    {

        m_bFaceShow = true;

        m_rectFace = rect;

       

        update();

    }

    void setScalPixmap(const QPixmap p);

    QString getPath() { return m_strPath; }

protected:

    void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;

    //void resizeEvent(QResizeEvent *event);

   

 

   

   

private:

    QPixmap pix;

    QString m_strPath = "";

    QRect m_rectPic;//图片在图片区的位置,可能未完全填充  m_picrect包含m_rect

    bool m_bFaceShow = false;

    FaceRect m_rectFace;//人脸相对值比例

};

 

 

#endif // ASPECTRATIOPIXMAPLABEL_H

源文件

#include "aspectratiopixmaplabel.h"

#include<QPainter>

 

AspectRatioPixmapLabel::AspectRatioPixmapLabel(QWidget *parent) :

    QLabel(parent)

{

    setWindowModality(Qt::NonModal);

    setStyleSheet("font-family: MicrosoftYaHeiUI;font-size: 14px;color:#028EC0;letter-spacing: 0;line-height: 20px;background:#000000;");

    setScaledContents(false);

    show();

}

 

void AspectRatioPixmapLabel::setScalPixmap(const QPixmap  p)

{

    setPixmap(scaledPixmap());

    setContentsMargins(0,0,0,0);

}

 

QPixmap AspectRatioPixmapLabel::scaledPixmap() const

{

    int low = this->size().width() < this->size().height() ? this->size().width() : this->size().height();

    QPixmap pixw= pix.scaled(low,low, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    QSize szie = pixw.size();

    return pixw;

}

void AspectRatioPixmapLabel::paintEvent(QPaintEvent *env)

{

    //让图片按照Qlabel的大小进行缩放,保持宽高比。

    //clear();

    QPainter painter(this);

    if (m_strPath!="")

    {

        QSize labelsize = this->size();

        pix = QPixmap(m_strPath);

        pix = pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);

        QSize pixsize = pix.size();

        //根据图片的宽高和QLabel的宽高计算绘图的矩形区域和坐标

        QRect rectPic(0, 0, pix.size().width(), pix.size().height());

 

        if (pix.width() < this->width())

        {

            rectPic.setX((this->width() - pix.width()) / 2);

            rectPic.setY(0);

            rectPic.setWidth(pix.size().width());

            rectPic.setHeight(pix.size().height());

        }

        if (pix.height() < this->height())

        {

            rectPic.setX(0);

            rectPic.setY((this->height() - pix.height()) / 2);

            rectPic.setWidth(pix.size().width());

            rectPic.setHeight(pix.size().height());

        }

        QSize pimageszie = pix.toImage().size();

        painter.drawPixmap(rectPic, pix);

        //setPixmap(pix);

        m_rectPic = rectPic;

       

       

        //painter.setPen(QPen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap));

        //painter.drawRect(QRect(50, 50, 200, 200));

    }

    if (m_bFaceShow)

    {

        //QRect FaceRectContrast;

        int x = m_rectFace.x * m_rectPic.width() + m_rectPic.left();

        int y = m_rectFace.y * m_rectPic.height() + m_rectPic.top();

        int width = m_rectFace.width * m_rectPic.width();

        int height = m_rectFace.height * m_rectPic.height();

        /*FaceRectContrast.setX(x);

        FaceRectContrast.setY(y);

        FaceRectContrast.setWidth(width);

        FaceRectContrast.setHeight(height);*/

        painter.setPen(QPen(QColor("#FF9c38"), 2, Qt::SolidLine, Qt::RoundCap));

        //左上

        painter.drawLine(x, y, x + 5, y);

        painter.drawLine(x, y, x, y + 5);

        //左下

        painter.drawLine(x, y + height, x + 5, y + height);

        painter.drawLine(x, y + height, x, y + height - 5);

        //右上

        painter.drawLine(x + width, y, x + width - 5, y);

        painter.drawLine(x + width, y, x + width, y + 5);

        //右下

        painter.drawLine(x + width, y + height, x + width - 5, y + height);

        painter.drawLine(x + width, y + height, x + width, y + height - 5);

    }

   

}

2.4   图片无感知切换播放界面

 

 

在图片查看的时候,直接定位到视频中图片播放的位置,进行播放,并做到用户无感知;采用两层结构,上层是图片查看,下层是视频播放,图片是视频抓拍的图片;所以要实现用户无感知的切换,图片显示窗口和视频播放窗口同大小,且图片显示和视频播放都是按照分辨率的宽高比进行同比例缩放,这样图片和视频在窗口中相同的位置显示;上下两次切换时才有直接在图片上播放视频的效果;

void DetailShowSinglePic::SlotStartPlayVideo()

{

    QString strvideo = "";

    QString time = "";

    if (m_currentAttri.contains("location"))

    {

         strvideo = m_currentAttri.value("location").toString();

    }

    if (strvideo == "")

    {

        LOG_ERROR("video path is empty!");

        return;

    }

    if (m_currentAttri.contains("time"))

    {

         time = m_currentAttri.value("time").toString();

    }

   

    m_VedioPlayWidget.StartPlayVedioStrTime(strvideo, time);

    int ret=m_VedioPlayWidget.SetScaleType(PLAYM4_ENUM_SCALE_FIT);// PLAYM4_ENUM_SCALE_FILL || type == PLAYM4_ENUM_SCALE_FIT

    m_VedioPlayWidget.setGeometry(0, 0, ui.widgetmid->width(), ui.widgetmid->height());

    m_VedioPlayWidget.show();

   

}

m_VedioPlayWidget是封装的一个播放窗口类;鼠标移动到上面之后,会出现标题栏和控制栏;

 

2.5   图片截图功能

有时需要对图片进行截图,或者在播放界面上进行截图,截图用于图片搜索,保存图片等功能;QT截取子窗口或者播放窗口图片分为两个步骤,先获取子窗口widget在屏幕中的坐标和宽高,然后调用抓取屏幕图片的方法抓取子窗口坐标和宽高的表示的区域;

 

 

 

(1)子窗口获取相对屏幕的坐标

假如要抓取子窗口widgetmid的图片,先计算widgetmid在整个屏幕中的坐标;

   QRect widgetRect;

    //widgetmid在屏幕中的坐标

    QPoint point = ui.widgetmid->mapToGlobal(QPoint(0, 0));

    widgetRect.setX(point.x());

    widgetRect.setY(point.y());

    //widgetmid的宽高

    widgetRect.setWidth(ui.widgetmid->width());

    widgetRect.setHeight(ui.widgetmid->height());

    //抓子窗口区域图片,并显示在CutPicWidget

    m_CutPicWidget.CutWidgetPic(widgetRect);

    //m_CutPicWidget显示窗口截图,覆盖在ui.widgetmid之上

    m_CutPicWidget.setGeometry(0, 0, ui.widgetmid->width(), ui.widgetmid->height());

    m_CutPicWidget.show();

    m_CutPicWidget.raise();

 

(2)抓取区域图片并保存

QPixmap m_widgetScreenPic;是一个变量

int CutPicWidget::CutWidgetPic(QRect rect)

{

    //抓取区域截图

    QScreen *screen = QGuiApplication::primaryScreen();

    m_widgetScreenPic = screen->grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height());//抓取widget的图片

    m_widgetScreenPic = m_widgetScreenPic.scaled(QSize(rect.width(), rect.height()), Qt::KeepAspectRatio);

    //显示图片

    ui.labelPic->setPixmap(m_widgetScreenPic);

    //保存图片

    QString filePathName = "cut-";

    filePathName += QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz");

    filePathName += ".png";

    filePathName = QDir::currentPath() + "/" + filePathName;

    m_widgetScreenPic.save(filePathName);

    return 0;

}

通过这两个步骤,就可以抓取指定窗口的图片,并显示保存;也可以抓取正在播放的视频画面;

 

3         总结

综上所述,本文实现了图片的查看的常用功能:缩放、移动、框选人脸、无感切换播放、截图等功能,并且对功能进行了封装,可以进行分类入库,用于其他项目,避免二次开发;

 

4         封装类压缩文件

将相关的封装类打包如下,可复制到电脑,解压后直接添加下面的文件到指定的工程项目,进行复用;

 

posted @ 2022-11-23 20:10  一字千金  阅读(1685)  评论(0编辑  收藏  举报