QT框架实现自定义形状截图效果

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软件开发技术

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的形界面和功能库。要实现不规则形状的进度条,可以使用Qt的绘功能和自定义控件。 以下是一种实现不规则形状进度条的方法: 1. 创建一个自定义的QWidget子类,作为进度条的容器。 2. 在该自定义控件中重写paintEvent函数,使用Qt的绘功能绘制进度条的背景和前景。 3. 根据进度值计算出前景的宽度,并使用绘函数绘制前景。 4. 使用QPainterPath类创建一个不规则形状的路径,可以使用addRect、addEllipse等函数添加矩形、椭圆等形状。 5. 使用setClipPath函数将绘区域限制在不规则形状的路径内。 6. 在paintEvent函数中绘制进度条的背景和前景。 下面是一个简单的示例代码: ```cpp #include <QtWidgets> class IrregularProgressBar : public QWidget { public: IrregularProgressBar(QWidget *parent = nullptr) : QWidget(parent), m_progress(0) { } void setProgress(int progress) { m_progress = progress; update(); } protected: void paintEvent(QPaintEvent *event) override { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 绘制背景 painter.fillRect(rect(), Qt::lightGray); // 绘制前景 QRectF foregroundRect(rect().x(), rect().y(), rect().width() * m_progress / 100.0, rect().height()); painter.fillRect(foregroundRect, Qt::blue); // 创建不规则形状的路径 QPainterPath path; path.addRoundedRect(rect(), 10, 10); // 设置绘区域为不规则形状的路径 painter.setClipPath(path); // 绘制进度条的背景和前景 painter.fillRect(rect(), Qt::lightGray); painter.fillRect(foregroundRect, Qt::blue); } private: int m_progress; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); IrregularProgressBar progressBar; progressBar.setProgress(50); progressBar.resize(300, 30); progressBar.show(); return app.exec(); } ``` 这个示例代码创建了一个自定义的QWidget子类IrregularProgressBar,通过重写paintEvent函数实现了不规则形状的进度条。在paintEvent函数中,首先绘制了背景和前景,然后创建了一个圆角矩形的路径,并使用setClipPath函数将绘区域限制在该路径内,最后再次绘制了背景和前景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值