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]…...
【数学】什么是方法矩估计?和最大似然估计是什么关系?
背景 方法矩估计(Method of Moments Estimation)和最大似然估计(Maximum Likelihood Estimation, MLE)是两种常用的参数估计方法。方法矩估计基于样本矩与总体矩的关系,通过样本数据计算样本矩来估计总体参数。最大似…...
C++初学者指南第一步---10.内存(基础)
C初学者指南第一步—10.内存(基础) 文章目录 C初学者指南第一步---10.内存(基础)1.内存模型1.1 纸上谈兵:C的抽象内存模型1.2 实践:内存的实际处理 2. 自动存储3.动态存储:std::vector3.1 动态内…...
扩散模型详细推导过程——编码与解码
符号表 符号含义 x ( i ) z 0 ( i ) \boldsymbol{x}^{(i)}\boldsymbol{z}_0^{(i)} x(i)z0(i)第 i i i个训练数据,其为长度为 d d d的向量 z t ( i ) \boldsymbol{z}_t^{(i)} zt(i)第 i i i个训练数据在第 t t t时刻的加噪版本 ϵ t ( i ) \boldsymbol{\epsilo…...
js如何实现开屏弹窗
开屏弹窗是什么,其实就是第一次登录后进入页面给你的一种公告提示,此后再回到当前这个页面时弹窗是不会再出现的。也就是说这个弹窗只会出现一次。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>…...
C#——文件读取Directory类详情
文件读取Directory类 Durectory提供了目录以及子目录进行创建移动和列举操作方法 Directory和Directorylnfo类(主要操作文件目录属性列如文件是否隐藏的 或者只读等这些属性) Directory对目录进行复制、移动、重命名、创建和删除等操作DirectoryInfo用于对目录属性执行操作 …...
Ruby on Rails Post项目设置网站初始界面
在构建了Ruby的Web服务器后,第三步就可以去掉框架的官方页面,设置自己的网页初始页了。 Linux系统安装Ruby语言-CSDN博客 、在Ubuntu中创建Ruby on Rails项目并搭建数据库-CSDN博客、 Ruby语言建立Web服务器-CSDN博客 了解Ruby onRails项目中的主要文件…...
03-QTWebEngine中使用qtvirtualkeyboard
qt提供了 virtualKeyboard 虚拟键盘模块,只需要在在main函数中最开始加入这样一句就可以了 qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); 但是在使用的时候遇到了一些问题: 1、中文输入的时候没有输入提示 Qvirt…...
leetcode3无重复字符的最长字串(重点讲滑动窗口)
本文主要讲解无重复字符的最长字串的要点与细节,根据步骤一步步走更方便理解 c与java代码如下,末尾 具体要点: 1. 区分一下子串和子序列 子串:要求元素在母串中是连续地出现 子序列:不要求连续 2. 题目中有两个核心…...
Gobject tutorial 八
The GObject base class Object memory management Gobject的内存管理相关的API很复杂,但其目标是提供一个基于引用计数的灵活的内存管理模式。 下面我们来介绍一下,与管理引用计数相关的函数。 Reference Count 函数g_object_ref和g_object_unref的…...
DDMA信号处理以及数据处理的流程---cfar检测
Hello,大家好,我是Xiaojie,好久不见,欢迎大家能够和Xiaojie一起学习毫米波雷达知识,Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程,本系列文章将从目标生成、信号仿真、测距、测速、cfar检测、测角、目标聚类、目标跟踪这几个模块逐步介绍,这个系列的…...
国际企业网站建设/郑州seo顾问热狗
在Unity3D中动态创建对象和创建Prefab原型对象的三种方式u3d里动态创建对象,需要使用prefab而创建的时候 MonoBehaviour.Instantiate( GameObject orignal) 需要一个作为原型的对象。本文提供三种方式获得prefab对象。方式一:使用脚本的public字段直接在…...
平板购物网站建设/搜索引擎优化的概念
转载于:https://www.cnblogs.com/island1994/p/9205475.html...
装修设计软件app排行/举例说明seo
背景 同一个生产部署项目,基内外网的访问路径并不相同,内网是基于域名根目录来访问,而外网却指向了一个子目录。 eg. : vue-router: history模式内网环境:192.168.1.1:8080/index.html外网环境:domain.com/…...
乡村旅行网站开发的毕业论文/seo排名第一的企业
场景:由于自己的电脑A性能不足,需要转移到一台高性能的主机B上运行python程序,但是该主机不能连接互联网。问题:在个人电脑A上建立了一个虚拟环境,安装了依赖包并且调试程序使其能运行。但是将虚拟环境所在文件夹复制粘…...
会计网站建设意义/广告公司网站
数据存储 在javascript中,数据存储的位置会对代码整体性能产生重大的影响。 数据存储共有4种方式:字面量、变量、数组、对象成员。 要理解变量的访问速度,就要理解作用域。由于局部变量处于作用域的起始位置,因此访问速度比访问…...
wordpress做导航站/做seo用哪种建站程序最好
i spend ¥60 on lottery these days,as you might expect ,i lost all . At least i learned that its really a lottery.转载于:https://www.cnblogs.com/diving/archive/2012/02/20/lottery.html...