Qt 实现屏幕截图功能

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 功能特点

全屏截图 - 捕获整个屏幕

窗口截图 - 捕获当前活动窗口

区域截图 - 鼠标拖拽选择区域

延迟截图 - 倒计时后截图

编辑标注 - 矩形、椭圆、箭头、文字

系统托盘 - 后台运行,随时截图

剪贴板 - 一键复制到剪贴板

快捷键 - 支持全局快捷键


五、扩展功能建议

  1. 滚动截图 - 自动滚动并拼接长网页
  2. OCR识别 - 截图后识别文字
  3. 云同步 - 自动上传到图床
  4. 历史记录 - 管理所有截图
  5. GIF录制 - 屏幕录制功能
相关推荐
小白学大数据1 小时前
爬虫性能天花板:asyncio赋能 Aiohttp,并发提速 10 倍
开发语言·爬虫·数据分析
凡人叶枫2 小时前
Effective C++ 条款07:为多态基类声明 virtual 析构函数
linux·c语言·开发语言·c++
凡人叶枫2 小时前
Effective C++ 条款10:令 operator= 返回一个 reference to *this
java·linux·服务器·开发语言·c++·effective c++
满天星83035772 小时前
【Qt】信号和槽(三) (断开连接和lambda函数)
qt
leo__5202 小时前
MATLAB实现牧羊人算法
开发语言·算法·matlab
fpcc3 小时前
C++编程实践—C++实现类似Qt的信号槽机制
c++·qt
格发许可优化管理系统3 小时前
Mentor许可证使用规定全解析
java·大数据·c语言·开发语言·c++
郝学胜_神的一滴3 小时前
Qt 高级开发 030:QListWidget 右键菜单全解,从策略配置到精准删除的优雅实现
c++·qt
FL16238631293 小时前
C# OpenCvSharp 基于霍夫变换直线检测的文本图像倾斜校正文本图像倾斜校
开发语言·c#