QT截图程序三-截取自定义多边形
上一篇文章QT截图程序,可多屏幕截图二,增加调整截图区域功能-CSDN博客描述了如何截取,具备调整边缘功能后已经方便使用了,但是与系统自带的程序相比,似乎没有什么特别,只能截取矩形区域。
如果可以按照自己定义截取多边形,那样功能会强大许多。下面是程序主要功能截取任意边的多边形,不规则形状截取的好帮手。先看看效果。这是截取到的图像
具体步骤:
操作方法,使用左键点击选择点,右键点击时,最后一个点和第一个点连接完成截图。按下空格键跳转到保存界面。
思路:
1. 记录点击的每个点,然后把它们当成多边形绘制在屏幕上
QPainter painter(this);painter.setPen(Qt::red);if (m_points.size() == 1){painter.drawLine(m_points.at(0), QCursor::pos()); //只有一个点,线随着鼠标活动}else if (m_points.size() > 1){int i=1;for (i=1; i<m_points.size(); ++i){painter.drawLine(m_points.at(i-1), m_points.at(i));}if (snapstate == Snapped){painter.drawLine(m_points.at(i-1), m_points.at(0)); //最后一个点和初始点连接}else{painter.drawLine(m_points.at(i-1), QCursor::pos()); //最后一个点,线随着鼠标活动}}
2. 完成时将所选区域透明化来做区分。
QRegion all(0, 0, width(), height());
QPolygon tempMask(this->m_points.toVector());QRegion sub(tempMask);
setMask(all.subtracted(sub));
m_maskRect = tempMask.boundingRect();
3. 取所选多边形的边框矩形,然后将它从大图中分离出来,
QImage img = combined.copy(m_maskRect).toImage(); //根据选中区域的边框剪裁图像
img.convertTo(QImage::Format_RGBA8888);
4. 平移多边形,使它位于剪裁后的区域
QPolygon tempMask(this->m_points.toVector());tempMask.translate(-m_maskRect.x(), -m_maskRect.y()); //平移多边形,使它位于剪裁后的区域
5. 根据多边形所在区域,判断矩形上的点是否在多边形内,通过图像alpha值来设置点的可见度
QPainterPath path;path.addPolygon(tempMask);
for (int i=0; i<img.width(); ++i)
{for (int j=0; j<img.height(); ++j){QColor col = img.pixelColor(i, j);QPainterPath pathPoint = QPainterPath(QPointF(i,j));if(path.contains(pathPoint))//判断位置i,j是否在多边形内{col.setAlpha(255);}else{col.setAlpha(0);}img.setPixelColor(i,j,col);}
}
平移和提取的示意图,第一步,类似在窗口里选中了一个多边形,红色矩形是多边形的boundingRect:
第二部,截取矩形边框,类似将矩形边框平移到左上角
第三步,第二部的操作导致矩形和多边形不重叠,此时要移动多边形,然后将只留它们。
代码:
源文件:
#include "maskwidget.h"
#include "ui_maskwidget.h"
#include <QMouseEvent>
#include <QRegion>
#include <QScreen>
#include <QPainter>
#include <QGuiApplication>
#include <QPixmap>
#include <QCursor>
#include <QList>
#include <QPolygon>
#include <QPainterPath>
#include <QDebug>//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();}}this->setFixedSize(width, height);m.hide();connect(&m, SIGNAL(resetSnap()), this, SLOT(ResetSnap()));
}MaskWidget::~MaskWidget()
{delete ui;
}void MaskWidget::mousePressEvent(QMouseEvent *event)
{qDebug()<< __func__<<event->pos();if (event->button() == Qt::LeftButton){if (snapstate == NoSnap){m_points.push_back(event->pos());}}if (event->button() == Qt::RightButton){snapstate = Snapped;QRegion all(0, 0, width(), height());QPolygon tempMask(this->m_points.toVector());QRegion sub(tempMask);setMask(all.subtracted(sub));m_maskRect = tempMask.boundingRect();}update();QWidget::mousePressEvent(event);
}void MaskWidget::mouseReleaseEvent(QMouseEvent *event)
{update();return QWidget::mouseReleaseEvent(event);
}void MaskWidget::mouseMoveEvent(QMouseEvent* event)
{update();return QWidget::mouseMoveEvent(event);
}void MaskWidget::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setPen(Qt::red);if (m_points.size() == 1){painter.drawLine(m_points.at(0), QCursor::pos()); //只有一个点,线随着鼠标活动}else if (m_points.size() > 1){int i=1;for (i=1; i<m_points.size(); ++i){painter.drawLine(m_points.at(i-1), m_points.at(i));}if (snapstate == Snapped){painter.drawLine(m_points.at(i-1), m_points.at(0)); //最后一个点和初始点连接}else{painter.drawLine(m_points.at(i-1), QCursor::pos()); //最后一个点,线随着鼠标活动}}}void MaskWidget::keyPressEvent(QKeyEvent *event)
{if (event->key() == Qt::Key_Escape){close();}else if (event->key() == Qt::Key_Space) //空格键截图{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);}QImage img = combined.copy(m_maskRect).toImage(); //根据选中区域的边框剪裁图像img.convertTo(QImage::Format_RGBA8888);QPainterPath path;QPolygon tempMask(this->m_points.toVector());tempMask.translate(-m_maskRect.x(), -m_maskRect.y()); //平移多边形,使它位于剪裁后的区域path.addPolygon(tempMask);for (int i=0; i<img.width(); ++i){for (int j=0; j<img.height(); ++j){QColor col = img.pixelColor(i, j);QPainterPath pathPoint = QPainterPath(QPointF(i,j));if(path.contains(pathPoint))//判断位置i,j是否在多边形内{col.setAlpha(255);}else{col.setAlpha(0);}img.setPixelColor(i,j,col);}}QPixmap pix = QPixmap::fromImage(img);m.SetImage(pix);this->hide();m.show();}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;m_points.clear();this->show();
}
头文件
#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};QList<QPoint> m_points;
private:Ui::MaskWidget *ui;};#endif // MASKWIDGET_H
mainwindow.cpp同第二篇文章
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QFileDialog>
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle(QString(tr("截图")));ui->centralwidget->setMouseTracking(true);ui->comboBox->addItem(QString(tr(".")));ui->comboBox->addItem(QString(tr("Select Folder")));connect(ui->comboBox, SIGNAL(activated(int)), this, SLOT(SelectFolder(int)));connect(ui->button_reset, SIGNAL(clicked(bool)), this, SLOT(ResetSnap(bool)));connect(ui->button_save, SIGNAL(clicked(bool)), this, SLOT(SavePicture(bool)));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::mouseMoveEvent(QMouseEvent *event)
{qDebug()<<this->geometry();QMainWindow::mouseMoveEvent(event);
}void MainWindow::SetImage(QPixmap &pixmap)
{const double defultWidth = 400.0;const double defaultHeight = 160.0;ui->label->setPixmap(pixmap);double pixscale = 1.0 * pixmap.width()/pixmap.height();double initscale = defultWidth/defaultHeight;if (pixscale > initscale){ui->label->setFixedWidth(defultWidth);ui->label->setFixedHeight(defultWidth / pixscale);}else{ui->label->setFixedHeight(defaultHeight);ui->label->setFixedWidth(defaultHeight * pixscale);}
}void MainWindow::SelectFolder(int index)
{if (index == 1){qDebug()<<ui->comboBox->itemText(index);QString directory = QFileDialog::getExistingDirectory(this,tr("QFileDialog::getExistingDirectory()"),".");if (!directory.isEmpty()){qDebug()<<directory;ui->comboBox->addItem(directory);ui->comboBox->setCurrentText(directory);}}
}void MainWindow::ResetSnap(bool)
{this->hide();emit resetSnap();
}void MainWindow::SavePicture(bool)
{if (ui->comboBox->currentText() != "."){ui->label->pixmap()->save(ui->comboBox->currentText() + "/" + ui->lineEdit->text(), "PNG");}else{ui->label->pixmap()->save(ui->lineEdit->text(), "PNG");}
}
mainwindow.h同第二篇文章
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void SetImage(QPixmap &pixmap);protected:void mouseMoveEvent(QMouseEvent *event) override;private slots:void SelectFolder(int index);void ResetSnap(bool);void SavePicture(bool);signals:void resetSnap();private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
相关文章:

QT截图程序三-截取自定义多边形
上一篇文章QT截图程序,可多屏幕截图二,增加调整截图区域功能-CSDN博客描述了如何截取,具备调整边缘功能后已经方便使用了,但是与系统自带的程序相比,似乎没有什么特别,只能截取矩形区域。 如果可以按照自己…...

Unity的三种Update方法
1、FixedUpdate 物理作用——处理物理引擎相关的计算和刚体的移动 (1) 调用时机:在固定的时间间隔内,而不是每一帧被调用 (2) 作用:用于处理物理引擎的计算,例如刚体的移动和碰撞检测 (3) 特点:能更准确地处理物理…...

[Python学习篇] Python字典
字典是一种可变的、无序的键值对(key-value)集合。字典在许多编程(Java中的HashMap)任务中非常有用,因为它们允许快速查找、添加和删除元素。字典使用花括号 {} 表示。字典是可变类型。 语法: 变量 {key1…...

react项目中如何书写css
一:问题: 在 vue 项目中,我们书写css的方式很简单,就是在 .vue文件中写style标签,然后加上scope属性,就可以隔离当前组件的样式,但是在react中,是没有这个东西的,如果直…...
PostgreSQL源码分析——绑定变量
这里分析一下函数中应用绑定变量的问题,但实际应用场景中,不推荐这么使用。 prepare divplan2(int,int) as select div($1,$2); execute divplan2(4,2);语法解析 分别分析prepare语句以及execute语句。 gram.y中定义 /******************************…...

Zynq学习笔记--了解中断配置方式
目录 1. 简介 2. 工程与代码解析 2.1 Vivado 工程 2.2 Vitis 裸机代码 2.3 关键代码解析 3. 总结 1. 简介 Zynq 中的中断可以分为以下几种类型: 软件中断(Software Generated Interrupt, SGI):由软件触发,通常…...

吴恩达机器学习 第二课 week2 多分类问题
目录 01 学习目标 02 实现工具 03 概念与原理 04 应用示例 05 总结 01 学习目标 (1)理解二分类与多分类的原理区别 (2)掌握简单多分类问题的神经网络实现方法 (3)理解多分类问题算法中的激活函数与损失…...

112、路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。 叶子节点 是指没有子节点…...
Vue 封装组件之Input框
封装Input组件:MyInput.vue <template><div class"base-input-wraper"><el-inputv-bind"$attrs"v-on"$listeners"class"e-input":style"inputStyle":value"value":size"size"input&quo…...
一段代码让你了解Java中的抽象
我们先来看一道题! 计算几何对象的面积之和)编写一个方法,该方法用于计算数组中所有几何对象的面积之和。该方法的签名是: public static double sumArea(GeometricObject[] a) 编写一个测试程序,该程序创建一个包含四…...

Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(factoryMethod)
序言 前面文章介绍了在Spring中多种创建Bean实例的方式,包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor。 这篇文章继续介绍Spring中创建Bean的形式之一——factoryMethod。方法用的不多,感兴趣可以当扩展了解。…...
绝对全网首发,利用Disruptor EventHandler实现在多线程下顺序执行任务
disruptor有两种任务处理器,一个是EventHandler ,另一个是WorkHandler. EventHandler可以彼此独立消费同一个队列中的任务,WorkHandler可以共同竞争消费同一个队列中的任务。也就是说,假设任务队列中有a、b、c、d三个事件,eventHa…...
单例设计模式双重检查的作用
先看双重校验锁的写法 public class Singleton {/*volatile 修饰,singleton new Singleton() 可以拆解为3步:1、分配对象内存(给singleton分配内存)2、调用构造器方法,执行初始化(调用 Singleton 的构造函数来初始化成员变量&am…...

NGINX_十二 nginx 地址重写 rewrite
十二 nginx 地址重写 rewrite 1 什么是Rewrite Rewrite对称URL Rewrite,即URL重写,就是把传入Web的请求重定向到其他URL的过程。URL Rewrite最常见的应用是URL伪静态化,是将动态页面显示为静态页面方式的一种技术。比如 http://www.123.com…...

react用ECharts实现组织架构图
找到ECharts中路径图。 然后开始爆改。 <div id{org- name} style{{ width: 100%, height: 650, display: flex, justifyContent: center }}></div> // data的数据格式 interface ChartData {name: string;value: number;children: ChartData[]; } const treeDep…...

坚持刷题|合并有序链表
文章目录 题目思考代码实现迭代递归 扩展实现k个有序链表合并方法一方法二 PriorityQueue基本操作Java示例注意事项 Hello,大家好,我是阿月。坚持刷题,老年痴呆追不上我,消失了一段时间,我又回来刷题啦,今天…...

SPI协议——对外部SPI Flash操作
目录 1. W25Q32JVSSIQ背景知识 1.1 64个可擦除块 1.2 1024个扇区(每个块有16个扇区) 1.3 页 1. W25Q32JVSSIQ背景知识 W25Q32JV阵列被组织成16,384个可编程页,每页有256字节。一次最多可以编程256个字节。页面可分为16组(4KB扇区清除&…...

kotlin类型检测与类型转换
一、is与!is操作符 1、使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型。 fun main() {var a "1"if(a is String) {println("a是字符串类型:${a.length}")}// 或val b a is Stringprintln(b) } 二、"不安全的"转换操作符…...
【JDBC】Oracle数据库连接问题记录
Failed to load driver class oracle.jdbc.driver.OracleDriver in either of HikariConfig class oracle驱动包未正确加载,可以先尝试使用下面方式加载检查类是否存在,如果不存在需要手动下载odbc包 try {Class.forName("oracle.jdbc.driver.Ora…...
leetcode45 跳跃游戏II
题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1]…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...