Qt 实现高仿微信的滑动截屏工具(一)

目录

前言

主要使用的Qt库

业务逻辑层 (Business Logic Layer)

思路描述

阴影与遮罩思路

关键点

关键函数讲解:

takeScreenshot()

mousePressEvent(QMouseEvent *event)

mouseMoveEvent(QMouseEvent *event)

mouseReleaseEvent(QMouseEvent *event)

paintEvent(QPaintEvent *event)

onConfirmButtonClicked()

onSaveButtonClicked()


文章最下方有源码获取方式哦~

前言

最近在完善简易QQ项目的过程中,我在基本布局的基础上添加了一些新功能,其中截图功能是首要实现的功能之一。鉴于网上缺乏现成的开源截图软件,我决定自己开发,并在此分享我的源码。初期的实现较为简单,仅涵盖了截图的基本功能。未来若有时间,我会继续对其进行优化和扩展。接下来,直接进入正题。

主要使用的Qt库

  • QWidget: 作为所有用户界面对象的基础类。
  • QLabel: 用于显示文本或图像,这里用来展示截取的屏幕快照。
  • QPushButton: 创建按钮,如确认、取消和保存按钮。
  • QVBoxLayout 和 QHBoxLayout: 用于布局管理,组织窗口内的小部件。
  • QMouseEvent: 处理鼠标事件(按下、移动、释放)。
  • QFileDialog: 提供一个对话框让用户选择文件来保存截图。
  • QPainter: 用于绘制图形和文本,这里用来绘制截图和选择区域。
  • QPixmap: 用于表示和操作像素图,存储截图内容。
  • QRegion: 用于定义和操作不规则形状的区域,用以创建遮罩效果。
  • QScreen: 获取屏幕信息,并能够抓取整个屏幕的内容。
  • QColor, QPen, QBrush: 分别用于定义颜色、设置画笔属性和填充形状的颜色或图案,用于绘制选择矩形的边框和阴影区域的颜色。
  • Qt::Alignment 和其他 Qt::命名空间下的枚举:用于指定对齐方式和其他UI属性。
  • QRect 和 QPoint: 用于几何计算,比如确定鼠标点击的位置和选择区域的大小。
  • QtConcurrent::run: 用于异步执行函数,这里是异步保存文件,避免阻塞主线程。

业务逻辑层 (Business Logic Layer)

  • takeScreenshot() 方法:使用 QScreen::grabWindow() 抓取屏幕快照,并将结果存储在 screenshot 成员变量中。
  • mousePressEvent(), mouseMoveEvent(), 和 mouseReleaseEvent():处理用户的鼠标交互,允许用户选择截图区域。
  • paintEvent():负责重绘窗口,包括显示截图、绘制选择矩形和遮罩效果。
  • updateButtonPositions():根据选择区域的位置动态调整按钮的位置,确保它们不会被遮挡并且始终可见。
  • onConfirmButtonClicked(), onCancelButtonClicked(), 和 onSaveButtonClicked():分别处理确认、取消和保存截图的操作。保存操作通过 QtConcurrent::run 异步执行,以保持界面响应性。

思路描述

Qt 简单截图工具(一) 高仿QQ截屏 滑动截屏_qt qml 仿qq截图-CSDN博客

受到一篇CSDN博客文章的影响,我纠正了最初的错误思路。最初我计划将屏幕截图放置在一个 QLabel 上,然后尝试用 QPainter 进行截图操作,但发现 QLabel 是一个控件,不能直接在其上绘画。最终,我选择直接在 QPixmap 上进行操作,这使得问题得到了解决。

阴影与遮罩思路
  • screenshotPixmap 用于保存缩放后的屏幕截图。它首先被填充为透明色(Qt::transparent),然后使用 QPainter 将原始截图按照窗口大小进行缩放并绘制到这个 QPixmap 上。这样做是为了确保截图能够适配当前窗口的尺寸,同时保持原有的宽高比,并使用平滑变换来提高图像质量。

  • shadowPixmap 专门用于创建阴影效果或遮罩区域。它同样初始化为透明色,然后根据用户选择的矩形区域来绘制半透明的阴影。在 paintEvent() 方法中,当用户正在绘制选择矩形时,程序会计算出一个非选择区域(即除了用户选择的矩形之外的所有区域),并将这部分区域用半透明的颜色(如黑色,带有一些透明度)填充,以达到视觉上的“阴影”效果。如果没有选择矩形或者选择矩形为空,则整个窗口会被绘制上阴影,表示没有选中的区域。

  • 在 paintEvent() 的最后,程序依次绘制 screenshotPixmap 和 shadowPixmap 到窗口上。先绘制 screenshotPixmap 展示截图内容,再绘制 shadowPixmap 显示阴影效果。如果用户正在绘制选择矩形,还会在最顶层绘制一个绿色边框的矩形,以清晰地标记出用户选择的区域。

关键点
  • screenshotPixmap 专注于展示经过缩放处理后的屏幕截图。
  • shadowPixmap 专注于创建遮罩效果,突出显示用户选择的区域。

绘制顺序为先绘制 screenshotPixmap,再绘制 shadowPixmap,最后如果有必要,绘制选择矩形的边框。shadowPixmap 使用半透明颜色来创建阴影效果,而不是完全不透明的颜色,这样可以保留背景图像的可见性,同时提供视觉上的区分。

程序运行后屏幕不会有任何改变。

截图时效果图:

关键函数讲解:

takeScreenshot()

这个方法用于获取屏幕的快照。它使用了QScreen::grabWindow()方法来抓取整个屏幕(通过传递0作为窗口句柄参数),并将结果存储在成员变量screenshot中。这为后续绘制和剪裁提供了基础图像。

void ScreenshotWidget::takeScreenshot()
{
    QScreen *screen = QApplication::primaryScreen();
    if (screen) {
        screenshot = screen->grabWindow(0);
    }
}

mousePressEvent(QMouseEvent *event)

当用户按下鼠标按钮时触发此事件处理器。在这里,我们检查是否是左键被按下,并初始化一个矩形选择操作。如果条件满足,我们将开始位置设置为当前鼠标位置,并将isDrawing标志设为true,以表示用户正在绘制选择区域。

void ScreenshotWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "Mouse pressed at" << event->pos();
        isDrawing = true;
        startPos = event->pos();
        endPos = startPos;
        update();
    }
}

mouseMoveEvent(QMouseEvent *event)

每当鼠标移动且左键被按住时,都会调用此事件处理器。它更新结束位置,并重新绘制窗口以反映新的选择区域。

void ScreenshotWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (isDrawing && (event->buttons() & Qt::LeftButton)) {
        qDebug() << "Mouse moved to" << event->pos();
        endPos = event->pos();
        update();
    }
}

mouseReleaseEvent(QMouseEvent *event)

当用户释放鼠标左键时触发。这里计算并规范化最终的选择矩形,并更新按钮的位置使其出现在选择区域下方。同时也会重新绘制窗口以显示最终的选择。

void ScreenshotWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && isDrawing) {
        qDebug() << "Mouse released at" << event->pos();
        endPos = event->pos();
        selectionRect = QRect(startPos, endPos).normalized();
        update();
        updateButtonPositions();
    }
}

paintEvent(QPaintEvent *event)

这是自定义绘图的关键所在。在这个函数中,我们创建了一个与窗口大小相同的QPixmap,然后在其上绘制缩放后的截图。接着,我们根据是否正在绘制选择矩形来决定是否应用遮罩效果,并绘制出绿色边框的选择矩形。

void ScreenshotWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    // 绘制截图
    // ...

    if (isDrawing) {
        // 应用遮罩效果
        // ...
        
        // 绘制选择矩形
        if (!selectRect.isEmpty()) {
            QPen pen(Qt::green, 3);
            pen.setStyle(Qt::SolidLine);
            painter.setPen(pen);
            painter.drawRect(selectRect);
        }
    }
}

onConfirmButtonClicked()

当用户点击确认按钮时,该槽函数会被调用。它会复制选中的截图部分,并携带图像发出screenshotCaptured信号,方便其他组件获取此图片。之后,它隐藏所有按钮,重置状态,并关闭窗口。

void ScreenshotWidget::onConfirmButtonClicked()
{
    QRect rect = QRect(startPos, endPos).normalized();
    if (!rect.isEmpty()) {
        QPixmap croppedPixmap = screenshot.copy(rect);
        emit screenshotCaptured(croppedPixmap);
        confirmButton->hide();
        cancelButton->hide();
        saveButton->hide();
        isDrawing = false;
        update();
        this->close();
    }
}

onSaveButtonClicked()

当用户点击保存按钮时,这个槽函数负责打开文件对话框让用户选择保存路径和格式,然后异步地将截图保存到磁盘。完成后,同样会隐藏按钮、重置状态并关闭窗口。

void ScreenshotWidget::onSaveButtonClicked()
{
    QRect rect = QRect(startPos, endPos).normalized();
    if (!rect.isEmpty()) {
        QPixmap croppedPixmap = screenshot.copy(rect);

        QString filePath = QFileDialog::getSaveFileName(
                    this,
                    tr("Save Screenshot"),
                    "untitled.png",
                    tr("PNG Files (*.png);;JPEG Files (*.jpg *.jpeg);;All Files (*)")
                    );

        if (!filePath.isEmpty()) {
            // 使用 QtConcurrent::run 异步保存文件
            QtConcurrent::run([=]() {
                croppedPixmap.save(filePath);
                qDebug() << "Screenshot saved to" << filePath;
            });

            // 隐藏按钮
            confirmButton->hide();
            cancelButton->hide();
            saveButton->hide();
            isDrawing = false;
            update();

            this->close();
        }
    }
}

源码已经给小伙伴们整理好了,微信搜索 嵌入式工程之家 关注公众号回复 截图 即可获得源码和详细操作指示哦~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨笨小乌龟11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值