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

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截图程序&#xff0c;可多屏幕截图二&#xff0c;增加调整截图区域功能-CSDN博客描述了如何截取&#xff0c;具备调整边缘功能后已经方便使用了&#xff0c;但是与系统自带的程序相比&#xff0c;似乎没有什么特别&#xff0c;只能截取矩形区域。 如果可以按照自己…...

Unity的三种Update方法

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

[Python学习篇] Python字典

字典是一种可变的、无序的键值对&#xff08;key-value&#xff09;集合。字典在许多编程&#xff08;Java中的HashMap&#xff09;任务中非常有用&#xff0c;因为它们允许快速查找、添加和删除元素。字典使用花括号 {} 表示。字典是可变类型。 语法&#xff1a; 变量 {key1…...

react项目中如何书写css

一&#xff1a;问题&#xff1a; 在 vue 项目中&#xff0c;我们书写css的方式很简单&#xff0c;就是在 .vue文件中写style标签&#xff0c;然后加上scope属性&#xff0c;就可以隔离当前组件的样式&#xff0c;但是在react中&#xff0c;是没有这个东西的&#xff0c;如果直…...

PostgreSQL源码分析——绑定变量

这里分析一下函数中应用绑定变量的问题&#xff0c;但实际应用场景中&#xff0c;不推荐这么使用。 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 中的中断可以分为以下几种类型&#xff1a; 软件中断&#xff08;Software Generated Interrupt, SGI&#xff09;&#xff1a;由软件触发&#xff0c;通常…...

吴恩达机器学习 第二课 week2 多分类问题

目录 01 学习目标 02 实现工具 03 概念与原理 04 应用示例 05 总结 01 学习目标 &#xff08;1&#xff09;理解二分类与多分类的原理区别 &#xff08;2&#xff09;掌握简单多分类问题的神经网络实现方法 &#xff08;3&#xff09;理解多分类问题算法中的激活函数与损失…...

112、路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 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中的抽象

我们先来看一道题&#xff01; 计算几何对象的面积之和&#xff09;编写一个方法&#xff0c;该方法用于计算数组中所有几何对象的面积之和。该方法的签名是&#xff1a; public static double sumArea(GeometricObject[] a) 编写一个测试程序&#xff0c;该程序创建一个包含四…...

Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(factoryMethod)

序言 前面文章介绍了在Spring中多种创建Bean实例的方式&#xff0c;包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor。 这篇文章继续介绍Spring中创建Bean的形式之一——factoryMethod。方法用的不多&#xff0c;感兴趣可以当扩展了解。…...

绝对全网首发,利用Disruptor EventHandler实现在多线程下顺序执行任务

disruptor有两种任务处理器&#xff0c;一个是EventHandler ,另一个是WorkHandler. EventHandler可以彼此独立消费同一个队列中的任务&#xff0c;WorkHandler可以共同竞争消费同一个队列中的任务。也就是说&#xff0c;假设任务队列中有a、b、c、d三个事件&#xff0c;eventHa…...

单例设计模式双重检查的作用

先看双重校验锁的写法 public class Singleton {/*volatile 修饰&#xff0c;singleton new Singleton() 可以拆解为3步&#xff1a;1、分配对象内存(给singleton分配内存)2、调用构造器方法&#xff0c;执行初始化&#xff08;调用 Singleton 的构造函数来初始化成员变量&am…...

NGINX_十二 nginx 地址重写 rewrite

十二 nginx 地址重写 rewrite 1 什么是Rewrite Rewrite对称URL Rewrite&#xff0c;即URL重写&#xff0c;就是把传入Web的请求重定向到其他URL的过程。URL Rewrite最常见的应用是URL伪静态化&#xff0c;是将动态页面显示为静态页面方式的一种技术。比如 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&#xff0c;大家好&#xff0c;我是阿月。坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;消失了一段时间&#xff0c;我又回来刷题啦&#xff0c;今天…...

SPI协议——对外部SPI Flash操作

目录 1. W25Q32JVSSIQ背景知识 1.1 64个可擦除块 1.2 1024个扇区&#xff08;每个块有16个扇区&#xff09; 1.3 页 1. W25Q32JVSSIQ背景知识 W25Q32JV阵列被组织成16,384个可编程页&#xff0c;每页有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驱动包未正确加载&#xff0c;可以先尝试使用下面方式加载检查类是否存在&#xff0c;如果不存在需要手动下载odbc包 try {Class.forName("oracle.jdbc.driver.Ora…...

leetcode45 跳跃游戏II

题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1]…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...