文章目录
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框架感兴趣,可以看一下这篇文章:
如果读者对如何学习QT框架有兴趣,可以看一下这篇文章:
如果您认为这篇文章对您有所帮助,请您一定立即点赞+喜欢+收藏,本文作者将能从您的点赞+喜欢+收藏中获取到创作新的好文章的动力。如果您认为作者写的文章还有一些参考价值,您也可以关注这篇文章的作者。