截图相关介绍参考Qt实现截图之一
整个截图分为两层,一层为截图显示区,一层为裁剪选着层,裁剪选择层蒙在截图显示区之上。
本节介绍一种方案实现裁剪框。
裁剪层分为5个区域,上下左右和中间,分别代表5个QWidget, 中间区域即为需要裁剪的区域-高亮区域,没有截图时此区域不存在。
1.布局
top, left,right, bottom 是个窗体均设置半透明颜色,mid是全透明效果就是裁剪区域
void MaskFrameBase::setup()
{
this->setMouseTracking(true);
this->setObjectName("shtcenterframe");
this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QString shtcenterStyle = "QFrame#shtcenterframe{background:rgba(100, 100, 100 ,0);}";
this->setStyleSheet(shtcenterStyle);
this->topMask = new QFrame(this);
this->topMask->setObjectName("topmaskframe");
this->topMask->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QString topmaskStyle = QString("QFrame#topmaskframe{background:%1;}").arg(maskColor);
this->topMask->setStyleSheet(topmaskStyle);
this->topMask->setMouseTracking(true);
this->midLeftMask = new QFrame(this);
this->midLeftMask->setObjectName("leftmaskframe");
this->midLeftMask->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QString leftmaskStyle = QString("QFrame#leftmaskframe{background:%1;}").arg(maskColor);
this->midLeftMask->setStyleSheet(leftmaskStyle);
this->midLeftMask->setMouseTracking(true);
this->captureArea = new QFrame(this);
this->captureArea->setObjectName("captureareaframe");
this->captureArea->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QString captureStyle = QString("QFrame#captureareaframe{%1;background:rgba(100, 100, 100 ,2);}").arg(this->capturBorderStyle);
this->captureArea->setStyleSheet(captureStyle);
this->captureArea->setMouseTracking(true);
this->midRightMask = new QFrame(this);
this->midRightMask->setObjectName("rightmaskframe");
this->midRightMask->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QString rightMaskStyle = QString("QFrame#rightmaskframe{background:%1;}").arg(maskColor);
this->midRightMask->setStyleSheet(rightMaskStyle);
this->midRightMask->setMouseTracking(true);
this->bottomMask = new QFrame(this);
this->bottomMask->setObjectName("bottommaskframe");
this->bottomMask->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QString bottomMaskStyle = QString("QFrame#bottommaskframe{background:%1;}").arg(maskColor);
this->bottomMask->setStyleSheet(bottomMaskStyle);
this->bottomMask->setMouseTracking(true);
this->snapPopupView = new SnapPopupView(this);
snapPopupView->setup();
createBtnFrame();
createSizeIipFrame();
bSetupView = true;
adjustCaptureArea();
}
2.裁剪区域实现
鼠标按下并拖动时,实时计算5个窗体的大小和位置
void MaskFrameBase::adjustCaptureArea()
{
if(this->width()==0 || this->height()==0){
return;
}
int startx = this->startPointCapture.x();
int starty = this->startPointCapture.y();
int endx = this->endPointCapture.x();
int endy = this->endPointCapture.y();
//qDebug()<<"adjustCaptureArea:"<<startx<<" "<<starty<<" "<<endx<<" "<<endy<<" "<<endl;
//top
this->topMask->setGeometry(0,0,this->width(),starty);
if(!this->topMask->isVisible()){
this->topMask->show();
}
this->topMask->raise();
//mid-left
int captureHeight = endy-starty;
this->midLeftMask->setGeometry(0,starty,startx,captureHeight);
if(!this->midLeftMask->isVisible()){
this->midLeftMask->show();
}
this->midLeftMask->raise();
//mid-capture
int captureWidth = endx-startx;
this->captureArea->setGeometry(startx,starty,captureWidth,captureHeight);
if(!this->captureArea->isVisible()){
this->captureArea->show();
}
this->captureArea->raise();
//mid-right
int rightWidth = this->width()-endx;
this->midRightMask->setGeometry(endx,starty,rightWidth,captureHeight);
if(!this->midRightMask->isVisible()){
this->midRightMask->show();
}
this->midRightMask->raise();
//bottom
int bottomheigh = this->height()-endy;
this->bottomMask->setGeometry(0,endy,this->width(),bottomheigh);
if(!this->bottomMask->isVisible()){
this->bottomMask->show();
}
this->bottomMask->raise();
if(endx-startx>20 && endy-starty>20){
showSizeIipFrame();
}
}
3.边框大小改变
根据鼠标位置来计算位置,其实也就是条用步骤2中的接口,但是这里要做一下区域限制,不能操作屏幕之外。
//移动时,改变抓屏区坐标
void MaskFrameBase::updateCaptureRectMove()
{
int movex = this->curmousePoint.x()-this->pressPoint.x();
int movey = this->curmousePoint.y()-this->pressPoint.y();
//qDebug()<<"updateCaptureRectMove:"<<movex<<" "<<movey<<endl;
bool outbounds = false;
if(movex > 0){
//移动到右边界时,停止移动
if(this->endPointCaptureMark.x()+movex > this->width()){
//qDebug()<<"updateCaptureRectMove over right"<<endl;
movex = this->width()-this->endPointCaptureMark.x();
outbounds = true;
}
}
else{
//移动到左边界
if(this->startPointCaptureMark.x()+movex < 0){
//qDebug()<<"updateCaptureRectMove over left"<<endl;
movex = -this->startPointCaptureMark.x();
outbounds = true;
}
}
if(movey > 0){
//移动到下边界
if(this->endPointCaptureMark.y()+movey > this->height()){
//qDebug()<<"updateCaptureRectMove over bottom"<<endl;
movey = this->height()-this->endPointCaptureMark.y();
outbounds = true;
}
}
else{
//移动到上边界
if(this->startPointCaptureMark.y()+movey < 0){
//qDebug()<<"updateCaptureRectMove over top"<<endl;
movey = -this->startPointCaptureMark.y();
outbounds = true;
}
}
if(outbounds){
//固定一下鼠标
QPoint curcurpos(this->pressPoint.x()+movex,this->pressPoint.y()+movey);
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
}
//调整截屏区域坐标
this->startPointCapture.setX(this->startPointCaptureMark.x()+movex);
this->startPointCapture.setY(this->startPointCaptureMark.y()+movey);
this->endPointCapture.setX(this->endPointCaptureMark.x()+movex);
this->endPointCapture.setY(this->endPointCaptureMark.y()+movey);
}
4.裁剪区域边框绘制
一般我们的裁剪框区域也就是mid区域并不是一个简单的举行,产品设计可能会有一些个性化设计,我的项目中是4周会有一个小圆圈。
首先安装事件过虑器,这样父窗口就可以直接捕获该窗体的各种事件
this->captureArea->installEventFilter(this);
重载事件过滤器虚函数
virtual bool eventFilter(QObject *obj, QEvent *event) override;
截获paint事件绘制边框
bool MaskFrameBase::eventFilter(QObject *obj, QEvent *event)
{
if(obj == this->captureArea)
{
if(event->type() == QEvent::Paint)
{
QPainter painter(captureArea);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor(0xFC, 0xC9, 0x01), lineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); //设置画笔形式
//画矩形
calRect();
QPainterPath path;
path.addEllipse(leftRect.left(), leftRect.top(), leftRect.width(), leftRect.width());
path.addEllipse(topRect.left(), topRect.top(), topRect.width(), topRect.width());
path.addEllipse(rigtTopRect.left(), rigtTopRect.top(), rigtTopRect.width(), rigtTopRect.width());
path.addEllipse(rightRect.left(), rightRect.top(), rightRect.width(), rightRect.width());
path.addEllipse(rightBottomRect.left(), rightBottomRect.top(), rightBottomRect.width(), rightBottomRect.width());
path.addEllipse(bottomRect.left(), bottomRect.top(), bottomRect.width(), bottomRect.width());
path.addEllipse(leftBottomRect.left(), leftBottomRect.top(), leftBottomRect.width(), leftBottomRect.width());
path.addEllipse(leftTopRect.left(), leftTopRect.top(), leftTopRect.width(), leftTopRect.width());
painter.setBrush(Qt::white);
painter.drawPath(path);
//划线
painter.drawLine(QLine(QPoint(_rect_border_size.width() + edgeMargin, _rect_border_size.height() * 0.5 + edgeMargin), QPoint((this->captureArea->width() - _rect_border_size.width()) * 0.5 - 1, _rect_border_size.height() * 0.5 + edgeMargin)));
painter.drawLine(QLine(QPoint((this->captureArea->width() - _rect_border_size.width()) * 0.5 + _rect_border_size.width(), _rect_border_size.height() * 0.5 + edgeMargin), QPoint(this->captureArea->width() - _rect_border_size.width() - edgeMargin - 1, _rect_border_size.height() * 0.5 + edgeMargin)));
painter.drawLine(QLine(QPoint(this->captureArea->width() - _rect_border_size.width() * 0.5 - edgeMargin, _rect_border_size.height() + edgeMargin), QPoint(this->captureArea->width() - _rect_border_size.width() * 0.5 - edgeMargin, (this->captureArea->height() - _rect_border_size.height()) * 0.5 - 1)));
painter.drawLine(QLine(QPoint(this->captureArea->width() - _rect_border_size.width() * 0.5 - edgeMargin, (this->captureArea->height() - _rect_border_size.height()) * 0.5 + _rect_border_size.height()), QPoint(this->captureArea->width() - _rect_border_size.width() * 0.5 - edgeMargin, this->captureArea->height() - _rect_border_size.height() - edgeMargin - 1)));
painter.drawLine(QLine(QPoint((this->captureArea->width() - _rect_border_size.width()) * 0.5 + _rect_border_size.width(), this->captureArea->height() - _rect_border_size.height() * 0.5 - edgeMargin), QPoint(this->captureArea->width() - _rect_border_size.width() - edgeMargin - 1, this->captureArea->height() - _rect_border_size.height() * 0.5 - edgeMargin)));
painter.drawLine(QLine(QPoint(_rect_border_size.width() + edgeMargin, this->captureArea->height() - _rect_border_size.height() * 0.5 - edgeMargin), QPoint((this->captureArea->width() - _rect_border_size.width()) * 0.5 - 1, this->captureArea->height() - _rect_border_size.height() * 0.5 - edgeMargin)));
painter.drawLine(QLine(QPoint(_rect_border_size.width() * 0.5 + edgeMargin, _rect_border_size.height() + edgeMargin), QPoint(_rect_border_size.width() * 0.5 + edgeMargin, (this->captureArea->height() - _rect_border_size.height()) * 0.5 - 1)));
painter.drawLine(QLine(QPoint(_rect_border_size.width() * 0.5 + edgeMargin, (this->captureArea->height() - _rect_border_size.height()) * 0.5 + _rect_border_size.height()), QPoint(_rect_border_size.width() * 0.5 + edgeMargin, this->captureArea->height() - _rect_border_size.height() - edgeMargin - 1)));
}
if(event->type() == QEvent::Resize || event->type() == QEvent::Move )
{
repaint();
}
}
return QFrame::eventFilter(obj, event);
}
5.使用
if(this->snapShootView == Q_NULLPTR){
this->snapShootView = new TakeSnapshootView(this);
}
if(this->snapShootView != Q_NULLPTR){
this->snapShootView->takeSnapImage();
}
最终效果图:
源码:
#ifndef SNAP_MASK_FRAME_H
#define SNAP_MASK_FRAME_H
#include "maskframebase.h"
/**
* @brief The SnapMaskFrame class 抓屏过程中的遮罩层
*/
class SnapMaskFrame : public MaskFrameBase {
Q_OBJECT
public:
explicit SnapMaskFrame(QWidget* parent = 0);
virtual ~SnapMaskFrame();
public:
signals:
void selectedSnap(QPoint start,QPoint end);
void savepicture(QPoint start,QPoint end);
protected:
virtual void createBtnFrame();
virtual bool judgeOperateState(QPoint curpos);//鼠标未按下左键,移动过程中使用,会改变鼠标指针样式
virtual bool updateCaptureRect();//更新截屏区域的坐标,更新了返回true
virtual void cancelSelect();
protected slots:
void slot_delayShowSmallClassChat();
private:
void updateCaptureRectMove();//移动时,改变抓屏区坐标
void updateCaptureRectBorderScale();
void updateCaptureRectCornerScale();
void report(const QString opt);
void showSmallClassChat();
private slots:
void cancelsnap();
void sendsnap();
void savesnap();
};
#endif // SNAP_MASK_FRAME_H
#include "snapmaskframe.h"
#include "view/uisettings.h"
#include "utils/utility.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QToolButton>
#include <QApplication>
#include <QDesktopWidget>
#include <QDebug>
#include <QTimer>
#include "report/reportutility.h"
#include "notificationcenter.h"
#define SNAP_BTN_SAVE "snapsavebtn"
#define SNAP_BTN_CANCEL "snapcancelbtn"
#define SNAP_BTN_SEND "snapsendbtn"
SnapMaskFrame::SnapMaskFrame(QWidget* parent):MaskFrameBase(parent)
{
}
SnapMaskFrame::~SnapMaskFrame()
{
}
void SnapMaskFrame::createBtnFrame()//按钮
{
this->btnFrameHeigh = 36*mScale;
this->btnFrameWidth = this->btnFrameHeigh*3;
this->btnFrame = new QFrame(this);
if(!this->btnFrame){
return;
}
this->btnFrame->setObjectName("tstbtnframe");
this->btnFrame->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
this->btnFrame->setStyleSheet("QFrame#tstbtnframe{border:none;background:#5fa2ee;}");
this->btnFrame->setFixedWidth(this->btnFrameWidth);
this->btnFrame->setFixedHeight(this->btnFrameHeigh);
this->btnFrame->setMouseTracking(true);
QHBoxLayout* hlayout = new QHBoxLayout(this->btnFrame);
if(!hlayout){
return;
}
hlayout->setMargin(1);
hlayout->setSpacing(1);
QToolButton* btnsave = createActionButton(SNAP_BTN_SAVE,"");
if(btnsave){
connect(btnsave, SIGNAL(clicked(bool)), this, SLOT(savesnap()));
hlayout->addWidget(btnsave,0,Qt::AlignVCenter);
btnsave->setMouseTracking(true);
}
QLabel* lineVec = new QLabel(this->btnFrame);
lineVec->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
lineVec->setFixedWidth(1*this->mScale);
lineVec->setFixedHeight(this->btnHeigh/2);
lineVec->setStyleSheet("background:white;");
hlayout->addWidget(lineVec,0,Qt::AlignVCenter);
QToolButton* btnCancel = createActionButton(SNAP_BTN_CANCEL,"");
if(btnCancel){
connect(btnCancel, SIGNAL(clicked(bool)), this, SLOT(cancelsnap()));
hlayout->addWidget(btnCancel);
hlayout->addWidget(btnCancel,0,Qt::AlignVCenter);
btnCancel->setMouseTracking(true);
}
QLabel* lineVec01 = new QLabel(this->btnFrame);
lineVec01->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
lineVec01->setFixedWidth(1*this->mScale);
lineVec01->setFixedHeight(this->btnHeigh/2);
lineVec01->setStyleSheet("background:white;");
hlayout->addWidget(lineVec01,0,Qt::AlignVCenter);
QToolButton* btnSend = createActionButton(SNAP_BTN_SEND,"");
if(btnSend){
connect(btnSend, SIGNAL(clicked(bool)), this, SLOT(sendsnap()));
hlayout->addWidget(btnSend,0,Qt::AlignVCenter);
btnSend->setMouseTracking(true);
}
this->btnFrame->setLayout(hlayout);
this->btnFrame->hide();
}
//鼠标未按下左键,移动过程中使用,会改变鼠标指针样式
bool SnapMaskFrame::judgeOperateState(QPoint curpos)
{
bool match = MaskFrameBase::judgeOperateState(curpos);
if(match){
//qDebug()<<"judgeOperateState:base match"<<endl;
return true;
}
//在四个边角
if(judgeUponCorner(curpos)){
//qDebug()<<"judgeOperateState onCorner"<<endl;
return true;
}
this->operateMode = SelectArea;
this->setCursor(Qt::CrossCursor);
return false;
}
bool SnapMaskFrame::updateCaptureRect()
{
//基类处理一下移动
bool match = MaskFrameBase::updateCaptureRect();
if(match){
return true;
}
if(this->operateMode == SelectArea)
{
//限一下幅
if(qAbs(this->curmousePoint.x()-this->pressPoint.x())>this->captureBorderWidth*2 &&
qAbs(this->curmousePoint.y()-pressPoint.y())>this->captureBorderWidth*2){
generateResultPos(this->pressPoint,this->curmousePoint);
return true;
}
}
else if(this->operateMode == BorderScale)
{
if(captureAreaValide()){
updateCaptureRectBorderScale();
return true;
}
}
else if(this->operateMode == CornerScale)
{
updateCaptureRectCornerScale();
return true;
}
return false;
}
void SnapMaskFrame::cancelSelect()
{
emit selectedSnap(QPoint(0,0),QPoint(0,0));
showSmallClassChat();
}
void SnapMaskFrame::slot_delayShowSmallClassChat()
{
emit NotificationCenter::instance()->signal_showSmallClassChatView(true);
}
void SnapMaskFrame::updateCaptureRectBorderScale()
{
int minwidth = this->captureBorderWidth*2+1;
int padding = 0;//可以设定截屏区域和全屏区域之间的padding
int movex = this->curmousePoint.x()-this->pressPoint.x();
int movey = this->curmousePoint.y()-this->pressPoint.y();
int markstartx = this->startPointCaptureMark.x();
int markstarty = this->startPointCaptureMark.y();
int markendx = this->endPointCaptureMark.x();
int markendy = this->endPointCaptureMark.y();
if(this->borderSide == LeftBorder){
if(movex > 0){
//缩小
if(markstartx+movex > markendx-minwidth){
//宽度小于minwidth时,不再允许拖放
movex = markendx-minwidth-markstartx;
QPoint curcurpos(this->pressPoint.x()+movex,this->curmousePoint.y());
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale leftsacle too small"<<endl;
}
}
else{
//左边界
if(markstartx+movex < padding){
movex = padding-markstartx;
QPoint curcurpos(this->pressPoint.x()+movex,this->curmousePoint.y());
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale leftsacle to end"<<endl;
}
}
this->startPointCapture.setX(markstartx+movex);
}
else if(this->borderSide == RightBorder){
if(movex < 0){
if(markendx+movex < markstartx+minwidth){
movex = markstartx+minwidth-markendx;
QPoint curcurpos(this->pressPoint.x()+movex,this->curmousePoint.y());
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale rightsacle too small"<<endl;
}
}
else{
if((markendx+movex) > (this->width()-padding)){
movex = this->width()-padding-markendx;
QPoint curcurpos(this->pressPoint.x()+movex,this->curmousePoint.y());
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale rightsacle to end"<<endl;
}
}
this->endPointCapture.setX(markendx+movex);
}
else if(this->borderSide == TopBorder){
if(movey > 0){
if(markstarty+movey > markendy-minwidth){
movey = markendy-minwidth-markstarty;
QPoint curcurpos(this->curmousePoint.x(),this->pressPoint.y()+movey);
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale topsacle too small"<<endl;
}
}
else{
if(markstarty+movey < padding){
movey = padding-markstarty;
QPoint curcurpos(this->curmousePoint.x(),this->pressPoint.y()+movey);
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale topsacle to end"<<endl;
}
}
this->startPointCapture.setY(markstarty+movey);
}
else if(this->borderSide == BottomBorder){
if(movey < 0){
if(markendy+movey < markstarty+minwidth){
movey = markstarty+minwidth-markendy;
QPoint curcurpos(this->curmousePoint.x(),this->pressPoint.y()+movey);
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale bottomsacle too small"<<endl;
}
}
else{
if(markendy+movey > this->height()-padding){
movey = this->height()-padding-markendy;
QPoint curcurpos(this->curmousePoint.x(),this->pressPoint.y()+movey);
QApplication::desktop()->cursor().setPos(this->mapToGlobal(curcurpos));
//qDebug()<<"updateCaptureRectBorderScale bottomsacle to end"<<endl;
}
}
this->endPointCapture.setY(markendy+movey);
}
}
void SnapMaskFrame::updateCaptureRectCornerScale()
{
QPoint startTmp(this->startPointCaptureMark.x(),this->startPointCaptureMark.y());
QPoint endTmp(this->curmousePoint.x(),this->curmousePoint.y());
if(this->cornerSide == LeftTop){
startTmp = this->endPointCaptureMark;
}
else if(this->cornerSide == LeftBottom){
startTmp.setX(this->endPointCaptureMark.x());
startTmp.setY(this->startPointCaptureMark.y());
}
else if(this->cornerSide == RightTop){
startTmp.setX(this->startPointCaptureMark.x());
startTmp.setY(this->endPointCaptureMark.y());
}
else if(this->cornerSide == RightBottom){
startTmp = this->startPointCaptureMark;
}
generateResultPos(startTmp,endTmp);
}
void SnapMaskFrame::report(const QString opt)
{
showSmallClassChat();
QMap<QString,QString> extraParams;
extraParams["btn_name"] = opt;
ReportUserOptWithParas(ReportModule::COMMON,
ReportOperateCode::SNAPSHOT_FRAME_BTN_CLICK,
"printscreen",
extraParams,
"snapshot_frame_btn_click");
}
void SnapMaskFrame::showSmallClassChat()
{
QTimer::singleShot(50,this,SLOT(slot_delayShowSmallClassChat()));
}
void SnapMaskFrame::cancelsnap()
{
showSmallClassChat();
emit selectedSnap(QPoint(0,0),QPoint(0,0));
}
void SnapMaskFrame::sendsnap()
{
emit selectedSnap(this->startPointCapture,this->endPointCapture);
report("send_pic");
}
void SnapMaskFrame::savesnap()
{
emit savepicture(this->startPointCapture,this->endPointCapture);
report("save_pic");
}