程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Qt實現截屏

Qt實現截屏

編輯:關於C語言

   軟件實現拖動截屏並頂置截屏結果,將最後截圖復制到剪切板。可用於數據對比或其它場合。

   軟件運行流程為:快捷鍵-》抓屏-》截圖-》頂置-》復制結果。

   開始時沒注意內存,截屏耗費大量內存,後優化後空閒時內存使用在4M左右。

   以下為代碼:

 

#-------------------------------------------------
# Ssot.pro
# Project created by QtCreator 2013-11-24T11:01:06
#
#-------------------------------------------------
QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Ssot
TEMPLATE = app
SOURCES += main.cpp\
        mainwindow.cpp \
    ctoplabel.cpp \
    aboutdialog.cpp
HEADERS  += mainwindow.h \
    ctoplabel.h \
    aboutdialog.h
RESOURCES += \
    resource.qrc
RC_FILE  += \
    info.rc
OTHER_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_OBJECT
public:
    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>[email protected]</td></tr></table>");
    aboutDialog->show();
}

ctoplabel.h

//截屏窗口
#ifndef CTOPLABEL_H
#define CTOPLABEL_H
#include <QtWidgets>
class CTopLabel : public QLabel
{
    Q_OBJECT
public:
    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=\'#\'"     //當前狀態設置為初始化
    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_OBJECT
public:
    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=\'#\'"  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=\'#\'"  QString styleSheet)
{
    if(styleSheet=="default")
    {
        this->setStyleSheet("AboutDialog{background=\'#\'"  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>


以下為運行效果:

191009885.jpg

191012373.jpg

191019827.jpg



  • QPixmap::grabWindow(QApplication::desktop()->winId())//用於抓取整個屏幕

  • 利用paintEve繪制拖動矩形,並將剪切結果繪制到窗口。

  • 截圖完調整窗口大小前用repaint重繪窗口,否則有可能閃爍。

  • 關閉一個窗口時,用delete刪除內存,否則內存會一直疊。

  • 截圖完畢後用fullScreenPixmap.loadFromData(NULL)刪除全屏pixmap,可釋放幾M的內存。

  • setWindowFlags(Qt::FramelessWindowHint|Qt::Tool|Qt::WindowStaysOnTopHint);可設置無邊框,無任務欄圖標,頂置窗口。


源代碼地址為:https://github.com/pansinm/Ssot

程序下載地址:http://url.cn/K89Rrn




本文出自 “水水的菜鳥” 博客,請務必保留此出處http://cpp51.blog.51cto.com/5346598/1331326

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved