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