提要

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

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

最简单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中截图功能的实现的更多相关文章

  1. Qt5:Qt中屏幕或窗口截图功能的实现

    要想在Qt中实现屏幕或窗口截图功能 ,通常有两种方法: 1  -- 使用 QPixmap 类 2  -- 使用 QScreen类 然而虽然俩两种方法用到的类不相同,但是调用到的类成员函数的函数名称和参 ...

  2. Qt调用dll中的功能函数

    声明: 事先我已经自己动手写了一个简单的dll文件(myDLL.dll),C版接口的.并且用我前两篇有关DLL文章里面的方法,从dll中导出了导入库(.lib)文件,dll中有两个函数,原型如下:   ...

  3. 封装selenium自动化框架中的截图功能

    对selenium自带的截图功能进行封装: 以下为封装的代码,自定义一个.py文件即可,图片路径自己设置一个. #coding:utf-8 class Screen(object): ''' 封装的截 ...

  4. Qt中OpenGL的初步使用

    结果预览: 一.代码5个文件 //glwidget.h #ifndef GLWIDGET_H #define GLWIDGET_H #include <QGLWidget> class G ...

  5. QT 中Widgets-Scene3d例子学习

    QT中自带的例子widgets-scene3d实现在基于Widget的应用程序中使用qml 3d场景的功能,我在此基础上,将basicshapes-cpp的例子加以嵌入: 相关代码如下:  C++ C ...

  6. canvas与html5实现视频截图功能

    这段时间一直在研究canvas,突发奇想想做一个可以截屏视频的功能,然后把图片拉去做表情包,哈哈哈哈哈哈~~ 制作方法: 1.在页面中加载视频 在使用canvas制作这个截图功能时,首先必须保证页面上 ...

  7. QT 中 关键字讲解(emit,signal,slot)

    Qt中的类库有接近一半是从基类QObject上继承下来,信号与反应槽(signals/slot)机制就是用来在QObject类或其子类间通讯的方法.作为一种通用的处理机制,信号与反应槽非常灵活,可以携 ...

  8. 第47课 Qt中的调色板

    1. QPalette类 (1)QPalette类提供了绘制QWidget组件的不同状态所使用的颜色. (2)QPalette对象包含了3个状态的颜色描述 ①激活颜色组(Active):组件获得焦点使 ...

  9. 第38课 Qt中的事件处理(上)

    1. GUI程序原理回顾 (1)图形界面应用程序的消息处理模型 (2)思考:操作系统发送的消息如何转变为Qt信号 2. Qt中的事件处理 (1)Qt平台将系统产生的消息转换为Qt事件 ①Qt事件是一个 ...

随机推荐

  1. li里的a标签浮动了,为什么li本身也浮动了?

    <ul> <li><a href="#"></a></li> <li><a href="#& ...

  2. 详谈隐藏Tabbar的几种方法

    如今正在写的一个项目,涉及到了使用两个TabBar,然后我须要显示当中一个的时候,然后隐藏另外一个,可是中间却出现故障了.我查了一些资料,想总结一下关于TabBar的隐藏. 第一种方法是: //隐藏t ...

  3. hive编程指南--employees表数据定义

    hive编程指南中有个employees表,默认的分隔符比較繁杂,编辑起来不太方便(普通编辑器编辑的控制字符^A等被当成字符串处理了,没有起到分隔符的作用). 收集的解决方式例如以下: http:// ...

  4. Dvtm -- 平铺式终端

    Dvtm -- 平铺式终端_Linux伊甸园开源社区-24小时滚动更新开源资讯,全年无休! Dvtm -- 平铺式终端 关注我们: 如果你使用过 Dwn 或 Awesome 这种平铺式窗口管理器,一定 ...

  5. SilkTest高级进阶系列8 – 放下榔头,立地成佛

    在地球尚未毁灭的某天,手动测试的同事找到你所在的自动组,问是否可以帮助他们自动化一个需要连续添加100个条目的测试用例,因为他们觉得这是一个噩梦.你口头上说要先烟酒烟酒才能知道,但是内里早就满心欢喜: ...

  6. 201215-03-19---cocos2dx内存管理--具体解释

    因为cocos2dx我们的使用c++写的,所以内存管理就是一个绕只是去的坎,这个你不懂内存仅仅懂业务逻辑的话,还玩什么c++,今天看了半天这个东西,事实上本质上是理解的,可是就是有一个过不去的坎,最终 ...

  7. 类是公共,它应该被命名为.java文件声明

    当类的设置public时间,,public只要类的文件名必须是相同的,..这种错误可能发生在不同的

  8. poj1836--Alignment(dp,最长上升子序列变形)

    Alignment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13319   Accepted: 4282 Descri ...

  9. Andriod中绘(画)图----Canvas的使用具体解释

    转载请注明出处:http://blog.csdn.net/qinjuning     因为在网络上找到关于Canvas的使用都比較抽象,或许是我的逻辑思维不太好吧,总是感觉理解起来比較困难, 尤其是对 ...

  10. c++中volatile详解

    1. 为什么用volatile? C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrier.这是 BS 在 "The ...