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         封裝類壓縮文件

將相關的封裝類打包如下,可複製到電腦,解壓後直接添加下面的文件到指定的工程項目,進行復用;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章