QT截图程序,可多屏幕截图二,增加调整截图区域功能

上一篇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_OBJECTpublic: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状态

其他几个状态类似。

根据鼠标的位置和动作,变化不同的状态。这里由于逻辑简单,没有使用状态机。

为了有更好的显示效果,这里限制了拖动区域,不会出现一条边覆盖另一条的情况,可在动图里看出来。

这样修改后,截图工具好用了很多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/18949.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab|基于PMU相量测量单元进行电力系统电压幅值和相角状态估计

主要内容 程序采用三种方法对14节点和30节点电力系统状态进行评估&#xff1a; ①PMU同步相量测量单元结合加权最小二乘法&#xff08;WLS&#xff09;分析电力系统的电压幅值和相角状态&#xff1b; ②并采用牛顿-拉夫逊方法进行系统潮流计算&#xff0c;结果作为理论分…

LLAMA3==shenzhi-wang/Llama3-8B-Chinese-Chat。windows安装不使用ollama

创建环境&#xff1a; conda create -n llama3_env python3.10 conda activate llama3_env conda install pytorch torchvision torchaudio cudatoolkit11.7 -c pytorch 安装Hugging Face的Transformers库&#xff1a; pip install transformers sentencepiece 下载模型 ht…

开源一个工厂常用的LIMS系统

Senaite是一款强大且可靠的基于Web的LIMS/LIS系统&#xff0c;采用Python编写&#xff0c;构建在Plone CMS基础架构之上。该系统处于积极开发阶段&#xff0c;在灵活的定制空间中为开发人员提供了丰富的功能。其中&#xff0c;Senaite在处理REST的JSON API上做得出色&#xff0…

python -【二】判断语句

判断语句 一、 布尔类型 True&#xff1a;真(1)False&#xff1a;假(0) 比较运算符 运算符实例1 1 True!1 ! 1 Flase<1 < 1 Flse>1 > 1 Flse<1 < 1 True>1 > 1 True b1 True b2 False print(f"b1值是{b1},类型是{type(b1)}") print(…

【Linux】Socket中的心跳机制(心跳包)

Socket中的心跳机制(心跳包) 1. 什么是心跳机制&#xff1f;(心跳包) 在客户端和服务端长时间没有相互发送数据的情况下&#xff0c;我们需要一种机制来判断连接是否依然存在。直接发送任何数据包可以实现这一点&#xff0c;但为了效率和简洁&#xff0c;通常发送一个空包&am…

npm入门介绍

npm&#xff08;Node Package Manager&#xff09;是 Node.js 的包管理工具&#xff0c;用于安装、管理和共享 JavaScript 代码包。它是全球最大的软件注册表&#xff0c;拥有数百万个开源包&#xff0c;为开发者提供了丰富的资源和工具。以下是 npm 的详细介绍&#xff1a; 1…

vs工程添加自定义宏

一、简介 用户可以添加自定义宏变量方便工程路径名称的修改和配置 例&#xff1a;$(SolutionDir) 为解决方案路径&#xff0c;$(PojectDir) 为工程所在路径 测试环境&#xff1a;vs2017&#xff0c;qt5.14.0 二、配置 1、打开属性窗口&#xff1a;视图-》其他窗口-》属性管…

四数之和-力扣

本题在三数之和的基础上&#xff0c;再增加一重循环进行解答 首先注意的点是&#xff0c;一级剪枝处理&#xff0c;target > 0 && nums[i] > target 此处只有整数才可剪枝处理&#xff0c;如果target为负数&#xff0c;nums[i] < target&#xff0c;也不能代…

精准操控时间的艺术:JavaScript节流函数的深度探索与实践【含代码示例】

精准操控时间的艺术&#xff1a;JavaScript节流函数的深度探索与实践【含代码示例】 节流基础&#xff1a;概念与作用实现策略&#xff1a;案例展示案例一&#xff1a;基础定时器实现案例二&#xff1a;立即执行版本案例三&#xff1a;使用requestAnimationFrame实现动画节流 功…

蓝桥杯-AB路线(详细原创)

问题描述&#xff1a; 有一个由 N M 个方格组成的迷宫&#xff0c;每个方格写有一个字母 A 或者 B。小蓝站在迷宫左上角的方格&#xff0c;目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。 由于特殊的原因&#xff0c;小蓝的路线必须先走 K 个 A 格子、再…

前端面试题日常练-day40 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末 1. Bootstrap 的栅格系统是基于&#xff08; &#xff09;进行布局的。A. 像素 B. 百分比 C. 媒体查询 2. 在 Bootstrap 中&#xff0c;要创建一个按钮&#xff0c;可以使用&#xff08; &#xff…

【CSharp】判断目录以及文件是否存在

【CSharp】判断目录以及文件是否存在 1.背景2.判断目录3.判断文件1.背景 我们在进行磁盘IO的时候进行需要判断目录、文件是否存在,根据判断结果再做进一步的操作。 其中判断目录是否存在,涉及Directory.Exists(String) 方法; 命名空间:System.IO 方法功能:确定给定路径是…

Spring OAuth2:开发者的安全盾牌!(下)

上文我们教了大家如何像海盗一样寻找宝藏&#xff0c;一步步解锁令牌的奥秘&#xff0c;今天将把更加核心的技巧带给大家一起学习&#xff0c;共同进步&#xff01; 文章目录 6. 客户端凭证与密码模式6.1 客户端凭证模式应用适用于后端服务间通信 6.2 密码模式考量直接传递用户…

(精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标

&#xff08;精确度&#xff0c;召回率&#xff0c;真阳性&#xff0c;假阳性&#xff09;ACC、敏感性、特异性等 ROC指标

【微机原理及接口技术】可编程计数器/定时器8253

【微机原理及接口技术】可编程计数器/定时器8253 文章目录 【微机原理及接口技术】可编程计数器/定时器8253前言一、8253的内部结构和引脚二、8253的工作方式三、8253的编程总结 前言 本篇文章就8253芯片展开&#xff0c;详细介绍8253的内部结构和引脚&#xff0c;8253的工作方…

E: Package ‘curl‘ has no installation candidate

错误1&#xff1a; E: Package curl has no installation candidate Package curl is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another sourceE: Package curl …

人工智能初识

&#x1f31e;欢迎来到人工智能基础的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年5月1…

C/C++语法|pthread线程库的使用

笔记主要内容来自 爱编程的大柄–线程 爱编程的大柄–线程同步 在进入代码实践之前&#xff0c;我们应该搞清楚。 线程是成语的最小执行单位&#xff0c;进程是操作系统中最小的资源分配单位。 这样的话我们可以理解以下两点&#xff1a; 同一地址空间中的多个线程独有的是&…

618手把手教你捡漏服务器

618最全捡漏攻略 捡漏规则1、新人优惠⭐⭐⭐2、教育优惠⭐⭐3、回馈活动⭐️ ECS价格对比新人优惠&#x1f49d;京东云 50/年百度云 60.69/年阿里云 82/年腾讯云 99/年 回馈活动&#x1f381;阿里云 教育优惠&#x1f3eb;阿里云腾讯云 hi&#xff0c;好久不见各位&#xff0c;…

16 -java反射

目录 第16章 反射 16.1 反射的概念 16.2反射的作用 16.3 java.lang.Class类 16.3.1 哪些类型可以获取Class对象 16.3.2 获取Class对象的四种方式 16.4 反射的基本应用 16.4.1 获取类型的详细信息 16.4.2 创建任意引用类型的对象 16.4.3 操作任意类型的属性 16.4.4 调…