上一篇QT截图程序,可多屏幕截图只是实现了最基本的截图功能,虽然能用但是缺点也有,没办法更改选中的区域,这在实际使用时不太方便。这篇增加了这个功能。先看看效果。
实现代码为:
头文件
#ifndef MASKWIDGET_H
#define MASKWIDGET_H
#include <QWidget>
#include "mainwindow.h"
namespace Ui {
class MaskWidget;
}
enum SnapState{
NoSnap,
Snapped,
PreLeftDrag,
LeftDrag,
PreRightDrag,
RightDrag,
PreTopDrag,
TopDrag,
PreBottomDrag,
BottomDrag
};
class MaskWidget : public QWidget
{
Q_OBJECT
public:
explicit MaskWidget(QWidget *parent = nullptr);
~MaskWidget();
protected:
void mousePressEvent(QMouseEvent *event)override;
void mouseReleaseEvent(QMouseEvent *event)override;
void mouseMoveEvent(QMouseEvent *event)override;
void paintEvent(QPaintEvent *event)override;
void keyPressEvent(QKeyEvent *event) override;
void showEvent(QShowEvent *event) override;
private slots:
void ResetSnap();
private:
QPoint m_pressPos;
QPoint m_newPos;
QRect m_maskRect{0, 0, 0, 0};
QPixmap m_image;
bool isPressed{false};
MainWindow m;
SnapState snapstate{NoSnap};
private:
Ui::MaskWidget *ui;
};
#endif // MASKWIDGET_H
源文件
#include "maskwidget.h"
#include "ui_maskwidget.h"
#include <QMouseEvent>
#include <QRegion>
#include <QScreen>
#include <QPainter>
#include <QGuiApplication>
#include <QPixmap>
#include <QDebug>
#include <QtMath>
#include <QCursor>
const int MINSIZE = 10;
MaskWidget::MaskWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MaskWidget)
{
ui->setupUi(this);
setMouseTracking(true);
setWindowFlags(Qt::FramelessWindowHint);
setWindowOpacity(0.8);
QList<QScreen*> screens = QGuiApplication::screens();
int width = 0;
int height = 0;
for (QScreen *screen : screens)
{
width += screen->geometry().width();
if (height < screen->geometry().height())
{
height = screen->geometry().height();
}
qDebug()<<screen->geometry();
}
this->setFixedSize(width, height);
m.hide();
connect(&m, SIGNAL(resetSnap()), this, SLOT(ResetSnap()));
}
MaskWidget::~MaskWidget()
{
delete ui;
}
void MaskWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if (m_maskRect.width() == 0 && m_maskRect.width() == 0)
{
m_pressPos = event->pos();
this->setCursor(Qt::CrossCursor);
isPressed = true;
update();
}
if (snapstate == PreLeftDrag)
{
snapstate = LeftDrag;
}
else if (snapstate == PreRightDrag)
{
snapstate = RightDrag;
}
else if (snapstate == PreTopDrag)
{
snapstate = TopDrag;
}
else if (snapstate == PreBottomDrag)
{
snapstate = BottomDrag;
}
}
if (event->button() == Qt::RightButton)
{
if (m_maskRect.width() > 0)
{
isPressed = false;
QRegion all(0, 0, width(), height());
QRegion sub(m_maskRect);
setMask(all.subtracted(sub));
QPixmap combined(this->width(), this->height());
combined.fill(Qt::transparent);
QPainter painter(&combined);
QList<QScreen*> screens = QGuiApplication::screens();
for (QScreen *screen : screens)
{
m_image = screen->grabWindow(0);
painter.drawPixmap(screen->geometry().x(), 0, screen->geometry().width(), screen->geometry().height(), m_image);
}
// auto gpos = mapToGlobal(event->pos());
// auto gposStart = mapToGlobal(m_pressPos);
// qDebug()<<gpos<<gposStart;
//m_image = combined.copy(qMin(gpos.x(), gposStart.x()), qMin(gpos.y(), gposStart.y()),
// qFabs(gpos.x() - gposStart.x()), qFabs(gpos.y() - gposStart.y()));
m_image = combined.copy(m_maskRect);
this->hide();
m.SetImage(m_image);
update();
m.show();
}
}
QWidget::mousePressEvent(event);
}
void MaskWidget::mouseReleaseEvent(QMouseEvent *event)
{
qDebug()<<__func__<<" "<<snapstate;
if (isPressed)
{
isPressed = false;
snapstate = Snapped;
this->setCursor(Qt::ArrowCursor);
}
switch(snapstate)
{
case LeftDrag:
{
snapstate = Snapped;
this->setCursor(Qt::ArrowCursor);
}
break;
case RightDrag:
{
snapstate = Snapped;
this->setCursor(Qt::ArrowCursor);
}
break;
case TopDrag:
{
snapstate = Snapped;
this->setCursor(Qt::ArrowCursor);
}
break;
case BottomDrag:
{
snapstate = Snapped;
this->setCursor(Qt::ArrowCursor);
}
break;
default:
break;
}
return QWidget::mouseReleaseEvent(event);
}
void MaskWidget::mouseMoveEvent(QMouseEvent* event)
{
if (isPressed)
{
m_newPos = event->pos();
QRegion all(0, 0, width(), height());
m_maskRect = QRect(qMin(m_pressPos.x(), m_newPos.x()),
qMin(m_pressPos.y(), m_newPos.y()),
qAbs(m_newPos.x() - m_pressPos.x()),
qAbs(m_newPos.y() - m_pressPos.y()));
QRegion sub(m_maskRect);
setMask(all.subtracted(sub));
update();
}
else
{
switch(snapstate)
{
case Snapped:
{
if (m_maskRect.bottom() > event->pos().y() && m_maskRect.top() < event->pos().y())
{
if (qFabs(m_maskRect.left() - event->pos().x()) < 5)
{
this->setCursor(Qt::SizeHorCursor);
this->snapstate = PreLeftDrag;
}
else if (qFabs(m_maskRect.right() - event->pos().x()) < 5)
{
this->setCursor(Qt::SizeHorCursor);
this->snapstate = PreRightDrag;
}
}
else if (m_maskRect.left() < event->pos().x() && m_maskRect.right() > event->pos().y())
{
if (qFabs(m_maskRect.top() - event->pos().y()) < 5)
{
this->setCursor(Qt::SizeVerCursor);
this->snapstate = PreTopDrag;
}
else if (qFabs(m_maskRect.bottom() - event->pos().y()) < 5)
{
this->setCursor(Qt::SizeVerCursor);
this->snapstate = PreBottomDrag;
}
}
}
break;
case LeftDrag:
{
if (event->pos().x() + MINSIZE >= m_maskRect.right())
{
m_maskRect.setLeft(m_maskRect.right() - MINSIZE);
}
else
{
m_maskRect.setLeft(event->pos().x() + 2);
}
QRegion sub(m_maskRect);
QRegion all(0, 0, width(), height());
setMask(all.subtracted(sub));
qDebug()<<m_maskRect;
update();
}
break;
case PreLeftDrag:
{
if (qFabs(m_maskRect.left() - event->pos().x()) >= 5 || event->pos().y() > m_maskRect.bottom() || event->pos().y() < m_maskRect.top())
{
this->setCursor(Qt::ArrowCursor);
this->snapstate = Snapped;
}
}
break;
case PreRightDrag:
{
if (qFabs(event->pos().x() - m_maskRect.right()) >= 5 || event->pos().y() > m_maskRect.bottom() || event->pos().y() < m_maskRect.top())
{
this->setCursor(Qt::ArrowCursor);
this->snapstate = Snapped;
}
}
break;
case RightDrag:
{
if (event->pos().x() - MINSIZE <= m_maskRect.left())
{
m_maskRect.setRight(m_maskRect.left() + MINSIZE);
}
else
{
m_maskRect.setRight(event->pos().x() - 2);
}
QRegion sub(m_maskRect);
QRegion all(0, 0, width(), height());
setMask(all.subtracted(sub));
qDebug()<<m_maskRect;
update();
}
break;
case PreTopDrag:
{
if (qFabs(event->pos().y() - m_maskRect.top()) >= 5 || event->pos().x() < m_maskRect.left() || event->pos().x() > m_maskRect.right())
{
this->setCursor(Qt::ArrowCursor);
this->snapstate = Snapped;
}
}
break;
case TopDrag:
{
if (event->pos().y() + MINSIZE >= m_maskRect.bottom())
{
m_maskRect.setTop(m_maskRect.bottom() - MINSIZE);
}
else
{
m_maskRect.setTop(event->pos().y() + 2);
}
QRegion sub(m_maskRect);
QRegion all(0, 0, width(), height());
setMask(all.subtracted(sub));
update();
}
break;
case PreBottomDrag:
{
if (qFabs(event->pos().y() - m_maskRect.bottom()) >= 5 || event->pos().x() < m_maskRect.left() || event->pos().x() > m_maskRect.right())
{
this->setCursor(Qt::ArrowCursor);
this->snapstate = Snapped;
}
}
break;
case BottomDrag:
{
if (event->pos().y() - MINSIZE <= m_maskRect.top())
{
m_maskRect.setBottom(m_maskRect.top() + MINSIZE);
}
else
{
m_maskRect.setBottom(event->pos().y() - 2);
}
QRegion sub(m_maskRect);
QRegion all(0, 0, width(), height());
setMask(all.subtracted(sub));
update();
}
break;
default:
break;
}
}
return QWidget::mouseMoveEvent(event);
}
void MaskWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::red);
painter.drawRect(m_maskRect.x()-1, m_maskRect.y()-1, m_maskRect.width()+1, m_maskRect.height() + 1);
}
void MaskWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape)
{
close();
}
else if (event->key() == Qt::Key_Enter)
{
if (isPressed)
{
isPressed = false;
snapstate = Snapped;
this->setCursor(Qt::ArrowCursor);
}
}
QWidget::keyPressEvent(event);
}
void MaskWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
}
void MaskWidget::ResetSnap()
{
QRegion all(0, 0, width(), height());
setMask(all);
m_maskRect.setRect(0,0,0,0);
snapstate = NoSnap;
this->show();
}
思路:
第一,要保留住选中的框,选中后停留下来不自动跳转。实现方式为在mouseRelease函数里面不再进行隐藏和跳转,跳转改称点击鼠标右键。
第二,当鼠标移动到边框附近时,鼠标的形状要进行变化,表示可以拖动了。左右边对应的是双向横箭头,上下边对应的是双向竖箭头。这里取值距离5作为触发区域,当距离小于5时可进行拖动。类似途中的红色区域。
第三,为了配合形状变化,用一个枚举来表示不同的状态。
enum SnapState{
NoSnap,
Snapped,
PreLeftDrag,
LeftDrag,
PreRightDrag,
RightDrag,
PreTopDrag,
TopDrag,
PreBottomDrag,
BottomDrag
};
NoSnap表示初始状态,没有开始截图的时候。
Snapped表示已经截图了,此时会显示一个矩形方框。
PreLeftDrag表示进入左侧边框可拖动状态,此时鼠标形状变化成左右箭头。
PreLeftDrag----按下鼠标左键--->LeftDrag(可移动鼠标来拖动边框)
PreLeftDrag----鼠标距离左边框的距离大于5--->Snapped(鼠标状态恢复正常)
LeftDrag表示进入可拖动状态,可拖动鼠标更改左边框位置。此时松开鼠标则回到PreLeftDrag状态
其他几个状态类似。
根据鼠标的位置和动作,变化不同的状态。这里由于逻辑简单,没有使用状态机。
为了有更好的显示效果,这里限制了拖动区域,不会出现一条边覆盖另一条的情况,可在动图里看出来。
这样修改后,截图工具好用了很多。