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录制 - 屏幕录制功能
相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript