QT截图程序三-截取自定义多边形

news2024/12/22 9:57:20

上一篇文章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_OBJECT

public:
    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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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

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

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

相关文章

【Esp32连接微信小程序蓝牙】附Arduino源码《 返回10007 相同特征id冲突问题》

前言 最近接了一个外包&#xff0c;发现了esp32连接小程序会有很多bug&#xff0c;所以接下来会慢慢更新解决方案&#xff0c;还是需要多接触项目才能进步呀兄弟们&#xff01; 附上uuid的生成链接&#xff1a; // See the following for generating UUIDs: // https://www.uu…

112、路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点 是指没有子节点…

C语言 图的基础知识

图 图的基本概念图的存储方法**邻接矩阵**&#xff1a;邻接表 图的遍历深度优先&#xff08;DFS&#xff09;广度优先&#xff08;BFS&#xff09; 最小生成树Prim算法Kruskal算法 最短路径问题 图的基本概念 图的定义&#xff1a; 图是由顶点的非空有穷集合与顶点之间关系&am…

鸿蒙开发通信与连接:【@ohos.rpc (RPC通信)】

RPC通信 本模块提供进程间通信能力&#xff0c;包括设备内的进程间通信&#xff08;IPC&#xff09;和设备间的进程间通信&#xff08;RPC&#xff09;&#xff0c;前者基于Binder驱动&#xff0c;后者基于软总线驱动。 说明&#xff1a; 本模块首批接口从API version 7开始支…

MySQL之复制(七)

复制 定制的复制方案 分离功能 许多应用都混合了在线事务处理(OLTP)和在线数据分析(OLAP)的查询。OLTP查询比较短并且是事务型的。OLAP查询则通常很大&#xff0c;也很慢&#xff0c;并且不要求绝对最新的数据。这两种查询给服务器带来的负担完全不同&#xff0c;因此它们需…

go sync包(一) 互斥锁(一)

Sync包 sync包是go提供的用于并发控制的方法&#xff0c;类似于Java的JUC包。 &#xff08;图片来自《go设计与实现》&#xff09; 互斥锁 Mutex Go 语言的 sync.Mutex 由两个字段 state 和 sema 组成。 state 表示当前互斥锁的状态。sema 是用于控制锁状态的信号量。 ty…

Vue66-vue-默认插槽

一、默认插槽需求 1-1、原本的写法&#xff1a; 在每个category组件中用v-show来做条件渲染&#xff0c;但是不方便&#xff01; 1-2、默认插槽 img标签&#xff0c;ul标签&#xff0c;video标签&#xff0c;都是在app组件中完成解析之后&#xff0c;塞到category组件中的&…

git的远程管理与标签管理

✨前言✨ &#x1f4d8; 博客主页&#xff1a;to Keep博客主页 &#x1f646;欢迎关注&#xff0c;&#x1f44d;点赞&#xff0c;&#x1f4dd;留言评论 ⏳首发时间&#xff1a;2024年6月20日 &#x1f4e8; 博主码云地址&#xff1a;博主码云地址 &#x1f4d5;参考书籍&…

Docker 可用镜像源

当使用 docker 发现拉取不到镜像时&#xff0c;可以编辑 /etc/docker/daemon.json 文件&#xff0c;添加如下内容&#xff1a; 这文章不涉及政治&#xff0c;不涉及敏感信息&#xff0c;三番五次的审核不通过&#xff0c;一删再删&#xff0c;只好换图片了。 重新加载服务配置…

Meta悄咪咪的发布多款AI新模型

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则…

绿茶集团重启IPO:流量渐退、业绩波动,还能讲出好故事吗?

近日&#xff0c;绿茶集团有限公司(下称“绿茶集团”)向港交所递交上市申请&#xff0c;花旗、招银国际为其联席保荐人。 回望绿茶集团的上市之路&#xff0c;可谓有诸多坎坷。该公司于2021年3月首度向港交所发起冲击&#xff0c;但却将中文版招股书中的“流动负债总额”错写成…

开发一个python工具,pdf转图片,并且截成单个图片,然后修整没用的白边

今天推荐一键款本人开发的pdf转单张图片并截取没有用的白边工具 一、开发背景&#xff1a; 业务需要将一个pdf文件展示在前端显示&#xff0c;但是基于各种原因&#xff0c;放弃了h5使用插件展示 原因有多个&#xff0c;文件资源太大加载太慢、pdf展示兼容性问题、pdf展示效果…

Spring Boot集成tablesaw插件快速入门

1 什么是tablesaw&#xff1f; Tablesaw是一款Java的数据可视化库&#xff0c;主要包括两部分&#xff1a; 数据解析库&#xff0c;主要用于加载数据&#xff0c;对数据进行操作(转化&#xff0c;过滤&#xff0c;汇总等)&#xff0c;类比Python中的Pandas库&#xff1b; 数据…

Python基础教程(二十四):日期和时间

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

如何判断精益管理咨询公司的服务价格是否合理?

在当下市场竞争日益激烈的背景下&#xff0c;精益管理咨询公司如雨后春笋般涌现&#xff0c;为企业提供了更多的选择空间。然而&#xff0c;随之而来的是服务价格参差不齐&#xff0c;让企业难以判断其合理性。本文将探讨如何判断精益管理咨询公司的服务价格是否合理&#xff0…

02-QWebEngineView的使用

Qt WebEngine_hitzsf的博客-CSDN博客 一、QWebEngineView QWebEngineView 类是一个实现Web浏览器的便捷类&#xff0c;提供了back() 、forward()、reload()、stop() 等方法&#xff0c;可轻松实现页面的前进、后退、重载等导航功能&#xff0c;要实现一个简单的只有网页加载网…

【Python办公自动化之Word】

python办公⾃动化之-Word python-docx库 文章目录 python办公⾃动化之-Word1、安装python-docx库2、⽂档的结构说明3、基本操作语法3.1 打开⽂档3.2加⼊不同等级的标题3.3 添加⽂本3.4 设置字号插曲1&#xff1a;实战演示3.5 设置中⽂字体3.6 设置斜体3.7 设置粗体3.8⾸⾏缩进…

2024年,收付通申请开通流程

大家好&#xff0c;今天咱们来聊聊关于APP场景中开通微信收付通的一些实用小窍门。在如今的移动互联网时代&#xff0c;很多商家都选择通过APP来提供服务和产品&#xff0c;因此如何在APP中顺利集成微信收付通功能&#xff0c;让用户能够轻松完成支付&#xff0c;就显得尤为重要…

MySQL数据库进阶笔记

第一章 存储引擎 1.1 MySQL体系结构 连接层 最上层是一些客户端和链接服务,主要完成一些类似连接处理、授权认证、及相关的安全方案。服务器也为安全接入的每个客户端验证它所具有的操作权限。 服务层 第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查…

五十一、openlayers官网示例Layer Min/Max Resolution解析——设置图层最大分辨率,超过最大值换另一个图层显示

使用minResolution、maxResolution分辨率来设置图层显示最大分辨率。 <template><div class"box"><h1>Layer Min/Max Resolution</h1><div id"map" class"map"></div></div> </template><…