目录
- [Qt 实现自定义截图工具](#Qt 实现自定义截图工具)
Qt 实现自定义截图工具
本文使用Qt框架从头开始创建一个简单的屏幕截图工具。
实现效果图
截图按钮:
选取截图初始状态:
选取截图区域:
截图保存界面:
PrintScreen 类介绍
PrintScreen
类是一个自定义的 QWidget,允许用户捕捉屏幕上的任意区域。本教程将展示如何利用 Qt 的多功能库来实现这一功能。
PrintScreen 类的主要特性
- 全屏覆盖,用于选择要捕捉的屏幕区域。
- 通过鼠标互动选择定义捕捉区域。
- 拖放调整选定区域。
- 将捕获的区域保存为图片文件。
逐步实现
第一步:类定义
首先定义继承自 QWidget
的 PrintScreen
类。包含必要的 Qt 头文件,并声明我们的函数和成员变量。
cpp
#include <QWidget>
#include <QPainter>
/**
* @brief The PrintScreen class
* @param 区域截屏功能
*/
class PrintScreen : public QWidget
{
Q_OBJECT
public:
PrintScreen(QWidget *parent = nullptr);
~PrintScreen();
private:
/**
* @brief 初始化截图窗口的背景和尺寸
*/
void InitWindow();
/**
* @brief 根据起始点和终止点计算矩形区域
* @param beginPoint 矩形区域的起始点
* @param endPoint 矩形区域的终止点
* @return 返回根据两点计算出的 QRect 对象
*/
QRect GetRect(const QPoint &beginPoint, const QPoint &endPoint);
protected:
// 事件处理方法
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
void paintEvent(QPaintEvent *event);
private:
// 成员变量
bool m_isMousePress = false; // 是否按下鼠标
bool m_captureComplete = false; // 截图是否完成
bool m_isDragging = false; // 是否正在拖动截图区域
QPixmap m_loadPixmap; // 加载的屏幕截图
QPixmap m_capturePixmap; // 截取的屏幕区域
int m_screenWidth; // 屏幕宽度
int m_screenHeight; // 屏幕高度
QPoint m_beginPoint; // 截图开始点
QPoint m_endPoint; // 截图结束点
QPoint m_originalBegin; // 原始截图开始点
QPoint m_originalEnd; // 原始截图结束点
QPoint m_dragPosition; // 拖动时的鼠标位置
QPainter m_painter; // 绘图器对象
};
第二步:初始化截图窗口
InitWindow
方法设置窗口属性,如全屏模式、无边框窗口提示和鼠标跟踪。它还捕获整个屏幕并存储在 m_loadPixmap
中。
cpp
void PrintScreen::InitWindow()
{
// 启用鼠标跟踪
this->setMouseTracking(true);
// 设置无边框窗口
this->setWindowFlags(Qt::FramelessWindowHint);
// 设置窗口为激活状态和全屏模式
setWindowState(Qt::WindowActive | Qt::WindowFullScreen);
// 确保关闭时自动删除
setAttribute(Qt::WA_DeleteOnClose);
// 获取主屏幕
QScreen *screen = QApplication::primaryScreen();
// 抓取整个屏幕内容
m_loadPixmap = screen->grabWindow(QApplication::desktop()->winId());
// 设置屏幕宽度、高度
m_screenWidth = m_loadPixmap.width();
m_screenHeight = m_loadPixmap.height();
}
第三步:处理鼠标事件
鼠标事件对于定义捕获区域至关重要。mousePressEvent
、mouseMoveEvent
和 mouseReleaseEvent
处理捕获区域的开始、调整和最终确定。
cpp
void PrintScreen::mousePressEvent(QMouseEvent *event)
{
// 按下右键 关闭截图窗口
if (event->button() == Qt::RightButton)
{
close();
}
// 按下左键
else if (event->button() == Qt::LeftButton)
{
if (m_captureComplete && QRect(m_beginPoint, m_endPoint).contains(event->pos()))
{
m_isDragging = true; // 开始拖动
m_dragPosition = event->pos() - m_beginPoint; // 计算开始拖动位置
}
else
{
m_isMousePress = true; // 鼠标被按下
m_isDragging = false;
m_beginPoint = event->pos(); // 记录开始点
m_originalBegin = m_beginPoint;
}
}
}
void PrintScreen::mouseMoveEvent(QMouseEvent *event)
{
// 获取屏幕尺寸
QRect screenRect = QGuiApplication::primaryScreen()->geometry();
// 鼠标按下且截图未完成
if (m_isMousePress && !m_captureComplete)
{
// 确保终点坐标不超过屏幕范围
int x = qBound(screenRect.left(), event->pos().x(), screenRect.right());
int y = qBound(screenRect.top(), event->pos().y(), screenRect.bottom());
m_endPoint = QPoint(x, y);
}
// 正在拖动
else if (m_isDragging)
{
QPoint newTopLeft = event->pos() - m_dragPosition;
// 确保新的顶点坐标不超过屏幕范围
int x = qBound(screenRect.left(), newTopLeft.x(), screenRect.right() - m_dragPosition.x());
int y = qBound(screenRect.top(), newTopLeft.y(), screenRect.bottom() - m_dragPosition.y());
newTopLeft = QPoint(x, y);
QPoint offset = newTopLeft - m_beginPoint;
m_beginPoint += offset;
m_endPoint += offset;
}
update();
return QWidget::mouseMoveEvent(event);
}
void PrintScreen::mouseReleaseEvent(QMouseEvent *event)
{
// 鼠标释放且截图未完成
if (m_isMousePress && !m_captureComplete)
{
m_endPoint = event->pos(); // 设置结束点
m_isMousePress = false; // 重置鼠标按下状态
m_captureComplete = true; // 标记截图完成
update();
}
// 释放时正在拖动
else if (m_isDragging)
{
m_isDragging = false;
}
update();
}
第四步:计算截图区域
GetRect
方法接收两个参数:beginPoint
和 endPoint
,这两个点是用户通过鼠标操作定义的截图区域的开始和结束位置。此方法用于计算并返回一个 QRect
对象,该对象表示屏幕上要截取的矩形区域。
cpp
QRect PrintScreen::GetRect(const QPoint &beginPoint, const QPoint &endPoint)
{
int x = std::min(beginPoint.x(), endPoint.x());
int y = std::min(beginPoint.y(), endPoint.y());
int width = std::abs(beginPoint.x() - endPoint.x());
int height = std::abs(beginPoint.y() - endPoint.y());
if (width == 0) width = 1; // 确保宽度至少为1像素
if (height == 0) height = 1; // 确保高度至少为1像素
return QRect(x, y, width, height);
}
解释:
- 计算 x 和 y 坐标 :使用
std::min
函数确定矩形的左上角 x 和 y 坐标,这保证了无论用户如何拖动鼠标(从左到右或从右到左),都能正确计算出矩形的位置。 - 计算宽度和高度 :使用
std::abs
函数计算宽度和高度,确保值总是正数。如果计算结果为0(即起点和终点在同一直线上),则将宽度或高度设为1像素,确保矩形至少有最小的可见尺寸。
第五步:捕获和保存图像
paintEvent
方法在屏幕上绘制捕获的区域。keyPressEvent
监听回车键以触发保存捕获的图像。
cpp
void PrintScreen::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
m_painter.begin(this); // 开始绘制
QColor shadowColor(0, 0, 0, 100); // 半透明遮罩颜色
m_painter.setPen(QPen(Qt::blue, 1, Qt::SolidLine, Qt::FlatCap)); // 设置画笔
m_painter.drawPixmap(0, 0, m_loadPixmap); // 绘制加载的屏幕截图
m_painter.fillRect(m_loadPixmap.rect(), shadowColor); // 绘制半透明遮罩
QRect selectedRect = GetRect(m_beginPoint, m_endPoint); // 获取选择区域
m_capturePixmap = m_loadPixmap.copy(selectedRect); // 截取选择区域的屏幕截图
m_painter.drawPixmap(selectedRect.topLeft(), m_capturePixmap); // 绘制截取的区域
m_painter.drawRect(selectedRect); // 绘制选择区域的边框
m_painter.end(); // 结束绘制
}
void PrintScreen::keyPressEvent(QKeyEvent *event)
{
// 按下回车键
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
{
// 保存图片
QString filePath = QFileDialog::getSaveFileName(nullptr, "保存图片",QString(),"Images (*.png *.jpg)");
if(!filePath.isEmpty())
{
m_capturePixmap.save(filePath); // 保存截图到文件
}
close();
}
}
完整代码
PrintScreen.h
cpp
#ifndef PRINTSCREEN_H
#define PRINTSCREEN_H
#include <QWidget>
#include <QPainter>
/**
* @brief The PrintScreen class
* @param 区域截屏功能
*/
class PrintScreen : public QWidget
{
Q_OBJECT
public:
PrintScreen(QWidget *parent = nullptr);
~PrintScreen();
private:
/**
* @brief 初始化截图窗口的背景和尺寸
*/
void InitWindow();
/**
* @brief 根据起始点和终止点计算矩形区域
* @param beginPoint 矩形区域的起始点
* @param endPoint 矩形区域的终止点
* @return 返回根据两点计算出的 QRect 对象
*/
QRect GetRect(const QPoint &beginPoint, const QPoint &endPoint);
protected:
// 事件处理方法
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
void paintEvent(QPaintEvent *event);
private:
// 成员变量
bool m_isMousePress = false; // 是否按下鼠标
bool m_captureComplete = false; // 截图是否完成
bool m_isDragging = false; // 是否正在拖动截图区域
QPixmap m_loadPixmap; // 加载的屏幕截图
QPixmap m_capturePixmap; // 截取的屏幕区域
int m_screenWidth; // 屏幕宽度
int m_screenHeight; // 屏幕高度
QPoint m_beginPoint; // 截图开始点
QPoint m_endPoint; // 截图结束点
QPoint m_originalBegin; // 原始截图开始点
QPoint m_originalEnd; // 原始截图结束点
QPoint m_dragPosition; // 拖动时的鼠标位置
QPainter m_painter; // 绘图器对象
};
#endif // PRINTSCREEN_H
PrintScreen.cpp
cpp
#include "PrintScreen.h"
#include <QScreen>
#include <QFileDialog>
#include <QMouseEvent>
#include <QApplication>
#include <QDesktopWidget>
PrintScreen::PrintScreen(QWidget *parent)
: QWidget{parent}
{
InitWindow();
}
PrintScreen::~PrintScreen(){}
void PrintScreen::InitWindow()
{
// 启用鼠标跟踪
this->setMouseTracking(true);
// 设置无边框窗口
this->setWindowFlags(Qt::FramelessWindowHint);
// 设置窗口为激活状态和全屏模式
setWindowState(Qt::WindowActive | Qt::WindowFullScreen);
// 确保关闭时自动删除
setAttribute(Qt::WA_DeleteOnClose);
// 获取主屏幕
QScreen *screen = QApplication::primaryScreen();
// 抓取整个屏幕内容
m_loadPixmap = screen->grabWindow(QApplication::desktop()->winId());
// 设置屏幕宽度、高度
m_screenWidth = m_loadPixmap.width();
m_screenHeight = m_loadPixmap.height();
}
QRect PrintScreen::GetRect(const QPoint &beginPoint, const QPoint &endPoint)
{
int x = std::min(beginPoint.x(), endPoint.x());
int y = std::min(beginPoint.y(), endPoint.y());
int width = std::abs(beginPoint.x() - endPoint.x());
int height = std::abs(beginPoint.y() - endPoint.y());
if (width == 0) width = 1; // 确保宽度至少为1像素
if (height == 0) height = 1; // 确保高度至少为1像素
return QRect(x, y, width, height);
}
void PrintScreen::mousePressEvent(QMouseEvent *event)
{
// 按下右键 关闭截图窗口
if (event->button() == Qt::RightButton)
{
close();
}
// 按下左键
else if (event->button() == Qt::LeftButton)
{
if (m_captureComplete && QRect(m_beginPoint, m_endPoint).contains(event->pos()))
{
m_isDragging = true; // 开始拖动
m_dragPosition = event->pos() - m_beginPoint; // 计算开始拖动位置
}
else
{
m_isMousePress = true; // 鼠标被按下
m_isDragging = false;
m_beginPoint = event->pos(); // 记录开始点
m_originalBegin = m_beginPoint;
}
}
}
void PrintScreen::mouseMoveEvent(QMouseEvent *event)
{
// 获取屏幕尺寸
QRect screenRect = QGuiApplication::primaryScreen()->geometry();
// 鼠标按下且截图未完成
if (m_isMousePress && !m_captureComplete)
{
// 确保终点坐标不超过屏幕范围
int x = qBound(screenRect.left(), event->pos().x(), screenRect.right());
int y = qBound(screenRect.top(), event->pos().y(), screenRect.bottom());
m_endPoint = QPoint(x, y);
}
// 正在拖动
else if (m_isDragging)
{
QPoint newTopLeft = event->pos() - m_dragPosition;
// 确保新的顶点坐标不超过屏幕范围
int x = qBound(screenRect.left(), newTopLeft.x(), screenRect.right() - m_dragPosition.x());
int y = qBound(screenRect.top(), newTopLeft.y(), screenRect.bottom() - m_dragPosition.y());
newTopLeft = QPoint(x, y);
QPoint offset = newTopLeft - m_beginPoint;
m_beginPoint += offset;
m_endPoint += offset;
}
update();
return QWidget::mouseMoveEvent(event);
}
void PrintScreen::mouseReleaseEvent(QMouseEvent *event)
{
// 鼠标释放且截图未完成
if (m_isMousePress && !m_captureComplete)
{
m_endPoint = event->pos(); // 设置结束点
m_isMousePress = false; // 重置鼠标按下状态
m_captureComplete = true; // 标记截图完成
update();
}
// 释放时正在拖动
else if (m_isDragging)
{
m_isDragging = false;
}
update();
}
void PrintScreen::keyPressEvent(QKeyEvent *event)
{
// 按下回车键
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
{
// 保存图片
QString filePath = QFileDialog::getSaveFileName(nullptr, "保存图片",QString(),"Images (*.png *.jpg)");
if(!filePath.isEmpty())
{
m_capturePixmap.save(filePath); // 保存截图到文件
}
close();
}
}
void PrintScreen::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
m_painter.begin(this); // 开始绘制
QColor shadowColor(0, 0, 0, 100); // 半透明遮罩颜色
m_painter.setPen(QPen(Qt::blue, 1, Qt::SolidLine, Qt::FlatCap)); // 设置画笔
m_painter.drawPixmap(0, 0, m_loadPixmap); // 绘制加载的屏幕截图
m_painter.fillRect(m_loadPixmap.rect(), shadowColor); // 绘制半透明遮罩
QRect selectedRect = GetRect(m_beginPoint, m_endPoint); // 获取选择区域
m_capturePixmap = m_loadPixmap.copy(selectedRect); // 截取选择区域的屏幕截图
m_painter.drawPixmap(selectedRect.topLeft(), m_capturePixmap); // 绘制截取的区域
m_painter.drawRect(selectedRect); // 绘制选择区域的边框
m_painter.end(); // 结束绘制
}
MainWindow.h
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include "PrintScreen.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QPushButton *m_screenBtn;
PrintScreen *m_printScree;
// QObject interface
public:
bool eventFilter(QObject *watched, QEvent *event);
};
#endif // MAINWINDOW_H
MainWindow.cpp
cpp
#include "MainWindow.h"
#include <QLayout>
#include <QMouseEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, m_screenBtn(new QPushButton("截图", this))
{
// 设置中心窗口
setCentralWidget(m_screenBtn);
// 安装事件过滤器
m_screenBtn->installEventFilter(this);
// 隐藏标题栏
setWindowFlags(Qt::FramelessWindowHint);
resize(150, 50);
}
MainWindow::~MainWindow() {}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_screenBtn && event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::RightButton)
{
close();
return true;
}
else if (mouseEvent->button() == Qt::LeftButton)
{
m_printScree = new PrintScreen();
m_printScree->show();
return true;
}
}
return QMainWindow::eventFilter(watched, event);
}
main.cpp
cpp
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}