Qt中截图功能的实现

来源:互联网 发布:思科通过mac地址查询ip 编辑:程序博客网 时间:2024/06/02 05:15

Qt中截图功能的实现


【来源】http://blog.csdn.net/silangquan/article/details/41791609

需求:加载一张图片并显示,可以放大缩小,可以截取图片的某个矩形并保存。

原以为蛮简单的一个功能,其实还是有点小复杂。

最简单Qt图片浏览器可以参考Qt自带的Demo:Image Viewer Example

看一下最终的实现效果:







图片的加载显示

这里需要实现一个QImageViewer的类,继承自QWidget。

图片用QPixmap来加载和显示,还有三个成员分别是图片的缩放因子,图片是否已经加载,viewer是否已经初始化,是否处于裁剪状态。

[cpp] view plain copy
print?
  1. private:  
  2. QPixmap m_pixmap;  
  3. float scalling;  
  4. bool isLoaded;  
  5. bool isIntialised;  
  6. bool isCropping;  

scalling值是用于记录图片的缩放比例。

显示图片只要重新定义paintEvent,在里面绘制m_pixmap就可以了。

[cpp] view plain copy
print?
  1. void QImageViewer::paintEvent(QPaintEvent *event)  
  2. {  
  3.     QWidget::paintEvent(event);  
  4.     if (m_pixmap.isNull())  
  5.     {  
  6.         return;  
  7.     }  
  8.   
  9.     QPainter painter(this);  
  10.     if (isLoaded)  
  11.     {  
  12.         painter.setRenderHint(QPainter::SmoothPixmapTransform);  
  13.         QSize pixSize = m_pixmap.size();  
  14.   
  15.         //For canvas's size not change when window's size change.  
  16.         if (!isInitialised)  
  17.         {  
  18.             QSize initialSize = event->rect().size();  
  19.             scaling = 1.0 * initialSize.width() / pixSize.width();  
  20.             isInitialised = true;  
  21.         }  
  22.         pixSize.scale(scaling * pixSize, Qt::KeepAspectRatio);  
  23.         this->setMinimumSize(pixSize);  
  24.   
  25.         QPoint topleft;  
  26.         topleft.setX((this->width() - pixSize.width()) / 2);  
  27.         topleft.setY((this->height() - pixSize.height()) / 2);  
  28.   
  29.         painter.drawPixmap(topleft, m_pixmap.scaled(pixSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));  
  30.     }  
  31. }  

截图

思路很简单,首先选择进入裁剪模式(可以在menu中添加一个action或者用快捷键),然后在图片上拖出一个矩形,最后按回车,截取完成,Ctrl + s 保存。

主要用到的是 QPixmap::copy(QRect rect) 方法。


首先要实现一个CropRect类,用于记录截取的矩形。

[cpp] view plain copy
print?
  1. #ifndef CROPRECT_H  
  2. #define CROPRECT_H  
  3. #include <QPoint>  
  4. #include <QPainter>  
  5.   
  6. class CropRect  
  7. {  
  8. public:  
  9.     CropRect(){}  
  10.     ~CropRect(){}  
  11.     void setStart(QPoint s)   
  12.     {   
  13.         start = s;   
  14.     }   
  15.   
  16.     void setEnd(QPoint e)   
  17.     {   
  18.         end = e;   
  19.     }   
  20.   
  21.     QPoint startPoint() const  
  22.     {   
  23.         return start;   
  24.     }   
  25.   
  26.     QPoint endPoint() const  
  27.     {   
  28.         return end;   
  29.     }   
  30.     void reset()  
  31.     {  
  32.         QPoint P(0,0);  
  33.         start = P;  
  34.         end = P;  
  35.     }  
  36.   
  37.     QSize& size() const  
  38.     {  
  39.         return QSize(width(), height());  
  40.     }  
  41.       
  42.     int height() const  
  43.     {  
  44.         return qAbs(startPoint().y() - endPoint().y());  
  45.     }  
  46.   
  47.     int width() const  
  48.     {  
  49.         return qAbs(startPoint().x() - endPoint().x());  
  50.     }  
  51.   
  52. private:  
  53.     QPoint start;  
  54.     QPoint end;  
  55. };  
  56.   
  57. #endif // CROPRECT_H  


注意这里的start和end都是相对于当前要截取的图片的位置,也就是图片的左上角的坐标是CropRect的原点。


接下来再QImageviewer中实现两个辅助的方法。

由于图片并不是恰好完全充满窗体,所以在设定裁剪框的时候,鼠标如果没有点在图片上,就应该不裁剪,所以首先应该判断屏幕中的某个点是否在图片上。

[cpp] view plain copy
print?
  1. bool QImageViewer::isContainPoint(QPoint &p)  
  2. {  
  3.     QSize s = m_pixmap.size();  
  4.     s.scale(scaling * s, Qt::KeepAspectRatio);  
  5.   
  6.     //If pixmap bigger than current window.  
  7.     if ((s.height() > this->rect().height()) && (s.width() > this->rect().width()))  
  8.     {  
  9.         return true;  
  10.     }  
  11.   
  12.     QPoint topleft;  
  13.     topleft.setX((this->width() - s.width()) / 2);  
  14.     topleft.setY((this->height() - s.height()) / 2);  
  15.   
  16.     QRect rect(topleft, s);  
  17.     return rect.contains(p);  
  18. }  

第二个方法就是将鼠标的位置映射到图片上的位置,因为截图主要是对图片进行操作。

图片的大小和窗口大小有四种情况,第一种是图片高度和宽度都大于窗体。


逻辑就是将红色和绿色部分相加,得到点对于当前图片(已缩放)的位置,最后除以scalling就可以了。


还有一种是图片完全在窗口里面


这种情况将红色减去绿色部分,得到点对于当前图片(已缩放)的位置,最后除以scalling就可以了。还有两种简单的情况就不展开了,具体代码如下:

[cpp] view plain copy
print?
  1. QPoint QImageViewer::mapToPixmap(QPoint &screenPoint)  
  2. {  
  3.     QSize pixmapSize = m_pixmap.size();  
  4.     pixmapSize.scale(scaling * pixmapSize, Qt::KeepAspectRatio);  
  5.   
  6.     //Get the position of screenPoint to the pixmap in show.   
  7.     QPoint tmpPos;  
  8.     if (pixmapSize.width() > this->width() && pixmapSize.height() > this->height())  
  9.     {  
  10.         tmpPos.setX(pixmapSize.width() - (this->width() - screenPoint.x()));  
  11.         tmpPos.setY(pixmapSize.height() - (this->height() - screenPoint.y()));  
  12.     }  
  13.     else if (pixmapSize.width() < this->width() && pixmapSize.height() > this->height())  
  14.     {  
  15.         tmpPos.setX(screenPoint.x() - (this->width() - pixmapSize.width()) / 2);  
  16.         tmpPos.setY(pixmapSize.height() - (this->height() - screenPoint.y()));  
  17.     }  
  18.     else if (pixmapSize.width() > this->width() && pixmapSize.height() < this->height())  
  19.     {  
  20.         tmpPos.setX(pixmapSize.width() - (this->width() - screenPoint.x()));  
  21.         tmpPos.setY(screenPoint.y() - (this->height() - pixmapSize.height()) / 2);  
  22.     }  
  23.     else{  
  24.         QPoint topleft;  
  25.         topleft.setX((this->width() - pixmapSize.width()) / 2);  
  26.         topleft.setY((this->height() - pixmapSize.height()) / 2);  
  27.         tmpPos.setX(screenPoint.x() - topleft.x());  
  28.         tmpPos.setY(screenPoint.y() - topleft.y());  
  29.     }  
  30.     //return the position to the real pixmap.*/  
  31.     return QPoint(tmpPos.x() / scaling, tmpPos.y() / scaling);  
  32. }  


这里采取了一个投机取巧的办法,就是利用了QPoint.setX() 和 QPoint.setY()方法如果传进去的是负值,那么就等于传进去0,所以少了一些小于0的判断。


接下来就是对应的鼠标事件,用于确定裁剪框的大小

[cpp] view plain copy
print?
  1. void QImageViewer::mousePressEvent(QMouseEvent *event)  
  2. {  
  3.     if ((event->buttons() == Qt::LeftButton) && isContainPoint(event->pos()) && isCropping)  
  4.     {  
  5.         cropRect.setStart(mapToPixmap(event->pos()));  
  6.         cropRect.setEnd(mapToPixmap(event->pos()));  
  7.         isStartingCrop = true;  
  8.     }  
  9. }  
  10.   
  11. void QImageViewer::mouseMoveEvent(QMouseEvent *event)  
  12. {  
  13.     if ((event->buttons() == Qt::LeftButton) && isStartingCrop)  
  14.     {  
  15.         if (isContainPoint(event->pos()))  
  16.         {  
  17.             cropRect.setEnd(mapToPixmap(event->pos()));  
  18.             update();  
  19.         }  
  20.     }  
  21. }  
  22.   
  23. void QImageViewer::mouseReleaseEvent(QMouseEvent *e)  
  24. {  
  25.     QRect rect(cropRect.startPoint(), cropRect.endPoint());  
  26.     isStartingCrop = false;  
  27. }  


裁剪框绘制的相关代码,这里也根据startpoint 和endpoint的相对位置,也有几种情况需要注意一下。更炫酷的动态蚂蚁线可以参考:Qt中绘制蚂蚁线

[cpp] view plain copy
print?
  1. if (isCropping)  
  2. {  
  3.     qDebug() << cropRect.width() << cropRect.height();  
  4.     //painter.setPen(Qt::darkGreen);  
  5.     QPen pen;  
  6.     pen.setBrush(Qt::red);  
  7.     pen.setStyle(Qt::DashLine);  
  8.     pen.setWidth(1);  
  9.     painter.setPen(pen);  
  10.   
  11.     //start point in the left to the end point.  
  12.     if (cropRect.startPoint().x() < cropRect.endPoint().x())  
  13.     {  
  14.   
  15.         if (cropRect.startPoint().y() < cropRect.endPoint().y())  
  16.         {  
  17.             //start point in the top to the end point.  
  18.             painter.drawRect(topleft.x() + cropRect.startPoint().x() * scaling, topleft.y() + cropRect.startPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);  
  19.         }  
  20.         else{  
  21.             //start point in the bottom to the end point.  
  22.             painter.drawRect(topleft.x() + cropRect.startPoint().x() * scaling, topleft.y() + cropRect.endPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);  
  23.         }  
  24.     }  
  25.     else  
  26.     {  
  27.         if (cropRect.startPoint().y() > cropRect.endPoint().y())  
  28.         {  
  29.             painter.drawRect(topleft.x() + cropRect.endPoint().x() * scaling, topleft.y() + cropRect.endPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);  
  30.         }  
  31.         else{  
  32.             painter.drawRect(topleft.x() + cropRect.endPoint().x() * scaling, topleft.y() + cropRect.startPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);  
  33.         }  
  34.     }  
  35. }  


最后就是裁剪

[cpp] view plain copy
print?
  1. void QImageViewer::cropFinished()  
  2. {  
  3.     QRect crop(cropRect.startPoint(), QSize(cropRect.width(), cropRect.height()));  
  4.     QPixmap cropped = m_pixmap.copy(crop);  
  5.     m_pixmap = cropped;  
  6.     cropRect.reset();  
  7.     isCropping = false;  
  8.     this->update();  
  9. }  

打完收工。

0
0
 
 

  相关文章推荐
  • Qt 之 简单截图功能(三)实现可拖拽选中区域
  • QT实现类似QQ的截图功能
  • Qt4简单截图功能的实现
  • Qt 之 简单截图功能(二)实现可移动选中区域
  • Qt中截图功能的实现
  • QT做类似QQ截图功能(带图片编辑功能)
  • Qt 实现 QQ 截图工具(开源OEasyScreenshot)
  • Qt学习(九) 截图程序的实现
  • 通过Qt截图工具讲注册热键的实现
  • 【Qt】Qt 导出 PDF,Android和Qt截图功能实现
原创粉丝点击