[注]:本程序在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;
}
欢迎评论区交流。