12人参与 • 2026-08-06 • C/C++



#ifndef mainwindow_h
#define mainwindow_h
#include <qmainwindow>
#include <qpixmap>
#include <qscreen>
#include <qfiledialog>
#include <qclipboard>
#include <qpainter>
#include <qmouseevent>
qt_begin_namespace
namespace ui { class mainwindow; }
qt_end_namespace
// 选区截图全屏遮罩窗口
class capturemask : public qwidget
{
q_object
public:
explicit capturemask(qwidget *parent = nullptr);
void setscreenpix(const qpixmap &pix);
protected:
void paintevent(qpaintevent *) override;
void mousepressevent(qmouseevent *event) override;
void mousemoveevent(qmouseevent *event) override;
void mousereleaseevent(qmouseevent *event) override;
signals:
void capturefinished(const qpixmap &pix);
private:
qpixmap m_screenbg;
qpoint m_startpos;
qpoint m_endpos;
bool m_isdragging = false;
};
class mainwindow : public qmainwindow
{
q_object
public:
mainwindow(qwidget *parent = nullptr);
~mainwindow() override;
private slots:
// 全屏截图
void slotcapturefull();
// 选区截图
void slotcapturearea();
// 截图完成回调
void slotoncapturedone(const qpixmap &pix);
// 保存图片
void slotsaveimage();
// 复制到剪贴板
void slotcopyclipboard();
private:
ui::mainwindow *ui;
qpixmap m_capturepix; // 当前截图缓存
};
#endif // mainwindow_h
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qapplication>
#include <qmessagebox>
#include <qdatetime>
capturemask::capturemask(qwidget *parent)
: qwidget(parent, qt::window | qt::framelesswindowhint | qt::windowstaysontophint)
{
setattribute(qt::wa_deleteonclose);
setattribute(qt::wa_translucentbackground, false);
setautofillbackground(false);
}
void capturemask::setscreenpix(const qpixmap &pix)
{
m_screenbg = pix;
qrect screenrect = qapplication::primaryscreen()->geometry();
this->setgeometry(screenrect);
this->showfullscreen();
}
void capturemask::paintevent(qpaintevent *)
{
qpainter painter(this);
painter.setrenderhint(qpainter::antialiasing);
// ========== 绘制逻辑:原图在上,暗色遮罩覆盖,选区挖空 ==========
// 1. 先绘制完整原图
painter.drawpixmap(rect(), m_screenbg);
// 2. 全局绘制半透明黑色遮罩(全覆盖变暗)
qcolor darkcolor(0, 0, 0, 150);
painter.fillrect(rect(), darkcolor);
if (m_isdragging)
{
qrect selectrect = qrect(m_startpos, m_endpos).normalized();
// 3. 在选区重新绘制原图,覆盖掉黑色遮罩,实现框内高亮
painter.drawpixmap(selectrect, m_screenbg, selectrect);
// 4. 绘制选区边框与填充
painter.setpen(qpen(qcolor(0, 180, 255), 2));
painter.setbrush(qcolor(0, 160, 255, 60));
painter.drawrect(selectrect);
}
}
void capturemask::mousepressevent(qmouseevent *event)
{
m_isdragging = true;
m_startpos = event->pos();
m_endpos = m_startpos;
update();
}
void capturemask::mousemoveevent(qmouseevent *event)
{
if (!m_isdragging) return;
m_endpos = event->pos();
update();
}
void capturemask::mousereleaseevent(qmouseevent *)
{
m_isdragging = false;
qrect rect = qrect(m_startpos, m_endpos).normalized();
qpixmap result = m_screenbg.copy(rect);
emit capturefinished(result);
this->close();
}
// ================= mainwindow =================
mainwindow::mainwindow(qwidget *parent)
: qmainwindow(parent)
, ui(new ui::mainwindow)
{
ui->setupui(this);
this->setwindowtitle("qt6 截图工具");
this->resize(600, 450);
}
mainwindow::~mainwindow()
{
delete ui;
}
void mainwindow::slotcapturefull()
{
qscreen *screen = qapplication::primaryscreen();
m_capturepix = screen->grabwindow(0);
slotoncapturedone(m_capturepix);
}
void mainwindow::slotcapturearea()
{
capturemask *mask = new capturemask(this);
qscreen *screen = qapplication::primaryscreen();
qpixmap fullpix = screen->grabwindow(0);
mask->setscreenpix(fullpix);
connect(mask, &capturemask::capturefinished,
this, &mainwindow::slotoncapturedone);
}
void mainwindow::slotoncapturedone(const qpixmap &pix)
{
m_capturepix = pix;
// 显示截图到label,自动缩放
ui->label_view->setpixmap(pix.scaled(ui->label_view->size(), qt::keepaspectratio, qt::smoothtransformation));
}
void mainwindow::slotsaveimage()
{
if (m_capturepix.isnull())
{
qmessagebox::information(this, "提示", "请先截图!");
return;
}
qstring timestr = qdatetime::currentdatetime().tostring("yyyymmdd_hhmmss");
qstring path = qfiledialog::getsavefilename(this, "保存截图",
qstring("./%1.png").arg(timestr),
"png图片(*.png);;jpg图片(*.jpg)");
if (!path.isempty())
{
m_capturepix.save(path);
qmessagebox::information(this, "成功", "图片已保存");
}
}
void mainwindow::slotcopyclipboard()
{
if (m_capturepix.isnull())
{
qmessagebox::information(this, "提示", "请先截图!");
return;
}
qapplication::clipboard()->setpixmap(m_capturepix);
qmessagebox::information(this, "成功", "截图已复制到剪贴板");
}
<?xml version="1.0" encoding="utf-8"?>
<ui version="4.0">
<class>mainwindow</class>
<widget class="qmainwindow" name="mainwindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>450</height>
</rect>
</property>
<widget class="qwidget" name="centralwidget">
<layout class="qvboxlayout" name="verticallayout">
<item>
<layout class="qhboxlayout" name="horizontallayout">
<item>
<widget class="qpushbutton" name="btn_full">
<property name="text">
<string>全屏截图</string>
</property>
</widget>
</item>
<item>
<widget class="qpushbutton" name="btn_area">
<property name="text">
<string>选区截图</string>
</property>
</widget>
</item>
<item>
<widget class="qpushbutton" name="btn_save">
<property name="text">
<string>保存图片</string>
</property>
</widget>
</item>
<item>
<widget class="qpushbutton" name="btn_copy">
<property name="text">
<string>复制剪贴板</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="qlabel" name="label_view">
<property name="minimumsize">
<size>
<width>0</width>
<height>300</height>
</size>
</property>
<property name="stylesheet">
<string notr="true">border:1px solid #999;</string>
</property>
<property name="text">
<string>截图预览区域</string>
</property>
<property name="alignment">
<set>qt::alignmentflag::aligncenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>btn_full</sender>
<signal>clicked()</signal>
<receiver>mainwindow</receiver>
<slot>slotcapturefull()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>btn_area</sender>
<signal>clicked()</signal>
<receiver>mainwindow</receiver>
<slot>slotcapturearea()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>btn_save</sender>
<signal>clicked()</signal>
<receiver>mainwindow</receiver>
<slot>slotsaveimage()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>btn_copy</sender>
<signal>clicked()</signal>
<receiver>mainwindow</receiver>
<slot>slotcopyclipboard()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>slotcapturefull()</slot>
<slot>slotcapturearea()</slot>
<slot>slotoncapturedone(const qpixmap&)</slot>
<slot>slotsaveimage()</slot>
<slot>slotcopyclipboard()</slot>
</slots>
</ui>本截图工具的核心实现依赖于 qt6 的事件系统(event system)和绘图系统(painting system)。下面详细解析关键事件的工作原理:
qt 采用事件驱动的编程模型,所有用户交互(鼠标点击、键盘输入、窗口重绘等)都通过事件(event)来传递和处理。事件处理流程如下:
qapplication::notify() 派发到目标对象installeventfilter() 进行预处理event() 方法接收并分发给特定事件处理器void capturemask::paintevent(qpaintevent *)
{
qpainter painter(this);
painter.setrenderhint(qpainter::antialiasing);
// 1. 绘制完整屏幕截图作为背景
painter.drawpixmap(rect(), m_screenbg);
// 2. 全局半透明黑色遮罩(实现变暗效果)
qcolor darkcolor(0, 0, 0, 150); // rgba: 黑色,透明度150/255
painter.fillrect(rect(), darkcolor);
if (m_isdragging)
{
qrect selectrect = qrect(m_startpos, m_endpos).normalized();
// 3. 选区区域重新绘制原图(挖空效果)
painter.drawpixmap(selectrect, m_screenbg, selectrect);
// 4. 绘制选区边框和半透明填充
painter.setpen(qpen(qcolor(0, 180, 255), 2)); // 蓝色边框
painter.setbrush(qcolor(0, 160, 255, 60)); // 浅蓝色半透明填充
painter.drawrect(selectrect);
}
}
原理说明:
paintevent 在以下情况自动触发:
update() 或 repaint() 方法mousemoveevent)都会调用 update(),从而触发重绘mousepressevent - 鼠标按下
void capturemask::mousepressevent(qmouseevent *event)
{
m_isdragging = true; // 开始拖拽状态
m_startpos = event->pos(); // 记录起始位置
m_endpos = m_startpos; // 初始结束位置=起始位置
update(); // 触发重绘(绘制起始点)
}
mousemoveevent - 鼠标移动
void capturemask::mousemoveevent(qmouseevent *event)
{
if (!m_isdragging) return; // 非拖拽状态不处理
m_endpos = event->pos(); // 更新结束位置
update(); // 触发重绘(更新选区矩形)
}
mousereleaseevent - 鼠标释放
void capturemask::mousereleaseevent(qmouseevent *)
{
m_isdragging = false; // 结束拖拽状态
// 计算标准化矩形(确保左上角到右下角)
qrect rect = qrect(m_startpos, m_endpos).normalized();
// 从原图中截取选区
qpixmap result = m_screenbg.copy(rect);
// 发射信号通知截图完成
emit capturefinished(result);
// 关闭遮罩窗口
this->close();
}
事件传递机制:
qmouseevent 封装鼠标事件信息event->pos() 返回相对于当前窗口的坐标normalized() 确保矩形坐标是标准化的(左上角到右下角)用户按下鼠标 → mousepressevent
↓
用户拖动鼠标 → mousemoveevent → update() → paintevent
↓
用户释放鼠标 → mousereleaseevent
↓
发射 capturefinished 信号 → mainwindow::slotoncapturedone
全屏遮罩窗口
// 窗口标志设置 qt::window | // 作为独立窗口 qt::framelesswindowhint | // 无边框 qt::windowstaysontophint // 始终置顶 // 窗口属性 setattribute(qt::wa_deleteonclose); // 关闭时自动删除 setattribute(qt::wa_translucentbackground, false); // 不透明背景
屏幕捕获
// 获取主屏幕 qscreen *screen = qapplication::primaryscreen(); // 捕获整个屏幕(包括所有窗口) qpixmap fullpix = screen->grabwindow(0); // 0表示整个屏幕
图像处理
qpixmap::copy(const qrect &):截取指定区域qpixmap::save():保存到文件qclipboard::setpixmap():复制到剪贴板改进的事件过滤器:qt6 增强了事件过滤器的性能,支持更精细的事件拦截和处理。
手势事件支持:新增对触摸屏和手势的更好支持,虽然本工具未使用,但在移动端开发中很重要。
输入法事件优化:对多语言输入法的支持更加完善。
减少不必要的重绘:
update()update(qrect) 只重绘脏区域内存管理:
qpixmapcache 缓存常用图像响应式设计:
devicepixelratio 适配到此这篇关于qt实现屏幕截图小工具的完整指南的文章就介绍到这了,更多相关qt屏幕截图内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论