QT框架普通截图运行效果

截图软件一般在后台运行,当用户按下快捷键CTRL+SHIFT+X时进入截图状态,整个屏幕颜色变得暗淡了一些。此时用户可以按下鼠标左键进行拉框操作,拉框区域即为截图区域。当用户释放鼠标左键时,截图完成,图像已存储到了剪切板中。用户如果认为效果不好可以重新拉框选取截图区域。用户可以按ESC键或者点击鼠标右键恢复到初始状态。用户也可以点击Save按钮保存所截取的图像到图片文件中。最后用户可以选择双击鼠标左键退出截图状态,屏幕恢复正常状态。

普通矩形截图效果

QT框架系统级热键的原理

怎么实现用户在任意时刻按下快捷键都可以进入截图状态呢?这就是所谓的系统级的热键的作用。QT框架本身没有热键功能,在Windows系统中使用Windows API来实现热键功能。

热键功能包含注册热键、反注册热键以及处理热键消息这几个步骤。

注册热键的例子如下:


int ButianyunHotKey::registerHotkey(const QKeySequence& ks)
{
QString str = QString("ButianyunHotKey%1").arg(ks.toString());
int id = GlobalAddAtomA(str.toUtf8().constData());
if (hotkeys_id_to_ks.contains(id))
{
return -1;
} uint vk = 0;
uint modifiers = 0; for (int i = 0; i < ks.count(); i++)
{
int key = ks[i];
if (key & Qt::KeyboardModifierMask)
{
if (key & Qt::ControlModifier)
{
modifiers |= MOD_CONTROL;
}
if (key & Qt::AltModifier)
{
modifiers |= MOD_ALT;
}
if (key & Qt::ShiftModifier)
{
modifiers |= MOD_SHIFT;
}
}
vk = key & 0xffU;
break;
} int ret = RegisterHotKey(0, id, modifiers, vk);
if (ret)
{
hotkeys_id_to_ks.insert(id, ks);
hotkeys_vk_modifiers_to_id.insert(butianyun_vk_modifiers(vk, modifiers), id);
return id;
}
else
{
qDebug() << "Failed to register hotkey";
GlobalDeleteAtom((ATOM)id);
}
return -1;
}

具体调用注册的例子:


ButianyunHotKey* hotkey = ButianyunHotKey::instance();
hotkey_id = hotkey->registerHotkey(QKeySequence("CTRL+SHIFT+X"));
connect(hotkey, &ButianyunHotKey::sig_hotkey, this, &ButianyunSnapWidget::slot_hotkey);

注册热键

热键必须注册后才能生效。


int RegisterHotKey(HWND hWnd,int id,UINT fsModifiers,UINT vk);

hWnd:窗口句柄,Windows应用程序特有的概念,这里可以使用0作为参数。

id:注册热键时应该使用OS范围内唯一的一个整数ID。这个ID可以使用另外一个Windows API来获取到,稍后介绍。

fsModifiers: 修饰符。表示热键是否包含CTRL、ALT、SHIFT等修饰键,分别用MOD_CONTROL、MOD_ALT、MOD_SHIFT表示,可以组合修饰符。

vk:虚拟键盘码,就是表示键盘上某一个键的代码。比如F1的虚拟键盘码可以用VK_F1表示,字母X的键盘码可以用’X’表示。

返回值

TRUE:成功。FALSE:失败。

反注册热键

一个热键被某一个应用程序注册之后,其它应用程序就无法重复注册了。因此热键资源是十分稀缺的资源。对于同一个热键,如果跟先注册的应用程序产生热键冲突那么后注册的应用程序就注册不了。


int UnregisterHotKey(HWND hWnd,int id);

hWnd:窗口句柄。

id:热键ID。

获取系统级唯一的整数ID

可以通过一个字符串来产生一个唯一ID。


ATOM GlobalAddAtomA (LPCSTR lpString);

LPCSTR: const char*。

返回值:

ATOM类型实际就是unsigned short类型。返回值就是产生的系统级别唯一整数ID。

删除系统级唯一整数ID

系统级别唯一整数ID也是一种系统资源,不再使用之后应该予以删除。


ATOM GlobalDeleteAtom (ATOM nAtom);

nAtom:待删除的ID

怎么处理热键事件?
在用户按下热键之后,Windows系统会向注册了这个热键的应用程序发送WM_HOTKEY热键消息。在QT应用程序中怎么处理这个热键消息呢?

QT应用程序可以使用原生事件过滤器来处理Windows消息,包括WM_HOTKEY。

原生事件过滤器

QAbstractNativeEventFilter这个类型提供了虚函数


virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *);

一个原生事件过滤器的例子如下:


struct ButianyunNativeEventFilter: public QAbstractNativeEventFilter
{
ButianyunNativeEventFilter(ButianyunHotKey* hk)
: hotkey(hk)
{
}
ButianyunHotKey* hotkey;
bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override
{
if (eventType == "windows_generic_MSG")
{
MSG* msg = reinterpret_cast<MSG*>(message);
if (WM_HOTKEY == msg->message)
{
uint modifiers = (uint)LOWORD(msg->lParam);
uint vk = (uint)HIWORD(msg->lParam);
uint wp = msg->wParam;
return hotkey->handleHotKey(wp, vk, modifiers);
}
}
return false;
}
};

安装原生事件过滤器的例子如下:


filter = new ButianyunNativeEventFilter(this);
qApp->installNativeEventFilter(filter);

QT框架截图的原理

截图窗口

截图实际上是先将整个屏幕画面保存到像素图中,然后将截图窗口调整到跟屏幕同样大小,并将像素图绘制到截图窗口中。所以在截图状态下用户看到的屏幕画面是截图窗口显示的图片而已,并不是原始的屏幕画面。

拉框操作

QT框架中提供了橡皮框类型QRubberBand。这个类型可以用于实现拉框操作。

在用户按下鼠标左键时启动拉框操作,鼠标移动过程中改变橡皮框大小,鼠标左键释放时完成拉框操作并以橡皮框区域作为截图区域。此时从屏幕画面的像素图中截取部分图像作为截图结果。

系统剪切板操作

QT框架提供了QClipboard类型用于实现剪切板操作,支持将文字、图像等数据保存到剪切板中。之后别的应用程序就可以从剪切板中读取放进去的截图图像。

QT框架高级截图功能

椭圆截图原理

对截取结果像素图设置一个椭圆裁剪区域即可实现椭圆区域截图。

QT框架椭圆截图原理

圆角矩形截图原理

对截取结果像素图设置一个圆角矩形裁剪区域即可实现圆角矩形截图。由于QRegion类型无法直接设置圆角矩形裁剪区域,可以先创建一个圆角矩形绘图路径然后再设置裁剪区域为这个绘图路径,间接实现了圆角矩形截图。

QT框架圆角矩形截图原理

圆角矩形截图源代码

差别是在生成截图结果时对像素图做了裁剪处理,将裁剪区域之外的像素全部设置为透明色。


QImage img(target_pixmap.size(), QImage::Format_ARGB32);
img.fill(QColor(0, 0, 0, 0));
QPainter p(&img);
QPainterPath path;
path.addRoundRect(img.rect(), 30, 30);
p.setClipPath(path);
p.drawPixmap(img.rect(), target_pixmap, target_pixmap.rect());
target_pixmap = QPixmap::fromImage(img);

多边形截图原理

用户拖动鼠标选择多边形的顶点,然后将多边形加入绘图路径作为裁剪区域即可实现自定义形状截图。

QT框架拖变形截图效果

多边形截图源代码

生成截图结果


QImage img(target_pixmap.size(), QImage::Format_ARGB32);
img.fill(QColor(0, 0, 0, 0));
QPainter p(&img); if (polygon.size() > 2)
{
int mx = source_pixmap.width();
int my = source_pixmap.height();
for (int i = 0; i < polygon.size(); i++)
{
if (polygon.point(i).x() < mx)
{
mx = polygon.point(i).x();
}
if (polygon.point(i).y() < my)
{
my = polygon.point(i).y();
}
}
QPolygon temp;
for (int i = 0; i < polygon.size(); i++)
{
temp.append(QPoint(polygon.point(i).x() - mx,
polygon.point(i).y() - my));
} QPainterPath path;
path.addPolygon(temp);
p.setClipPath(path);
}
p.drawPixmap(img.rect(), target_pixmap, target_pixmap.rect());
target_pixmap = QPixmap::fromImage(img);

任意形状截图原理

实际上仍然是采用多边形裁剪区域进行图像截取,只是让用户通过拖动鼠标来选择多边形的顶点,所以效果上看就是任意形状的截图区域。

QT框架任意形状截图效果

任意形状截图源代码

生成截图结果的源代码


void ButianyunSnapWidget::updateSnap()
{
if (polygon.size() < 2)
{
return;
} target_rect = polygon.boundingRect();
if (target_rect.width() < 1 || target_rect.height() < 1)
{
return;
} target_pixmap = source_pixmap.copy(target_rect);
if (target_pixmap.isNull())
{
return;
} QImage img(target_pixmap.size(), QImage::Format_ARGB32);
img.fill(QColor(0, 0, 0, 0));
QPainter p(&img); if (polygon.size() > 2)
{
int mx = source_pixmap.width();
int my = source_pixmap.height();
for (int i = 0; i < polygon.size(); i++)
{
if (polygon.point(i).x() < mx)
{
mx = polygon.point(i).x();
}
if (polygon.point(i).y() < my)
{
my = polygon.point(i).y();
}
}
QPolygon temp;
for (int i = 0; i < polygon.size(); i++)
{
temp.append(QPoint(polygon.point(i).x() - mx,
polygon.point(i).y() - my));
} QPainterPath path;
path.addPolygon(temp);
p.setClipPath(path);
}
p.drawPixmap(img.rect(), target_pixmap, target_pixmap.rect());
target_pixmap = QPixmap::fromImage(img); }

完整源代码就不贴在这里了,有兴趣的朋友可以免费索取哈。

如果读者对如何快速全面了解QT框架感兴趣,可以看一下这篇文章:

bird:快速全面了解QT软件界面开发技术

如果读者对如何学习QT框架有兴趣,可以看一下这篇文章:

bird:如何学习C/C++/QT软件开发技术

如果您认为这篇文章对您有所帮助,请您一定立即点赞+喜欢+收藏,本文作者将能从您的点赞+喜欢+收藏中获取到创作新的好文章的动力。如果您认为作者写的文章还有一些参考价值,您也可以关注这篇文章的作者。

QT框架实现自定义形状截图效果的更多相关文章

  1. 制作自定义背景Button按钮、自定义形状Button的全攻略(转)

    在Android开发应用中,默认的Button是由系统渲染和管理大小的.而我们看到的成功的移动应用,都是有着酷炫的外观和使用体验的.因此,我们在开发产品的时候,需要对默认按钮进行美化.在本篇里,笔者结 ...

  2. Qt框架及模块认识

    小白自工作就接触Qt,一直都在使用Qt5.3.1版本,所以没有经历过大牛们把项目从Qt4程序到Qt5的烦恼,没准以后会碰到.对Qt所有的丰富的API表示惊叹,对于Qt的框架及模块认识也是极为模糊的,文 ...

  3. Qt计算器开发(三):执行效果及项目总结

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/guodongxiaren/article/details/26046543 执行效果 project ...

  4. Photoshop如何自定义形状

    Photoshop如何自定义形状,自定义形状定义一次,可以随便使用,而且形状无大小,填充后不会有像素问题,普通人可把常用的自定义成形状,很方便.PS中有一些自定义的形状,自己可以随便使用,但是不是很全 ...

  5. Qt 框架 开发HTTP 服务器 开发记录

    最近需求需要开发一款 HTTP ,然后由于先前接触过Qt,就直接用Qt写HTTP服务器了,也是为了当作练手,要不然是直接上HTTP框架的. 后端用C++ Qt框架 前端为了练手 当然是纯生的 js h ...

  6. QT笔记之自定义窗口拖拽移动

    1.QT自定义标题栏,拖拽标题栏移动窗口(只能拖拽标题,其他位置无法拖拽) 方法一: 转载:http://blog.sina.com.cn/s/blog_4ba5b45e0102e83h.html . ...

  7. 【iOS】7.4 定位服务->2.1.4 定位 - 官方框架CoreLocation 案例:指南针效果

    本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正. 本文相关目录: ================== 所属文集:[iOS]07 设备工具 === ...

  8. 使用Qt框架开发http服务器问题的记录

    最近需求需要开发一款 HTTP ,然后由于先前接触过Qt,就直接用Qt写HTTP服务器了,也是为了当作练手,要不然是直接上HTTP框架的. 后端用C++ Qt框架 前端为了练手 当然是纯生的 js h ...

  9. Three.js基础探寻六——文字形状与自定义形状

    1.文字形状 说起3d文字想起了早年word里的一些艺术字: 时间真快. 那么TextGeometry可以用来创建三维的文字形状. 使用文字形状需要下载和引用额外的字体库.这里,我们以 helveti ...

  10. IDA Pro 6.0使用Qt 框架实现了跨平台的UI

    IDA Pro 6.0使用Qt 框架实现了跨平台的UI.它的好处是插件编写者还可以直接使用 Qt 开发跨平台 UI.但是编剧呢? 在这篇博文中,我们将说明如何使用PySide使用IDAPython为 ...

随机推荐

  1. oeasy教您玩转vim - 81 - # 宏macro的进阶

    ​ 宏的进阶 macro 回忆 关于宏,上次有4个要点 qa 开始录制宏 q 结束录制宏 @a 应用宏 qA 追加录制宏 甚至可以编辑宏 "ap 把宏作为文本粘贴出来 编辑之后 " ...

  2. C#:只支持GET和POST方法的浏览器,如何发送PUT/DELETE请求?RESTful WebAPI如何响应?

    理想的RESTful WebAPI采用面向资源的架构,并使用请求的HTTP方法表示针对目标资源的操作类型.但是理想和现实是有距离的,虽然HTTP协议提供了一系列原生的HTTP方法,但是在具体的网络环境 ...

  3. FFmpeg开发笔记(四十一)结合OBS与MediaMTX实现SRT直播推流

    ​<FFmpeg开发实战:从零基础到短视频上线>一书的"10.2  FFmpeg推流和拉流"提到直播行业存在RTSP和RTMP两种常见的流媒体协议.除此以外,还有于20 ...

  4. 自制基于simplefoc大功率驱动板想法的由来,同时欢迎有相同兴趣的F友一起来玩。。。

    前一阵子,偶然在B站上看了一个simplefoc的介绍视频,代码简洁.算法精妙让人佩服,更让人佩服的是:开源!遂!搜索之!不搜不知道一搜吓一跳,发现太OUT了,原来玩这个算法的人这么多,让我这个整天沉 ...

  5. windows10使用scp命令

    windows10使用scp命令 windows自带scp命令 windows上传文件到linux//使用方法:scp 源文件路径 账户@地址:目的路径scp   C:\Users\zbh\Deskt ...

  6. 代码随想录Day1

    704.二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. 示例 1: ...

  7. 使用 Alba 对 AspnetCore项目进行测试

    前言 在AspnetCore生态系统中,我们测试项目一般使用Microsoft.AspNetCore.TestHost的TestServer 到.NET6后提供的Microsoft.AspNetCor ...

  8. 深度学习框架Theano停止维护

    Theano停止开发的声明地址: https://groups.google.com/g/theano-users/c/7Poq8BZutbY/m/rNCIfvAEAwAJ 原文内容: Dear us ...

  9. 使用Redis时的vm.overcommit_memory内存分配控制

    最近在使用Redis的时候遇到了linux系统中的vm.overcommit_memory参数设置,对此不是很了解,于是研究了一下,有了本文. ============================ ...

  10. 【转载】 Do's and Don'ts of using t-SNE to Understand Vision Models —— t-SNE 作者写的使用指南(PPT版本)

    <Do's and Don'ts of using t-SNE to Understand Vision Models> 作者的PPT视频:https://www.youtube.com/ ...