当前位置: 首页 > news >正文

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状态

其他几个状态类似。

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

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

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

相关文章:

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

上一篇QT截图程序&#xff0c;可多屏幕截图只是实现了最基本的截图功能&#xff0c;虽然能用但是缺点也有&#xff0c;没办法更改选中的区域&#xff0c;这在实际使用时不太方便。这篇增加了这个功能。先看看效果。 实现代码为&#xff1a; 头文件 #ifndef MASKWIDGET_H #de…...

开源浪潮与闭源堡垒:大模型未来的双重奏

从数据隐私、商业应用和社区参与等方面来看&#xff0c;开源大模型和闭源大模型各有优劣势。开源模型在透明度、社区协作和成本效益方面具有优势&#xff0c;而闭源模型在安全性、合规性和商业竞争力方面表现出色。因此&#xff0c;我更倾向于认为&#xff0c;未来的大模型发展…...

postman教程-6-发送delete请求

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了postman发送put请求的方法&#xff0c;本小节我们讲解一下postman发送delete请求的方法。 HTTP DELETE 请求是一种用于删除指定资源的请求方法。在RESTful API 设计中&#xff0c;DELETE 请求…...

java小技能: 数字和字母组合的验证码图片(生成验证码字符并加上噪点,干扰线)

文章目录 引言I 验证码的作用1.1 验证使用计算机的是一个人,而非计算机程序。1.2 提供一个很短的时间窗的一次性密码。II 数字和字母组合的验证码图片2.1 获取验证码图片2.2 生成验证码字符并加上噪点,干扰线see also引言 世界上没有绝对的信息安全,但是有防范得好和坏的分…...

网络故障与排除

一、Router-ID冲突导致OSPF路由环路 路由器收到相同Router-ID的两台设备发送的LSA&#xff0c;所以查看路由表看到的OSPF缺省路由信息就会不断变动。而当C1的缺省路由从C2中学到&#xff0c;C2的缺省路由又从C1中学到时&#xff0c;就形成了路由环路&#xff0c;因此出现路由不…...

Cocos Creator 编辑器的数据绑定详解

Cocos Creator 是一款由 Cocos 平台推出的游戏开发工具&#xff0c;它集成了图形化编辑器、脚本引擎和资源管理器等功能&#xff0c;方便开发者快速地创建游戏。其中&#xff0c;数据绑定是 Cocos Creator 编辑器中非常重要的一个功能&#xff0c;它可以帮助开发者实现页面元素…...

解决Selenium NameError: name ‘By’ is not defined

解决Selenium NameError: name ‘By’ is not defined 文章目录 解决Selenium NameError: name By is not defined背景错误原因解决方法1. 检查导入语句2. 修正拼写和大小写3. 验证Selenium库安装4. 重启IDE或终端5. 检查环境变量 验证总结 背景 在使用Selenium进行Web自动化测…...

创建特定结构的二维数组:技巧与示例

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;二维数组的奇妙世界 二、方法一&#xff1a;直接初始化 1. 初始化一个…...

React Native 之 BackHandler (二十)

react-native 中的 BackHandler 是一个用于处理 Android 设备上的硬件返回按钮&#xff08;back button&#xff09;和 iOS 设备上的手势返回&#xff08;swipe back gesture&#xff09;的模块。在 React Native 应用中&#xff0c;当用户按下返回按钮或执行返回手势时&#x…...

一篇文章讲透排序算法之快速排序

前言 本篇博客难度较高&#xff0c;建议在学习过程中先阅读一遍思路、浏览一遍动图&#xff0c;之后研究代码&#xff0c;之后仔细体会思路、体会动图。之后再自己进行实现。 一.快排介绍与思想 快速排序相当于一个对冒泡排序的优化&#xff0c;其大体思路是先在文中选取一个…...

kubernetes-PV与PVC、存储卷

一、PV和PVC详解 当前&#xff0c;存储的方式和种类有很多&#xff0c;并且各种存储的参数也需要非常专业的技术人员才能够了解。在Kubernetes集群中&#xff0c;放了方便我们的使用和管理&#xff0c;Kubernetes提出了PV和PVC的概念&#xff0c;这样Kubernetes集群的管理人员就…...

643. 子数组最大平均数 I

给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你找出平均数最大且 长度为 k 的连续子数组&#xff0c;并输出该最大平均数。 任何误差小于 10-5 的答案都将被视为正确答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,12,-5,-6,50,3], k 4 输出&#xff…...

Node性能如何进行监控以及优化?

一、 是什么 Node作为一门服务端语言&#xff0c;性能方面尤为重要&#xff0c;其衡量指标一般有如下&#xff1a; CPU内存I/O网络 CPU 主要分成了两部分&#xff1a; CPU负载&#xff1a;在某个时间段内&#xff0c;占用以及等待CPU的进程总数CPU使用率&#xff1a;CPU时…...

ToList()和ToArray()的区别

以下是具体分析&#xff1a; 1. 返回类型 ToList()&#xff1a;返回一个泛型列表 List<T>&#xff0c;其中 T 是列表中元素的类型。 ToArray()&#xff1a;返回一个 Object 类型的数组。如果需要特定类型的数组&#xff0c;必须使用重载的 ToArray(T[] a) 方法&#x…...

11.RedHat认证-Linux文件系统(中)

11.RedHat认证-Linux文件系统(中) Linux的文件系统 格式化分区(1道题) #对于Linux分区来说&#xff0c;只有格式化之后才能使用&#xff0c;不格式化是无法使用的。 #Linux分区格式化之后就会变成文件系统&#xff0c;格式化的过程相当于对分区做了一个文件系统。 #Linux常见…...

windows系统电脑外插键盘驱动出现感叹号或者显示未知设备,键盘无法输入的解决办法

笔记本外插的键盘不能用&#xff0c;鼠标可以使用。 查找故障&#xff0c;结果打开设备管理器看到键盘那项里是一个的黄色惊叹号显示未知设备&#xff01;[图片]如下图所示 其实解决办法很简单&#xff0c;不要相信网上的一些博主说删除什么注册表&#xff0c;我开始跟着他们操…...

【开源项目】Excel数据表自动生成工具v1.0版

一、介绍 Excel数据表自动生成工具是Go语言编写的一款小型工具软件&#xff0c;用于将特定的Excel表格内容导出为多种编程语言的代码或可以直接读取的数据内容。 开源Github地址&#xff1a;https://github.com/SkyCreator/goproj 二、版本v1.0功能概览 1.编程语言支持 目前…...

Docker-一文详解容器通信的基础网络模式及衍生的自定义网络模式

启动容器时&#xff0c;通过-p 宿主机端口:容器端口&#xff0c;就可以通过访问宿主机端口访问到容器&#xff0c;这种原理机制是啥&#xff0c;有没有其它方式可以让宿主机和容器通信&#xff0c;以及容器与容器之间如何通信。带着这几个问题开始学习Docker的网络知识。 文章…...

Convolutional Occupancy Networks【ECCV】

论文&#xff1a;https://arxiv.org/pdf/2003.04618 代码&#xff1a;GitHub - autonomousvision/convolutional_occupancy_networks: [ECCV20] Convolutional Occupancy Networks 图 1&#xff1a;卷积占据网络。传统的隐式模型 (a) 由于其全连接网络结构&#xff0c;表现能力…...

Android Studio 问题集锦

报 Plugin [id: ‘com.android.application’, version: ‘8.1.3’, apply: false] was not found in any of the following sources: 场景&#xff1a;在一个Android 11的项目中添加一个Android 9的项目作为其Module&#xff0c;结果导致原项目无法正常运行&#xff0c;且原项…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...