Qt 屏幕截图实现,包含全屏截图、区域选择、窗口截图、定时截图、编辑标注等功能。
一、项目结构
ScreenCapture/
├── main.cpp
├── screenshot.h
├── screenshot.cpp
├── screenshot.ui
├── regionselector.h
├── regionselector.cpp
├── editdialog.h
├── editdialog.cpp
└── resources.qrc
二、核心实现代码
2.1 主窗口头文件 (screenshot.h)
cpp
#ifndef SCREENSHOT_H
#define SCREENSHOT_H
#include <QMainWindow>
#include <QPixmap>
#include <QTimer>
#include <QSystemTrayIcon>
namespace Ui {
class ScreenShot;
}
class ScreenShot : public QMainWindow
{
Q_OBJECT
public:
explicit ScreenShot(QWidget *parent = nullptr);
~ScreenShot();
protected:
void keyPressEvent(QKeyEvent *event) override;
void closeEvent(QCloseEvent *event) override;
private slots:
void captureFullScreen();
void captureActiveWindow();
void captureSelectedRegion();
void captureWithDelay();
void saveScreenshot();
void copyToClipboard();
void showEditDialog();
void updatePreview();
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
private:
Ui::ScreenShot *ui;
QPixmap originalPixmap;
QPixmap currentPixmap;
QTimer *delayTimer;
QSystemTrayIcon *trayIcon;
void setupTrayIcon();
void enableButtons(bool enabled);
};
#endif // SCREENSHOT_H
2.2 主窗口实现 (screenshot.cpp)
cpp
#include "screenshot.h"
#include "ui_screenshot.h"
#include "regionselector.h"
#include "editdialog.h"
#include <QScreen>
#include <QApplication>
#include <QDesktopWidget>
#include <QFileDialog>
#include <QClipboard>
#include <QMessageBox>
#include <QDateTime>
#include <QMenu>
#include <QAction>
ScreenShot::ScreenShot(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::ScreenShot)
{
ui->setupUi(this);
setWindowTitle("Qt 屏幕截图工具");
delayTimer = new QTimer(this);
connect(delayTimer, &QTimer::timeout, this, &ScreenShot::captureWithDelay);
setupTrayIcon();
enableButtons(true);
// 快捷键
QShortcut *fullScreenShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_F), this);
connect(fullScreenShortcut, &QShortcut::activated, this, &ScreenShot::captureFullScreen);
QShortcut *regionShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_R), this);
connect(regionShortcut, &QShortcut::activated, this, &ScreenShot::captureSelectedRegion);
}
ScreenShot::~ScreenShot()
{
delete ui;
}
void ScreenShot::captureFullScreen()
{
enableButtons(false);
QScreen *screen = QApplication::primaryScreen();
if (!screen)
return;
originalPixmap = screen->grabWindow(0);
currentPixmap = originalPixmap.copy();
updatePreview();
enableButtons(true);
}
void ScreenShot::captureActiveWindow()
{
enableButtons(false);
WId winId = QApplication::activeWindow()->winId();
QScreen *screen = QApplication::primaryScreen();
if (!screen)
return;
originalPixmap = screen->grabWindow(winId);
currentPixmap = originalPixmap.copy();
updatePreview();
enableButtons(true);
}
void ScreenShot::captureSelectedRegion()
{
RegionSelector selector;
if (selector.exec() == QDialog::Accepted) {
QRect rect = selector.selectedRegion();
if (rect.isValid()) {
QScreen *screen = QApplication::primaryScreen();
if (screen) {
originalPixmap = screen->grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height());
currentPixmap = originalPixmap.copy();
updatePreview();
}
}
}
}
void ScreenShot::captureWithDelay()
{
static int countdown = 5;
if (delayTimer->isActive()) {
delayTimer->stop();
countdown = 5;
ui->statusLabel->setText("就绪");
enableButtons(true);
return;
}
enableButtons(false);
ui->statusLabel->setText(QString("将在 %1 秒后截图...").arg(countdown));
delayTimer->start(1000);
if (countdown-- <= 0) {
delayTimer->stop();
countdown = 5;
captureFullScreen();
}
}
void ScreenShot::saveScreenshot()
{
QString fileName = QFileDialog::getSaveFileName(this, "保存截图",
QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss") + ".png",
"PNG Files (*.png);;JPEG Files (*.jpg);;All Files (*.*)");
if (!fileName.isEmpty()) {
if (currentPixmap.save(fileName)) {
ui->statusLabel->setText("截图已保存: " + fileName);
} else {
QMessageBox::warning(this, "保存失败", "无法保存截图文件");
}
}
}
void ScreenShot::copyToClipboard()
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setPixmap(currentPixmap);
ui->statusLabel->setText("截图已复制到剪贴板");
}
void ScreenShot::showEditDialog()
{
EditDialog dialog(currentPixmap, this);
if (dialog.exec() == QDialog::Accepted) {
currentPixmap = dialog.editedPixmap();
updatePreview();
}
}
void ScreenShot::updatePreview()
{
if (currentPixmap.isNull()) {
ui->previewLabel->setText("暂无截图");
return;
}
QPixmap scaled = currentPixmap.scaled(ui->previewLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->previewLabel->setPixmap(scaled);
ui->infoLabel->setText(QString("尺寸: %1 x %2 像素")
.arg(currentPixmap.width())
.arg(currentPixmap.height()));
}
void ScreenShot::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
close();
} else if (event->key() == Qt::Key_S && event->modifiers() == Qt::ControlModifier) {
saveScreenshot();
} else if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) {
copyToClipboard();
}
}
void ScreenShot::closeEvent(QCloseEvent *event)
{
if (trayIcon && trayIcon->isVisible()) {
hide();
event->ignore();
} else {
event->accept();
}
}
void ScreenShot::setupTrayIcon()
{
trayIcon = new QSystemTrayIcon(this);
trayIcon->setIcon(QIcon(":/icons/screenshot.png"));
trayIcon->setToolTip("Qt 屏幕截图工具");
QMenu *menu = new QMenu(this);
QAction *fullScreenAction = menu->addAction("全屏截图");
QAction *regionAction = menu->addAction("区域截图");
QAction *quitAction = menu->addAction("退出");
connect(fullScreenAction, &QAction::triggered, this, &ScreenShot::captureFullScreen);
connect(regionAction, &QAction::triggered, this, &ScreenShot::captureSelectedRegion);
connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
trayIcon->setContextMenu(menu);
trayIcon->show();
connect(trayIcon, &QSystemTrayIcon::activated, this, &ScreenShot::trayIconActivated);
}
void ScreenShot::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
if (reason == QSystemTrayIcon::Trigger) {
if (isHidden()) {
show();
raise();
activateWindow();
} else {
hide();
}
}
}
void ScreenShot::enableButtons(bool enabled)
{
ui->fullScreenBtn->setEnabled(enabled);
ui->activeWindowBtn->setEnabled(enabled);
ui->regionBtn->setEnabled(enabled);
ui->delayBtn->setEnabled(enabled);
ui->saveBtn->setEnabled(enabled && !currentPixmap.isNull());
ui->copyBtn->setEnabled(enabled && !currentPixmap.isNull());
ui->editBtn->setEnabled(enabled && !currentPixmap.isNull());
}
2.3 区域选择器 (regionselector.h)
cpp
#ifndef REGIONSELECTOR_H
#define REGIONSELECTOR_H
#include <QDialog>
#include <QRubberBand>
#include <QPoint>
class RegionSelector : public QDialog
{
Q_OBJECT
public:
explicit RegionSelector(QWidget *parent = nullptr);
QRect selectedRegion() const;
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private:
QRubberBand *rubberBand;
QPoint origin;
QRect selection;
bool selecting;
};
#endif // REGIONSELECTOR_H
2.4 区域选择器实现 (regionselector.cpp)
cpp
#include "regionselector.h"
#include <QMouseEvent>
#include <QPainter>
#include <QScreen>
#include <QApplication>
RegionSelector::RegionSelector(QWidget *parent)
: QDialog(parent), rubberBand(nullptr), selecting(false)
{
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
setCursor(Qt::CrossCursor);
setStyleSheet("background-color: rgba(0, 0, 0, 100);");
// 设置全屏大小
QScreen *screen = QApplication::primaryScreen();
if (screen) {
setGeometry(screen->geometry());
}
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
}
QRect RegionSelector::selectedRegion() const
{
return selection;
}
void RegionSelector::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
origin = event->pos();
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
selecting = true;
} else if (event->button() == Qt::RightButton) {
reject();
}
}
void RegionSelector::mouseMoveEvent(QMouseEvent *event)
{
if (selecting) {
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}
}
void RegionSelector::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && selecting) {
selection = rubberBand->geometry();
rubberBand->hide();
selecting = false;
accept();
}
}
void RegionSelector::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setPen(QPen(Qt::white, 1, Qt::DashLine));
if (selecting) {
painter.drawRect(rubberBand->geometry());
}
}
void RegionSelector::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
reject();
}
}
2.5 编辑对话框 (editdialog.h)
cpp
#ifndef EDITDIALOG_H
#define EDITDIALOG_H
#include <QDialog>
#include <QPixmap>
#include <QPainter>
namespace Ui {
class EditDialog;
}
class EditDialog : public QDialog
{
Q_OBJECT
public:
explicit EditDialog(const QPixmap &pixmap, QWidget *parent = nullptr);
~EditDialog();
QPixmap editedPixmap() const;
private slots:
void on_rectBtn_clicked();
void on_ellipseBtn_clicked();
void on_arrowBtn_clicked();
void on_textBtn_clicked();
void on_saveBtn_clicked();
void on_cancelBtn_clicked();
private:
Ui::EditDialog *ui;
QPixmap originalPixmap;
QPixmap editingPixmap;
QPixmap drawingPixmap;
enum DrawMode { None, Rect, Ellipse, Arrow, Text };
DrawMode currentMode;
QPoint lastPoint;
void drawShape(const QPoint &endPoint);
void updateDrawing();
};
#endif // EDITDIALOG_H
2.6 编辑对话框实现 (editdialog.cpp)
cpp
#include "editdialog.h"
#include "ui_editdialog.h"
#include <QPainter>
#include <QMouseEvent>
#include <QColorDialog>
#include <QInputDialog>
EditDialog::EditDialog(const QPixmap &pixmap, QWidget *parent)
: QDialog(parent), ui(new Ui::EditDialog)
{
ui->setupUi(this);
originalPixmap = pixmap;
editingPixmap = pixmap.copy();
drawingPixmap = pixmap.copy();
currentMode = None;
ui->previewLabel->setPixmap(editingPixmap.scaled(ui->previewLabel->size(), Qt::KeepAspectRatio));
ui->colorBtn->setStyleSheet("background-color: red;");
}
EditDialog::~EditDialog()
{
delete ui;
}
QPixmap EditDialog::editedPixmap() const
{
return editingPixmap;
}
void EditDialog::on_rectBtn_clicked()
{
currentMode = Rect;
setCursor(Qt::CrossCursor);
}
void EditDialog::on_ellipseBtn_clicked()
{
currentMode = Ellipse;
setCursor(Qt::CrossCursor);
}
void EditDialog::on_arrowBtn_clicked()
{
currentMode = Arrow;
setCursor(Qt::CrossCursor);
}
void EditDialog::on_textBtn_clicked()
{
currentMode = Text;
QString text = QInputDialog::getText(this, "添加文字", "请输入文字:");
if (!text.isEmpty()) {
QPainter painter(&editingPixmap);
painter.setPen(Qt::red);
painter.setFont(QFont("Arial", 20));
painter.drawText(50, 50, text);
ui->previewLabel->setPixmap(editingPixmap.scaled(ui->previewLabel->size(), Qt::KeepAspectRatio));
}
currentMode = None;
setCursor(Qt::ArrowCursor);
}
void EditDialog::on_saveBtn_clicked()
{
accept();
}
void EditDialog::on_cancelBtn_clicked()
{
reject();
}
void EditDialog::drawShape(const QPoint &endPoint)
{
QPainter painter(&editingPixmap);
painter.setPen(QPen(Qt::red, 3));
switch (currentMode) {
case Rect:
painter.drawRect(QRect(lastPoint, endPoint));
break;
case Ellipse:
painter.drawEllipse(QRect(lastPoint, endPoint));
break;
case Arrow: {
painter.drawLine(lastPoint, endPoint);
QPoint arrow1 = endPoint - QPoint(10, 5);
QPoint arrow2 = endPoint - QPoint(10, -5);
painter.drawLine(endPoint, arrow1);
painter.drawLine(endPoint, arrow2);
break;
}
default:
break;
}
ui->previewLabel->setPixmap(editingPixmap.scaled(ui->previewLabel->size(), Qt::KeepAspectRatio));
}
void EditDialog::updateDrawing()
{
editingPixmap = drawingPixmap.copy();
ui->previewLabel->setPixmap(editingPixmap.scaled(ui->previewLabel->size(), Qt::KeepAspectRatio));
}
void EditDialog::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && currentMode != None && currentMode != Text) {
lastPoint = event->pos();
drawingPixmap = editingPixmap.copy();
}
}
void EditDialog::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && currentMode != None && currentMode != Text) {
drawShape(event->pos());
}
}
void EditDialog::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && currentMode != None && currentMode != Text) {
drawShape(event->pos());
currentMode = None;
setCursor(Qt::ArrowCursor);
}
}
2.7 UI 文件 (screenshot.ui)
xml
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScreenShot</class>
<widget class="QMainWindow" name="ScreenShot">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Qt 屏幕截图工具</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="previewLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>400</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="text">
<string>点击按钮开始截图</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QPushButton" name="fullScreenBtn">
<property name="text">
<string>全屏截图</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="activeWindowBtn">
<property name="text">
<string>活动窗口</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="regionBtn">
<property name="text">
<string>区域截图</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="delayBtn">
<property name="text">
<string>延迟截图</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="actionLayout">
<item>
<widget class="QPushButton" name="saveBtn">
<property name="text">
<string>保存</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyBtn">
<property name="text">
<string>复制</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="editBtn">
<property name="text">
<string>编辑</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="infoLabel">
<property name="text">
<string>信息</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>就绪</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
2.8 资源文件 (resources.qrc)
xml
<RCC>
<qresource prefix="/icons">
<file>screenshot.png</file>
</qresource>
</RCC>
2.9 主函数 (main.cpp)
cpp
#include "screenshot.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setApplicationName("Qt屏幕截图工具");
a.setApplicationVersion("1.0");
ScreenShot w;
w.show();
return a.exec();
}
三、CMakeLists.txt(如果使用 CMake)
cmake
cmake_minimum_required(VERSION 3.16)
project(ScreenCapture LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5 REQUIRED COMPONENTS Widgets)
add_executable(${PROJECT_NAME}
main.cpp
screenshot.cpp
regionselector.cpp
editdialog.cpp
resources.qrc
)
target_link_libraries(${PROJECT_NAME} Qt5::Widgets)
参考代码 QT实现屏幕截图 www.youwenfan.com/contentcsv/115883.html
四、使用说明
4.1 编译运行
bash
# 使用 qmake
qmake ScreenCapture.pro
make
# 使用 CMake
mkdir build && cd build
cmake ..
make
4.2 快捷键
| 快捷键 | 功能 |
|---|---|
Ctrl+Alt+F |
全屏截图 |
Ctrl+Alt+R |
区域截图 |
Ctrl+S |
保存截图 |
Ctrl+C |
复制到剪贴板 |
Esc |
退出/取消 |
4.3 功能特点
全屏截图 - 捕获整个屏幕
窗口截图 - 捕获当前活动窗口
区域截图 - 鼠标拖拽选择区域
延迟截图 - 倒计时后截图
编辑标注 - 矩形、椭圆、箭头、文字
系统托盘 - 后台运行,随时截图
剪贴板 - 一键复制到剪贴板
快捷键 - 支持全局快捷键
五、扩展功能建议
- 滚动截图 - 自动滚动并拼接长网页
- OCR识别 - 截图后识别文字
- 云同步 - 自动上传到图床
- 历史记录 - 管理所有截图
- GIF录制 - 屏幕录制功能