Qt实现截屏

来源:互联网 发布:微商拍小视频软件 编辑:程序博客网 时间:2024/05/29 15:38

转载自:http://cpp51.blog.51cto.com/5346598/1331326


软件实现拖动截屏并顶置截屏结果,将最后截图复制到剪切板。可用于数据对比或其它场合。

软件运行流程为:快捷键-》抓屏-》截图-》顶置-》复制结果。

开始时没注意内存,截屏耗费大量内存,后优化后空闲时内存使用在4M左右。

以下为代码:

#-------------------------------------------------# Ssot.pro# Project created by QtCreator 2013-11-24T11:01:06##-------------------------------------------------QT       += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = SsotTEMPLATE = appSOURCES += main.cpp\        mainwindow.cpp \    ctoplabel.cpp \    aboutdialog.cppHEADERS  += mainwindow.h \    ctoplabel.h \    aboutdialog.hRESOURCES += \    resource.qrcRC_FILE  += \    info.rcOTHER_FILES += \    info.rc \    readme.txt

mainwindow.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include<QtWidgets>#include<QVector>#include"aboutdialog.h"#include"ctoplabel.h"class MainWindow : public QMainWindow{    Q_OBJECTpublic:    MainWindow(QWidget *parent = 0);    ~MainWindow();    struct IDStruct    {        WId id;        CTopLabel* pTopLabel;    };private:    //托盘图标    //注册热键    void registerGlobalKey();    //开始截图热键ID    int hotkeyShotBgId;    QVector<IDStruct> topLabelList;    //初始化托盘;    void initTray();    //是否正在截图    bool isShotting;    //本地事件    bool nativeEvent(const QByteArray &eventType, void *message, long *result);    AboutDialog* aboutDialog;    QSystemTrayIcon* trayIcon;private slots:    //开始截图    void hotkeyShotBgReceived();    void clearShots();public slots:    //关闭    void clearShot(WId i);    //设置isShotting为false,允许再次截屏    void allowShot();    //关于    void doAboutAction();signals:    //开始截屏热键按下    void hotkeyShotBgPressed();};#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"#include<QDebug>#include"aboutdialog.h"MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){    topLabelList.clear();    isShotting=false;    registerGlobalKey();    initTray();    aboutDialog =new AboutDialog;    connect(this,SIGNAL(hotkeyShotBgPressed()),SLOT(hotkeyShotBgReceived()));    trayIcon->showMessage(tr("截图置顶 v1.0.1"),"程序已启动,按\"Shift+Z\"后拖动鼠标!");    trayIcon->setToolTip(tr("按\"Shift+Z\"后拖动鼠标"));}MainWindow::~MainWindow(){    if(UnregisterHotKey(HWND(this->winId()), hotkeyShotBgId))    {        qDebug("SHIFT+Z IS UNREGISTED");    }    clearShots();}void MainWindow::registerGlobalKey(){    hotkeyShotBgId =GlobalAddAtom(L"hotkeyShotBg") - 0xC000;    if(RegisterHotKey((HWND(this->winId())), hotkeyShotBgId,MOD_SHIFT,0x5A))    {        qDebug("SHIFT+Z IS REGISTERED");    }}bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result){    if(eventType=="windows_generic_MSG")    {        //qDebug("This is windows MSG");        MSG* msg=static_cast<MSG*>(message);        if(msg->message==WM_HOTKEY)        {            UINT fuModifiers = (UINT) LOWORD(msg->lParam);  // 模式            UINT uVirtKey = (UINT) HIWORD(msg->lParam);     // 键值            // qDebug("This is HotKey!");            if(fuModifiers==MOD_SHIFT&&uVirtKey==0x5A)            {                emit hotkeyShotBgPressed();                qDebug("SHIFT+Z is Pressed");            }            return true;        }    }    return  false;}void MainWindow::hotkeyShotBgReceived(){    //如果正在截图,则不处理    if(!isShotting)    {        CTopLabel* fullScreenLabel=new CTopLabel;        //用于传递窗口指针及WId        IDStruct idStruct;        idStruct.id=fullScreenLabel->winId();        idStruct.pTopLabel=fullScreenLabel;        topLabelList.append(idStruct);        //窗口发出关闭信号        connect(fullScreenLabel,SIGNAL(meClosed(WId)),this,SLOT(clearShot(WId)));        connect(fullScreenLabel,SIGNAL(shotted()),this,SLOT(allowShot()));        fullScreenLabel->showFullScreen();    }    isShotting=true;}void MainWindow::initTray(){    trayIcon=new QSystemTrayIcon(this);    trayIcon->setIcon(QIcon(":/icon/resource/icon.png"));    QMenu* trayMenu=new QMenu;    QAction* exitAction=new QAction(tr("退出 (&X)"),trayMenu);    QAction* aboutAciton=new QAction(tr("关于 (&A)"),trayMenu);    QAction* clearAciton=new QAction(tr("清除 (&C)"),trayMenu);    trayMenu->addAction(clearAciton);    connect(clearAciton,SIGNAL(triggered()),this,SLOT(clearShots()));    trayMenu->addSeparator();    trayMenu->addAction(aboutAciton);    connect(aboutAciton,SIGNAL(triggered()),this,SLOT(doAboutAction()));    trayMenu->addAction(exitAction);    connect(exitAction,SIGNAL(triggered()),this,SLOT(close()));    trayIcon->setContextMenu(trayMenu);    trayIcon->show();}void MainWindow::clearShots(){    while(topLabelList.count()>0)    {        delete topLabelList.first().pTopLabel;        topLabelList.first().pTopLabel=NULL;        topLabelList.removeFirst();    }}void MainWindow::clearShot(WId id){    qDebug()<<id;    if(!topLabelList.empty());    {        for(int i=0;i<topLabelList.count();i++)        {            if(id==topLabelList[i].id)            {                //释放内存                delete topLabelList[i].pTopLabel;                qDebug()<<"is deleted!";                //防止野指针                topLabelList[i].pTopLabel=NULL;            }        }    }    allowShot();}void MainWindow::allowShot(){    isShotting=false;}//关于对话框void MainWindow::doAboutAction(){    qDebug()<<"about";    aboutDialog->setText(":/icon/readme.txt",true);    aboutDialog->setWindowTitle(tr("关于截图置顶 v1.0.1"));    aboutDialog->setLogo(":/icon/resource/about-logo.png");    aboutDialog->setInfo        (            "<table border=\"0\">            <tr height=\"22\">            <td width=270 valign=\"middle\" >            <b>截图置顶 v1.0.1</b>            </td><td>            <a href=\"https://github.com/pansinm/Ssot/\">查看源代码</a>            </td></tr>            <tr height=\"22\">            <td width=300 valign=\"middle\">by pansinm</td>            <td>pansinm@163.com</td></tr></table>"        );    aboutDialog->show();}

ctoplabel.h

//截屏窗口#ifndef CTOPLABEL_H#define CTOPLABEL_H#include <QtWidgets>class CTopLabel : public QLabel{    Q_OBJECTpublic:    explicit CTopLabel(QWidget *parent = 0);    //截图状态    enum shotState{initShot,beginShot,finishShot};    //winId    void setLabelId(WId id){labelId=id;}    ~CTopLabel();signals:    //发送关闭信号    void meClosed(WId);    //完成截屏信号    void shotted();private:    int labelId;    //全屏Pixmap    QPixmap fullScreenPixmap;    //截图pixmap    QPixmap resultPixmap;    //截图状态    shotState currentState;    //鼠标起始点    QPoint beginPoint;    //终点    QPoint endPoint;    //鼠标拖动矩形    QRect shotRect;    //是否正在截图    bool isShot;    //左键是否按下    bool isPressed;    //拖动点    QPoint dragPosition;    void paintEvent(QPaintEvent *);    void mousePressEvent(QMouseEvent *ev);    void mouseMoveEvent(QMouseEvent *ev);    void mouseReleaseEvent(QMouseEvent *ev);    void mouseDoubleClickEvent(QMouseEvent *);};#endif // CTOPLABEL_H

ctoplabel.cpp

#include "ctoplabel.h"CTopLabel::CTopLabel(QWidget *parent) : QLabel(parent){    setLabelId(this->winId());    setAutoFillBackground(true);    //当前状态设置为初始化    currentState=initShot;    //设置为正在截图    isShot=true;    //初始化    beginPoint=QPoint(0,0);    endPoint=QPoint(0,0);    shotRect=QRect(0,0,0,0);    //左键按下状态    isPressed=false;    //初始化窗口拖动点    dragPosition=QPoint(-1,-1);    //设置无边框,任务栏无图标,顶置    setWindowFlags(Qt::FramelessWindowHint|Qt::Tool|Qt::WindowStaysOnTopHint);    //抓取屏幕    fullScreenPixmap = QPixmap::grabWindow(QApplication::desktop()->winId());}CTopLabel::~CTopLabel(){}void CTopLabel::mousePressEvent(QMouseEvent *ev){    if(ev->button()==Qt::LeftButton)    {        isPressed=true;        if(isShot)        {            //正在截图时,改变状态及beginPoint            beginPoint=ev->pos();            qDebug()<<beginPoint.x();        }        else        {            //截图完毕时            dragPosition=ev->globalPos()-this->pos();        }    }}void CTopLabel::mouseMoveEvent(QMouseEvent *ev){    if(isPressed)    {        if(isShot)        {            currentState=beginShot;            //截屏拖动时            //qDebug()<<"isShot";            endPoint=ev->pos();            //根据拖动位置,计算截图区域            //左上->右下            if(beginPoint.x()<endPoint.x()&&beginPoint.y()<endPoint.y())            {                shotRect=QRect(beginPoint,endPoint);            }            //左下->右上            else if(beginPoint.x()<endPoint.x()&&beginPoint.y()>endPoint.y())            {                shotRect=QRect(beginPoint.x(),endPoint.y(),endPoint.x()-beginPoint.x(),beginPoint.y()-endPoint.y());            }            //右上->左下            else if(beginPoint.x()>endPoint.x()&&beginPoint.y()<endPoint.y())            {                shotRect=QRect(endPoint.x(),beginPoint.y(),beginPoint.x()-endPoint.x(),endPoint.y()-beginPoint.y());            }            //右下->左上            else            {                shotRect=QRect(endPoint,beginPoint);            }            update();        }        else        {            //窗口移动            move(ev->globalPos()-dragPosition);        }    }}void CTopLabel::mouseReleaseEvent(QMouseEvent *ev){    if(ev->button()==Qt::LeftButton)    {        endPoint=ev->pos();        //qDebug()<<beginPoint.x()<<endPoint.x();        if(isShot)        {            if(currentState==beginShot)            {                isShot=false;                //改变状态                currentState=finishShot;                //截图区域图像                resultPixmap=fullScreenPixmap.copy(shotRect);                fullScreenPixmap.loadFromData(NULL);                this->repaint();                setGeometry(shotRect);                emit shotted();                //结果复制到剪切板                QClipboard *clipboard=QApplication::clipboard();                clipboard->setPixmap(resultPixmap);            }        }        else        {            move(ev->globalPos()-dragPosition);        }    }    isPressed=false;}void CTopLabel::mouseDoubleClickEvent(QMouseEvent *e){    if(e->button()==Qt::LeftButton)    {        //关闭当前        emit meClosed(labelId);    }}void CTopLabel::paintEvent(QPaintEvent *){    QPainter painter(this);    painter.setFont(QFont("simsun",20));    switch(currentState)    {    case initShot:        painter.drawPixmap(0,0,fullScreenPixmap);        painter.setPen(QPen(Qt::blue,1,Qt::SolidLine,Qt::FlatCap));//设置画笔        break;    case beginShot:        painter.drawPixmap(0,0,fullScreenPixmap);        painter.setPen(QPen(Qt::blue,1,Qt::SolidLine,Qt::FlatCap));//设置画笔        painter.drawRect(shotRect);        break;    case finishShot:        painter.drawPixmap(0,0,resultPixmap);        painter.setPen(QPen(Qt::gray,1,Qt::SolidLine,Qt::FlatCap));//设置画笔        painter.drawRect(0,0,resultPixmap.size().width()-1,resultPixmap.size().height()-1);        //截图结果窗口        break;    }}

aboutdialog.h

//这个是我原来做的关于对话框的一个模板,直接拿来用了#ifndef ABOUTDIALOG_H#define ABOUTDIALOG_H#include <QDialog>#include<QLabel>#include<QTextEdit>#include<QString>#include<QPushButton>class AboutDialog : public QDialog{    Q_OBJECTpublic:    explicit AboutDialog(QWidget *parent = 0);    //如果为true,则text为文件名,文本框中加载文件内容(文本文件)    void setText(const QString str,bool isFileName=false);    //370x45图片    void setLogo(const QString filename);    void setInfo(const QString info);    void setBackground(const QString styleSheet="default");private:    QLabel* label_Logo;    QLabel* label_Info;    QTextEdit* textEdit;    QPushButton* btn_confirm;    void init();signals:public slots:    void openUrl(QString);};#endif // ABOUTDIALOG_H

aboutdialog.cpp

#include "aboutdialog.h"#include<QHBoxLayout>#include<QVBoxLayout>#include<QPixmap>#include<QDesktopServices>#include<QUrl>#include<QFile>#include<QTextStream>AboutDialog::AboutDialog(QWidget *parent) :    QDialog(parent){    init();}void AboutDialog::init(){    //标题栏不要按钮    this->setWindowFlags(Qt::WindowTitleHint | Qt::CustomizeWindowHint);    label_Logo=new QLabel;    label_Logo->setMinimumSize(370,45);    label_Info=new QLabel;    label_Info->setMinimumSize(370,45);    textEdit=new QTextEdit;    textEdit->setMinimumSize(370,115);    textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);    textEdit->setReadOnly(true);    btn_confirm=new QPushButton("确定");    btn_confirm->setMinimumSize(75,25);    QHBoxLayout* hlayout=new QHBoxLayout;    hlayout->addStretch();    hlayout->addWidget(btn_confirm);    hlayout->setMargin(4);    QVBoxLayout* vlayout=new QVBoxLayout;    vlayout->addWidget(label_Logo);    vlayout->addWidget(label_Info);    vlayout->addWidget(textEdit);    vlayout->addLayout(hlayout);    vlayout->setMargin(0);    setLayout(vlayout);    this->setMinimumSize(370,254);    this->setMaximumSize(370,254);    setBackground();    connect(btn_confirm,SIGNAL(clicked()),this,SLOT(hide()));}void AboutDialog::setLogo(const QString filename){    QPixmap pixmap(filename);    label_Logo->setPixmap(pixmap);}void AboutDialog::setInfo(const QString info){    label_Info->setText(info);    connect(label_Info,SIGNAL(linkActivated(QString)),this,SLOT(openUrl(QString)));}void AboutDialog::setText(const QString str, bool isFileName){    if(isFileName==false)        textEdit->setText(str);    else if(isFileName==true)    {        QFile file(str);        if(file.open(QIODevice::ReadOnly|QIODevice::Text))        {            QTextStream in(&file);            QString s;            s=in.readAll();            textEdit->setText(s);            file.close();        }    }}void AboutDialog::setBackground(const QString styleSheet){    if(styleSheet=="default")    {        this->setStyleSheet("AboutDialog{background: rgb(238, 241, 255);}");    }    else        setStyleSheet(styleSheet);}void AboutDialog::openUrl(QString url){    QDesktopServices::openUrl(QUrl(url));}

main.cpp

#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]){    QApplication a(argc, argv);    MainWindow w;    //w.show();    return a.exec();}

resource.qrc

<RCC>    <qresource prefix="/icon">        <file>resource/icon.png</file>        <file>readme.txt</file>        <file>resource/about-logo.png</file>    </qresource></RCC>

以下为运行效果:
这里写图片描述
这里写图片描述
这里写图片描述

  1. QPixmap::grabWindow(QApplication::desktop()->winId())//用于抓取整个屏幕
  2. 利用paintEve绘制拖动矩形,并将剪切结果绘制到窗口。
  3. 截图完调整窗口大小前用repaint重绘窗口,否则有可能闪烁。
  4. 关闭一个窗口时,用delete删除内存,否则内存会一直叠。
  5. 截图完毕后用fullScreenPixmap.loadFromData(NULL)删除全屏pixmap,可释放几M的内存。
// 可设置无边框,无任务栏图标,顶置窗口。setWindowFlags(Qt::FramelessWindowHint|Qt::Tool|Qt::WindowStaysOnTopHint);

源代码地址为:https://github.com/pansinm/Ssot
程序下载地址:http://url.cn/K89Rrn

0 0
原创粉丝点击