Qt中截图功能的实现
提要
需求:载入一张图片并显示,能够放大缩小,能够截取图片的某个矩形并保存。
原以为蛮简单的一个功能,事实上还是有点小复杂。
最简单Qt图片浏览器能够參考Qt自带的Demo:Image Viewer Example
看一下终于的实现效果:
图片的载入显示
这里须要实现一个QImageViewer的类。继承自QWidget。
图片用QPixmap来载入和显示,还有三个成员各自是图片的缩放因子,图片是否已经载入,viewer是否已经初始化,是否处于裁剪状态。
private:
QPixmap m_pixmap;
float scalling;
bool isLoaded;
bool isIntialised;
bool isCropping;
scalling值是用于记录图片的缩放比例。
显示图片仅仅要又一次定义paintEvent,在里面绘制m_pixmap就能够了。
void QImageViewer::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (m_pixmap.isNull())
{
return;
} QPainter painter(this);
if (isLoaded)
{
painter.setRenderHint(QPainter::SmoothPixmapTransform);
QSize pixSize = m_pixmap.size(); //For canvas's size not change when window's size change.
if (!isInitialised)
{
QSize initialSize = event->rect().size();
scaling = 1.0 * initialSize.width() / pixSize.width();
isInitialised = true;
}
pixSize.scale(scaling * pixSize, Qt::KeepAspectRatio);
this->setMinimumSize(pixSize); QPoint topleft;
topleft.setX((this->width() - pixSize.width()) / 2);
topleft.setY((this->height() - pixSize.height()) / 2); painter.drawPixmap(topleft, m_pixmap.scaled(pixSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
}
截图
思路非常easy。首先选择进入裁剪模式(能够在menu中加入一个action或者用快捷键),然后在图片上拖出一个矩形。最后按回车。截取完毕,Ctrl + s 保存。
主要用到的是 QPixmap::copy(QRect rect) 方法。
首先要实现一个CropRect类,用于记录截取的矩形。
#ifndef CROPRECT_H
#define CROPRECT_H
#include <QPoint>
#include <QPainter> class CropRect
{
public:
CropRect(){}
~CropRect(){}
void setStart(QPoint s)
{
start = s;
} void setEnd(QPoint e)
{
end = e;
} QPoint startPoint() const
{
return start;
} QPoint endPoint() const
{
return end;
}
void reset()
{
QPoint P(0,0);
start = P;
end = P;
} QSize& size() const
{
return QSize(width(), height());
} int height() const
{
return qAbs(startPoint().y() - endPoint().y());
} int width() const
{
return qAbs(startPoint().x() - endPoint().x());
} private:
QPoint start;
QPoint end;
}; #endif // CROPRECT_H
注意这里的start和end都是相对于当前要截取的图片的位置。也就是图片的左上角的坐标是CropRect的原点。
接下来再QImageviewer中实现两个辅助的方法。
由于图片并非恰好全然充满窗口,所以在设定裁剪框的时候,鼠标假设没有点在图片上,就应该不裁剪。所以首先应该推断屏幕中的某个点是否在图片上。
bool QImageViewer::isContainPoint(QPoint &p)
{
QSize s = m_pixmap.size();
s.scale(scaling * s, Qt::KeepAspectRatio); //If pixmap bigger than current window.
if ((s.height() > this->rect().height()) && (s.width() > this->rect().width()))
{
return true;
} QPoint topleft;
topleft.setX((this->width() - s.width()) / 2);
topleft.setY((this->height() - s.height()) / 2); QRect rect(topleft, s);
return rect.contains(p);
}
第二个方法就是将鼠标的位置映射到图片上的位置,由于截图主要是对图片进行操作。
图片的大小和窗口大小有四种情况,第一种是图片高度和宽度都大于窗口。
逻辑就是将红色和绿色部分相加,得到点对于当前图片(已缩放)的位置。最后除以scalling就能够了。
另一种是图片全然在窗口里面
这样的情况将红色减去绿色部分,得到点对于当前图片(已缩放)的位置。最后除以scalling就能够了。还有两种简单的情况就不展开了,详细代码例如以下:
QPoint QImageViewer::mapToPixmap(QPoint &screenPoint)
{
QSize pixmapSize = m_pixmap.size();
pixmapSize.scale(scaling * pixmapSize, Qt::KeepAspectRatio); //Get the position of screenPoint to the pixmap in show.
QPoint tmpPos;
if (pixmapSize.width() > this->width() && pixmapSize.height() > this->height())
{
tmpPos.setX(pixmapSize.width() - (this->width() - screenPoint.x()));
tmpPos.setY(pixmapSize.height() - (this->height() - screenPoint.y()));
}
else if (pixmapSize.width() < this->width() && pixmapSize.height() > this->height())
{
tmpPos.setX(screenPoint.x() - (this->width() - pixmapSize.width()) / 2);
tmpPos.setY(pixmapSize.height() - (this->height() - screenPoint.y()));
}
else if (pixmapSize.width() > this->width() && pixmapSize.height() < this->height())
{
tmpPos.setX(pixmapSize.width() - (this->width() - screenPoint.x()));
tmpPos.setY(screenPoint.y() - (this->height() - pixmapSize.height()) / 2);
}
else{
QPoint topleft;
topleft.setX((this->width() - pixmapSize.width()) / 2);
topleft.setY((this->height() - pixmapSize.height()) / 2);
tmpPos.setX(screenPoint.x() - topleft.x());
tmpPos.setY(screenPoint.y() - topleft.y());
}
//return the position to the real pixmap.*/
return QPoint(tmpPos.x() / scaling, tmpPos.y() / scaling);
}
这里採取了一个投机取巧的办法。就是利用了QPoint.setX() 和 QPoint.setY()方法假设传进去的是负值,那么就等于传进去0,所以少了一些小于0的推断。
接下来就是相应的鼠标事件,用于确定裁剪框的大小
void QImageViewer::mousePressEvent(QMouseEvent *event)
{
if ((event->buttons() == Qt::LeftButton) && isContainPoint(event->pos()) && isCropping)
{
cropRect.setStart(mapToPixmap(event->pos()));
cropRect.setEnd(mapToPixmap(event->pos()));
isStartingCrop = true;
}
} void QImageViewer::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() == Qt::LeftButton) && isStartingCrop)
{
if (isContainPoint(event->pos()))
{
cropRect.setEnd(mapToPixmap(event->pos()));
update();
}
}
} void QImageViewer::mouseReleaseEvent(QMouseEvent *e)
{
QRect rect(cropRect.startPoint(), cropRect.endPoint());
isStartingCrop = false;
}
裁剪框绘制的相关代码。这里也依据startpoint 和endpoint的相对位置,也有几种情况须要注意一下。
更炫酷的动态蚂蚁线能够參考:Qt中绘制蚂蚁线
if (isCropping)
{
qDebug() << cropRect.width() << cropRect.height();
//painter.setPen(Qt::darkGreen);
QPen pen;
pen.setBrush(Qt::red);
pen.setStyle(Qt::DashLine);
pen.setWidth(1);
painter.setPen(pen); //start point in the left to the end point.
if (cropRect.startPoint().x() < cropRect.endPoint().x())
{ if (cropRect.startPoint().y() < cropRect.endPoint().y())
{
//start point in the top to the end point.
painter.drawRect(topleft.x() + cropRect.startPoint().x() * scaling, topleft.y() + cropRect.startPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
}
else{
//start point in the bottom to the end point.
painter.drawRect(topleft.x() + cropRect.startPoint().x() * scaling, topleft.y() + cropRect.endPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
}
}
else
{
if (cropRect.startPoint().y() > cropRect.endPoint().y())
{
painter.drawRect(topleft.x() + cropRect.endPoint().x() * scaling, topleft.y() + cropRect.endPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
}
else{
painter.drawRect(topleft.x() + cropRect.endPoint().x() * scaling, topleft.y() + cropRect.startPoint().y() * scaling, cropRect.width() * scaling, cropRect.height() * scaling);
}
}
}
最后就是裁剪了
void QImageViewer::cropFinished()
{
QRect crop(cropRect.startPoint(), QSize(cropRect.width(), cropRect.height()));
QPixmap cropped = m_pixmap.copy(crop);
m_pixmap = cropped;
cropRect.reset();
isCropping = false;
this->update();
}
打完收工。
Qt中截图功能的实现的更多相关文章
- Qt5:Qt中屏幕或窗口截图功能的实现
要想在Qt中实现屏幕或窗口截图功能 ,通常有两种方法: 1 -- 使用 QPixmap 类 2 -- 使用 QScreen类 然而虽然俩两种方法用到的类不相同,但是调用到的类成员函数的函数名称和参 ...
- Qt调用dll中的功能函数
声明: 事先我已经自己动手写了一个简单的dll文件(myDLL.dll),C版接口的.并且用我前两篇有关DLL文章里面的方法,从dll中导出了导入库(.lib)文件,dll中有两个函数,原型如下: ...
- 封装selenium自动化框架中的截图功能
对selenium自带的截图功能进行封装: 以下为封装的代码,自定义一个.py文件即可,图片路径自己设置一个. #coding:utf-8 class Screen(object): ''' 封装的截 ...
- Qt中OpenGL的初步使用
结果预览: 一.代码5个文件 //glwidget.h #ifndef GLWIDGET_H #define GLWIDGET_H #include <QGLWidget> class G ...
- QT 中Widgets-Scene3d例子学习
QT中自带的例子widgets-scene3d实现在基于Widget的应用程序中使用qml 3d场景的功能,我在此基础上,将basicshapes-cpp的例子加以嵌入: 相关代码如下: C++ C ...
- canvas与html5实现视频截图功能
这段时间一直在研究canvas,突发奇想想做一个可以截屏视频的功能,然后把图片拉去做表情包,哈哈哈哈哈哈~~ 制作方法: 1.在页面中加载视频 在使用canvas制作这个截图功能时,首先必须保证页面上 ...
- QT 中 关键字讲解(emit,signal,slot)
Qt中的类库有接近一半是从基类QObject上继承下来,信号与反应槽(signals/slot)机制就是用来在QObject类或其子类间通讯的方法.作为一种通用的处理机制,信号与反应槽非常灵活,可以携 ...
- 第47课 Qt中的调色板
1. QPalette类 (1)QPalette类提供了绘制QWidget组件的不同状态所使用的颜色. (2)QPalette对象包含了3个状态的颜色描述 ①激活颜色组(Active):组件获得焦点使 ...
- 第38课 Qt中的事件处理(上)
1. GUI程序原理回顾 (1)图形界面应用程序的消息处理模型 (2)思考:操作系统发送的消息如何转变为Qt信号 2. Qt中的事件处理 (1)Qt平台将系统产生的消息转换为Qt事件 ①Qt事件是一个 ...
随机推荐
- 使用GDI+绘制的360风格按钮控件(使用CN_DRAWITEM消息重绘,并使用TGPGraphics,TGPPen,TGPImage,TGPBitmap等)good
将下面的代码拷贝到一个单元中,创建一个包,加入这个单元后安装.使用的时候设置好背景颜色,边框颜色,图标(png格式)相对路径的文件名称.这个控件可以利用PNG图像的颜色透明特性,背景色默认透明度为50 ...
- jQuery 自学笔记—8 常见操作
jQuery 拥有可操作 HTML 元素和属性的强大方法. jQuery DOM 操作 jQuery 中非常重要的部分,就是操作 DOM 的能力. jQuery 提供一系列与 DOM 相关的方法,这使 ...
- C# Http以文件的形式上传文件
以下的是上传的方法: // <summary> /// 将本地文件上传到指定的服务器(HttpWebRequest方法) /// </summary> /// <para ...
- javascript --- 事件托付
javascript 之 事件托付 长处:1.提高性能(仅仅须要对父级进行操作,子节点相同会拥有其相关属性和方法) 2.对于新加入的事件.也让其拥有父级事件的属性 <!doctype html& ...
- MySQL HINT:Straight_JOIN
来自生产环境的朋友.可能都会碰到: 原本运行良好的查询语句,过了一段时间后,可能会突然变得很糟糕 一个很大可能的原因就是数据分布情况发生了变化 从而导致MySQL优化 ...
- Virtualbox mouse move in and out and file share with windows
How to use Virstalbox to share files with Linux and Windows, and to move the mouse in and out Virtua ...
- eclipse中我要同时看两个console
eclipse中我要同时看两个console 有一个按钮“New Console View”,可以让你再建一个Console,还有一个按钮“Display Selected Console”,可以在两 ...
- leetcode回文子串拆分-最小拆分次数
转载请注明来自souldak,微博:@evagle 上一篇是要输出所有的可能拆分,这回是要输出拆分次数最少的切割次数. 如果直接按照上一篇那么做的话,就会超时,因为我们在判断s[i][j]是否是回文的 ...
- [置顶] iframe使用总结(实战)
说在前面的话,iframe是可以做很多事情的. 例如: a>通过iframe实现跨域; b>使用iframe解决IE6下select遮挡不住的问题 c>通过iframe解决Ajax的 ...
- CI(codeigniter)框架,routes.php设置正确,但是显示服务器错误,是__construct少写了一个下划线
今天弄了一下CI框架,大概看了一下文档,感觉CI框架非常精简,但是在做的时候遇到了问题,CI文档中提供了一个新闻系统的例子,所有工作都做完了,在浏览器中打开相对应的url是,却显示“服务器错误”,一点 ...