1. 程式人生 > >Qt實現擷取螢幕小程式

Qt實現擷取螢幕小程式

[注]:本程式在Windows下實現,按理來說在其他平臺也可用(只需要改一下CCuter的某個函式,文中紅字標註)

先上效果圖(我兩個顯示屏整張圖太大,只截關鍵部分上傳):

以下是程式碼,兩個部分:一個是CCuter截圖選擇器,一個是CScreenShoot截圖程式

CCuter.h

#ifndef CCUTER_H
#define CCUTER_H

#include <QWidget>

//* 截圖選擇器
class CCuter: public QWidget
{
    Q_OBJECT
public:
    CCuter();

signals:
    void cut   (); //* 截圖
    void cancle(); //* 取消

protected:
    int     mTouchBorderWidth;
    bool    mMousePressed;
    QPoint  mLastMouseMovePos;
    int     mCurrentHit;

    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
    void mouseMoveEvent(QMouseEvent *e);

    int  OnNcHitTest(MSG*   msg); //* 檢測滑鼠狀態並嘗試改變大小(native window)
    int  OnNcHitTest(QPoint pos); //* 檢測滑鼠狀態並嘗試改變大小(software method,without native window)
    void contextMenuEvent(QContextMenuEvent *e); //* 支援的選單項
};

#endif // CCUTER_H

[注]:如果是在windows以外的平臺需要修改OnNcHitTest函式的實現。

CCuter.cpp

#include "CCuter.h"

#include <QPainter>
#include <QPainterPath>
#include <QMouseEvent>
#include <QMenu>
#include <QDebug>
#include <Windows.h>
#include <WindowsX.h>


CCuter::CCuter()
    :mTouchBorderWidth(10)
    ,mMousePressed(false)
    ,mCurrentHit(HTCLIENT)
{
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Widget);
    this->setAttribute(Qt::WA_TranslucentBackground);

    this->setMouseTracking(true);
    this->setCursor(Qt::OpenHandCursor);
    this->setContextMenuPolicy(Qt::DefaultContextMenu);
}

void CCuter::mousePressEvent(QMouseEvent *e)
{
    QWidget::mousePressEvent(e);
    mMousePressed = true;
    mLastMouseMovePos = e->globalPos();
}

void CCuter::mouseReleaseEvent(QMouseEvent *e)
{
    QWidget::mouseReleaseEvent(e);
    mMousePressed = false;

    //TODO:Cursor
    if(!mMousePressed)
    {
        switch(mCurrentHit)
        {
        case HTLEFT:
            this->setCursor(Qt::SizeHorCursor);
            break;
        case HTRIGHT:
            this->setCursor(Qt::SizeHorCursor);
            break;
        case HTTOP:
            this->setCursor(Qt::SizeVerCursor);
            break;
        case HTBOTTOM:
            this->setCursor(Qt::SizeVerCursor);
            break;
        case HTTOPLEFT:
            this->setCursor(Qt::SizeFDiagCursor);
            break;
        case HTTOPRIGHT:
            this->setCursor(Qt::SizeBDiagCursor);
            break;
        case HTBOTTOMLEFT:
            this->setCursor(Qt::SizeBDiagCursor);
            break;
        case HTBOTTOMRIGHT:
            this->setCursor(Qt::SizeFDiagCursor);
            break;
        case HTCLIENT:
            this->setCursor(Qt::OpenHandCursor);
            break;
        }
    }
}

void CCuter::mouseMoveEvent(QMouseEvent *e)
{
    QPoint currentPos = e->globalPos();
    QPoint defPos = currentPos - mLastMouseMovePos;
    mLastMouseMovePos =  e->globalPos();

    //TODO:Cursor
    if(!mMousePressed)
    {
        mCurrentHit = OnNcHitTest(e->pos());
        switch(mCurrentHit)
        {
        case HTLEFT:
            this->setCursor(Qt::SizeHorCursor);
            break;
        case HTRIGHT:
            this->setCursor(Qt::SizeHorCursor);
            break;
        case HTTOP:
            this->setCursor(Qt::SizeVerCursor);
            break;
        case HTBOTTOM:
            this->setCursor(Qt::SizeVerCursor);
            break;
        case HTTOPLEFT:
            this->setCursor(Qt::SizeFDiagCursor);
            break;
        case HTTOPRIGHT:
            this->setCursor(Qt::SizeBDiagCursor);
            break;
        case HTBOTTOMLEFT:
            this->setCursor(Qt::SizeBDiagCursor);
            break;
        case HTBOTTOMRIGHT:
            this->setCursor(Qt::SizeFDiagCursor);
            break;
        case HTCLIENT:
            this->setCursor(Qt::OpenHandCursor);
            break;
        }
    }
    else
    {
        QRect resizeRect = this->geometry();
        switch(mCurrentHit)
        {
        case HTLEFT:
            resizeRect.setLeft(currentPos.x());
            break;
        case HTRIGHT:
            resizeRect.setRight(currentPos.x());
            break;
        case HTTOP:
            resizeRect.setTop(currentPos.y());
            break;
        case HTBOTTOM:
            resizeRect.setBottom(currentPos.y());
            break;
        case HTTOPLEFT:
            resizeRect.setTopLeft(currentPos);
            break;
        case HTTOPRIGHT:
            resizeRect.setTopRight(currentPos);
            break;
        case HTBOTTOMLEFT:
            resizeRect.setBottomLeft(currentPos);
            break;
        case HTBOTTOMRIGHT:
            resizeRect.setBottomRight(currentPos);
            break;
        case HTCLIENT:
            this->move(this->pos() + defPos);
            break;
        }

        if(mCurrentHit != HTCLIENT)
        {
            if(resizeRect.left() > resizeRect.right())
            {
                int t = resizeRect.left();
                resizeRect.setLeft(resizeRect.right());
                resizeRect.setRight(t);
                switch(mCurrentHit)
                {
                case HTLEFT:
                    mCurrentHit = HTRIGHT;
                    break;
                case HTRIGHT:
                    mCurrentHit = HTLEFT;
                    break;
                case HTTOP:
                    break;
                case HTBOTTOM:
                    break;
                case HTTOPLEFT:
                    mCurrentHit = HTTOPRIGHT;
                    break;
                case HTTOPRIGHT:
                    mCurrentHit = HTTOPLEFT;
                    break;
                case HTBOTTOMLEFT:
                    mCurrentHit = HTBOTTOMRIGHT;
                    break;
                case HTBOTTOMRIGHT:
                    mCurrentHit = HTBOTTOMLEFT;
                    break;
                case HTCLIENT:
                    break;
                }
            }
            if(resizeRect.top() > resizeRect.bottom())
            {
                int t = resizeRect.top();
                resizeRect.setTop(resizeRect.bottom());
                resizeRect.setBottom(t);
                switch(mCurrentHit)
                {
                case HTLEFT:
                    break;
                case HTRIGHT:
                    break;
                case HTTOP:
                    mCurrentHit = HTBOTTOM;
                    break;
                case HTBOTTOM:
                    mCurrentHit = HTTOP;
                    break;
                case HTTOPLEFT:
                    mCurrentHit = HTBOTTOMLEFT;
                    break;
                case HTTOPRIGHT:
                    mCurrentHit = HTBOTTOMRIGHT;
                    break;
                case HTBOTTOMLEFT:
                    mCurrentHit = HTTOPLEFT;
                    break;
                case HTBOTTOMRIGHT:
                    mCurrentHit = HTTOPRIGHT;
                    break;
                case HTCLIENT:
                    break;
                }
            }

            this->setGeometry(resizeRect);
        }
    }


    QWidget::mouseMoveEvent(e);
}

int CCuter::OnNcHitTest(MSG *msg)
{
    POINT pt;
    pt.x = GET_X_LPARAM(msg->lParam);
    pt.y = GET_Y_LPARAM(msg->lParam);

    HWND hwnd = (HWND)this->winId();
    //::ScreenToClient(hwnd, &pt);

    RECT rcClient;
    ::GetWindowRect(hwnd, &rcClient);

    if (!::IsZoomed(hwnd))
    {
        if((pt.x < (rcClient.right + mTouchBorderWidth)) &&
           (pt.x > (rcClient.right - mTouchBorderWidth))   )
        {
            if((pt.y < (rcClient.top + mTouchBorderWidth)) &&
               (pt.y > (rcClient.top - mTouchBorderWidth))   )
            {
                return HTTOPRIGHT;
            }

            if((pt.y < (rcClient.bottom + mTouchBorderWidth)) &&
               (pt.y > (rcClient.bottom - mTouchBorderWidth))   )
            {
                return HTBOTTOMRIGHT;
            }

            return HTRIGHT;
        }

        if((pt.x < (rcClient.left + mTouchBorderWidth)) &&
           (pt.x > (rcClient.left - mTouchBorderWidth))   )
        {
            if((pt.y < (rcClient.top + mTouchBorderWidth)) &&
               (pt.y > (rcClient.top - mTouchBorderWidth))   )
            {
                return HTTOPLEFT;
            }

            if((pt.y < (rcClient.bottom + mTouchBorderWidth)) &&
               (pt.y > (rcClient.bottom - mTouchBorderWidth))   )
            {
                return HTBOTTOMLEFT;
            }

            return HTLEFT;
        }

        if((pt.y < (rcClient.top + mTouchBorderWidth)) &&
           (pt.y > (rcClient.top - mTouchBorderWidth))   )
        {
            if((pt.x < (rcClient.right + mTouchBorderWidth)) &&
               (pt.x > (rcClient.right - mTouchBorderWidth))   )
            {
                return HTTOPRIGHT;
            }

            if((pt.x < (rcClient.left + mTouchBorderWidth)) &&
               (pt.x > (rcClient.left - mTouchBorderWidth))   )
            {
                return HTTOPLEFT;
            }


            return HTTOP;
        }

        if((pt.y < (rcClient.bottom + mTouchBorderWidth)) &&
           (pt.y > (rcClient.bottom - mTouchBorderWidth))   )
        {

            if((pt.x < (rcClient.right + mTouchBorderWidth)) &&
               (pt.x > (rcClient.right - mTouchBorderWidth))   )
            {
                return HTBOTTOMRIGHT;
            }

            if((pt.x < (rcClient.left + mTouchBorderWidth)) &&
               (pt.x > (rcClient.left - mTouchBorderWidth))   )
            {
                return HTBOTTOMLEFT;
            }


            return HTBOTTOM;
        }

    }

//    if (pt.y < mTitleBarRect.height())
//    {

//        return HTCAPTION;
//    }

    return HTCLIENT;
}



int CCuter::OnNcHitTest(QPoint pos)
{

    QRect rcClient = this->rect();

    if((pos.x() < (rcClient.right() + mTouchBorderWidth)) &&
       (pos.x() > (rcClient.right() - mTouchBorderWidth))   )
    {
        if((pos.y() < (rcClient.top() + mTouchBorderWidth)) &&
           (pos.y() > (rcClient.top() - mTouchBorderWidth))   )
        {
            return HTTOPRIGHT;
        }

        if((pos.y() < (rcClient.bottom() + mTouchBorderWidth)) &&
           (pos.y() > (rcClient.bottom() - mTouchBorderWidth))   )
        {
            return HTBOTTOMRIGHT;
        }

        return HTRIGHT;
    }

    if((pos.x() < (rcClient.left() + mTouchBorderWidth)) &&
       (pos.x() > (rcClient.left() - mTouchBorderWidth))   )
    {
        if((pos.y() < (rcClient.top() + mTouchBorderWidth)) &&
           (pos.y() > (rcClient.top() - mTouchBorderWidth))   )
        {
            return HTTOPLEFT;
        }

        if((pos.y() < (rcClient.bottom() + mTouchBorderWidth)) &&
           (pos.y() > (rcClient.bottom() - mTouchBorderWidth))   )
        {
            return HTBOTTOMLEFT;
        }

        return HTLEFT;
    }

    if((pos.y() < (rcClient.top() + mTouchBorderWidth)) &&
       (pos.y() > (rcClient.top() - mTouchBorderWidth))   )
    {
        if((pos.x() < (rcClient.right() + mTouchBorderWidth)) &&
           (pos.x() > (rcClient.right() - mTouchBorderWidth))   )
        {
            return HTTOPRIGHT;
        }

        if((pos.x() < (rcClient.left() + mTouchBorderWidth)) &&
           (pos.x() > (rcClient.left() - mTouchBorderWidth))   )
        {
            return HTTOPLEFT;
        }


        return HTTOP;
    }

    if((pos.y() < (rcClient.bottom() + mTouchBorderWidth)) &&
       (pos.y() > (rcClient.bottom() - mTouchBorderWidth))   )
    {

        if((pos.x() < (rcClient.right() + mTouchBorderWidth)) &&
           (pos.x() > (rcClient.right() - mTouchBorderWidth))   )
        {
            return HTBOTTOMRIGHT;
        }

        if((pos.x() < (rcClient.left() + mTouchBorderWidth)) &&
           (pos.x() > (rcClient.left() - mTouchBorderWidth))   )
        {
            return HTBOTTOMLEFT;
        }


        return HTBOTTOM;
    }



//    if (pt.y < mTitleBarRect.height())
//    {

//        return HTCAPTION;
//    }

    return HTCLIENT;
}

void CCuter::contextMenuEvent(QContextMenuEvent *e)
{
    QMenu menu;
    QAction* acCut    = menu.addAction(QString::fromLocal8Bit("擷取"));
    QAction* acCancel = menu.addAction(QString::fromLocal8Bit("取消"));

    QAction* tag = menu.exec(e->globalPos());

    if(tag == acCut)
    {
        emit cut();
    }
    else if(tag == acCancel)
    {
        emit cancle();
    }
}

CScreenShoot.h

#ifndef CSCREENSHOOT_H
#define CSCREENSHOOT_H

#include <QWidget>
#include <QPixmap>
#include <QEventLoop>

class CCuter;


class CScreenShoot: public QWidget
{
    Q_OBJECT
public:
    CScreenShoot();

private:
    static CScreenShoot* gInstance        ; //* 截圖單例
    QPixmap              mPixmapBackground; //* 待截圖幕
    QPixmap              mCutPixmap       ; //* 被截圖幕
    CCuter*              mCuter           ; //* 截圖選擇器
    QEventLoop           mLoop            ; //* 內迴圈
    bool                 mIsInitCuterSize ; //* 當前是否可以改變截圖選擇器的尺寸

    void paintEvent(QPaintEvent* e);
    bool eventFilter(QObject *obj, QEvent *e);
    void keyPressEvent(QKeyEvent *e);
    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
    void mouseMoveEvent(QMouseEvent *e);

private slots:
    void cutPixmap(); //* 根據選擇器的位置和尺寸截圖
    void cancel();    //* 取消

public:
    static CScreenShoot* instance();

    bool    cut   (); //* 截圖成功返回true,否則返回false
    QPixmap pixmap(); //* 當前被擷取的螢幕
};

#endif // CSCREENSHOOT_H

CScreenShoot.cpp

#include "CScreenShoot.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QScreen>
#include <QPainter>
#include <QDebug>
#include <Windows.h>
#include <QPainterPath>
#include <QKeyEvent>
#include <QMouseEvent>

#include "CCuter.h"

CScreenShoot* CScreenShoot::gInstance = NULL;

CScreenShoot::CScreenShoot():
    mIsInitCuterSize(false)
  , mCuter(NULL)
{
    this->setWindowFlags(Qt::FramelessWindowHint| Qt::WindowStaysOnTopHint | Qt::Tool);
    this->setMouseTracking(true);

    mCuter = new CCuter;
    mCuter->setParent(this);
    mCuter->installEventFilter(this);

    connect(mCuter, &CCuter::cut   , this, &CScreenShoot::cutPixmap);
    connect(mCuter, &CCuter::cancle, this, &CScreenShoot::cancel   );
}

void CScreenShoot::paintEvent(QPaintEvent *e)
{
    QWidget::paintEvent(e);

    QPainter painter(this);
    painter.save();

    // bkg
    painter.drawPixmap(0,0, mPixmapBackground);

    if(mCuter->isVisible())
    {
        // mask
        QPainterPath maskLayer;
        QPolygon polygon;
        QRect bkgRect = this->rect();
        QRect cutRect = mCuter->geometry();

        polygon.append(bkgRect.topLeft());
        polygon.append(bkgRect.topRight());
        polygon.append(bkgRect.bottomRight());
        polygon.append(bkgRect.bottomLeft());
        polygon.append(bkgRect.topLeft());

        polygon.append(cutRect.topLeft());
        polygon.append(cutRect.topRight());
        polygon.append(cutRect.bottomRight());
        polygon.append(cutRect.bottomLeft());
        polygon.append(cutRect.topLeft());

        maskLayer.addPolygon(polygon);

        QBrush br(QColor(0,0,0,125));
        painter.fillPath(maskLayer, br);

        // selector
        painter.setPen(QPen(Qt::blue, 2));
        QRect tagRect = mCuter->geometry();
        painter.drawRect(tagRect);

        QPainterPath path;
        QRect rect1(tagRect.topLeft()     + QPoint(-3, -3), QSize(8, 8));
        QRect rect2(tagRect.topRight()    + QPoint(-3, -3), QSize(8, 8));
        QRect rect3(tagRect.bottomLeft()  + QPoint(-3, -3), QSize(8, 8));
        QRect rect4(tagRect.bottomRight() + QPoint(-3, -3), QSize(8, 8));

        QRect rect5(tagRect.topLeft()     + QPoint(tagRect.width()/2 - 3,  -3), QSize(8, 8));
        QRect rect6(tagRect.topLeft()     + QPoint( -3, tagRect.height()/2 - 3) , QSize(8, 8));
        QRect rect7(tagRect.bottomRight() + QPoint(-tagRect.width()/2 - 3, -3) , QSize(8, 8));
        QRect rect8(tagRect.bottomRight() + QPoint(-3,  -tagRect.height()/2 - 3) , QSize(8, 8));

        path.addRect(rect1);
        path.addRect(rect2);
        path.addRect(rect3);
        path.addRect(rect4);
        path.addRect(rect5);
        path.addRect(rect6);
        path.addRect(rect7);
        path.addRect(rect8);

        painter.fillPath(path, Qt::blue);

        // size info
        QString sizeInfo = QString::number(mCuter->geometry().width()) + QString::fromLocal8Bit("×") + QString::number(mCuter->geometry().height());

        painter.setPen(QPen(Qt::white, 2));
        painter.drawText(mCuter->pos() + QPoint(4, - 4), sizeInfo);
    }
    else
    {
        // mask
        QBrush br(QColor(0,0,0,125));
        painter.fillRect(this->rect(), br);
    }

    painter.restore();
}

bool CScreenShoot::eventFilter(QObject *obj, QEvent *e)
{
    bool res =  QWidget::eventFilter(obj, e);
    if(obj == mCuter)
    {
        switch (e->type()) {
        case QEvent::Move:
        case QEvent::Resize:
        {
            this->update();
        }
            break;
        default:
            break;
        }

    }

    return res;
}

void CScreenShoot::keyPressEvent(QKeyEvent *e)
{
    if(e->key() == Qt::Key_Escape)
    {
        emit mCuter->cancle();
    }

    QWidget::keyPressEvent(e);
}

void CScreenShoot::mousePressEvent(QMouseEvent *e)
{
    QWidget::mousePressEvent(e);
    if(!mCuter->isVisible())
    {
        mIsInitCuterSize = true;
        QRect resizeRect(e->pos(),e->pos());
        mCuter->setGeometry(resizeRect);
        mCuter->setVisible(true);
    }
}

void CScreenShoot::mouseReleaseEvent(QMouseEvent *e)
{
    QWidget::mouseReleaseEvent(e);
    mIsInitCuterSize = false;
}

void CScreenShoot::mouseMoveEvent(QMouseEvent *e)
{
    if(mIsInitCuterSize)
    {
        QRect resizeRect = mCuter->geometry();
        resizeRect.setBottomRight(e->pos());
        if(resizeRect.left() > resizeRect.right())
        {
            int t = resizeRect.left();
            resizeRect.setLeft(resizeRect.right());
            resizeRect.setRight(t);
        }
        if(resizeRect.top() > resizeRect.bottom())
        {
            int t = resizeRect.top();
            resizeRect.setTop(resizeRect.bottom());
            resizeRect.setBottom(t);
        }
        mCuter->setGeometry(resizeRect);
    }

    QWidget::mouseMoveEvent(e);
}

void CScreenShoot::cutPixmap()
{
    mCutPixmap = mPixmapBackground.copy(mCuter->geometry());
    mLoop.exit(1);
}

void CScreenShoot::cancel()
{
    mCutPixmap = QPixmap();
    mLoop.exit(0);
}

CScreenShoot *CScreenShoot::instance()
{
    if(gInstance == NULL)
    {
        gInstance = new CScreenShoot;
    }

    return gInstance;
}

bool CScreenShoot::cut()
{
    qDebug() << "cut...";
    QRect desktopRect = qApp->desktop()->geometry();
    mPixmapBackground = qApp->primaryScreen()->grabWindow(qApp->desktop()->winId(),
                                                          0,
                                                          0,
                                                          desktopRect.width(),
                                                          desktopRect.height());
    this->setGeometry(desktopRect);
    mCuter->hide();
    this->show();
    this->activateWindow();
    int res = mLoop.exec();
    this->close();

    if(res > 0)
        return true;
    else
        return false;
}

QPixmap CScreenShoot::pixmap()
{
    return mCutPixmap;
}

呼叫方式:

#include <QApplication>
#include <CScreenShoot.h>
#include <CCuter.h>
#include <QDebug>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    qDebug() << "done:" << CScreenShoot::instance()->cut();
    if(CScreenShoot::instance()->cut())
    {
        qDebug() << "done.";
        CScreenShoot::instance()->pixmap().save("./screen_shoot.bmp");
    }
    else
    {
        qDebug() << "cancel.";
    }

    return 0;

}

歡迎評論區交流。