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

[Python]用Qt6和Pillow实现截图小工具

        本文章主要讲述的内容是,使用python语言借助PyQt6和Pillow库进行简单截图工具的开发,含义一个简单的范围裁剪和软件界面。

        主要解决的问题是,在高DPI显示屏下,坐标点的偏差导致QWidget显示图片不全、剪裁范围偏差问题。

        适合有一点点基础的朋友来看,使用的工具有:Qt Designer、PyUIC、Qt6、Pillow

截图与剪裁功能设计思路

一般截图功能的步骤是:

  1. 启用截图功能
  2. 将整个屏幕进行截取,保存截取的全屏图片
  3. 呈现出刚刚截取的全屏,由用户选择截取的范围。并对所选的范围进行剪裁
  4. 保存剪裁的图片,删除截取的全屏

利用QtDesigner对软件前端的简单制作

mainWindow-主界面

这里不是重点,就新建一个Main Window后放置一个pushButton就好了。

并使用PyUIC对保存后的ui转换成.py格式

 minorWindow-副界面

创建一个简单的Widget就好了,副界面主要是作用是:呈现原图,提供剪裁的平台。

并使用PyUIC对保存后的ui转换成.py格式

主界面代码编写

主要是作用是:

  1. 为截图功能提供一个启动方法
  2. 保存截取的全屏幕截图。
import timefrom PIL import ImageGrab
from PyQt6 import QtWidgetsfrom shDemo import mainWindow
from shDemo import minorWindow# 继承我们前面编写的主界面的前端.py,以及对应的QMainWindow
class screenshot(QtWidgets.QMainWindow, mainWindow.Ui_MainWindow):def __init__(self):super().__init__()self.setupUi(self)  # 调用主界面的setupUIself.pushButton.clicked.connect(self.screenshot)  # 绑定pushButton按钮到screenshot事件上# 按钮被点击后触发此方法def screenshot(self):# 把当前窗口最小化self.showMinimized()# 等待1秒,给窗口最小化的时间time.sleep(1)# 截取全屏img = ImageGrab.grab()# 暂存全屏图片 保存到本地img.save('屏幕快照.png')# 生成副窗口self.childWidget = minorWindow.Ui_jieping()# 展示副窗口self.childWidget.show()# 完成剪裁工作,恢复主窗口self.showNormal()if __name__ == '__main__':app = QtWidgets.QApplication([])window = screenshot()window.show()app.exec()

副界面代码编写

因为此处的副界面是被调用的,我们直接在其ui转换后的.py文件上进行编写,拓展其方法

继承一下QWidget,调用一下setupUi

class Ui_jieping(QtWidgets.QWidget):def __init__(self):super().__init__()self.setupUi(self)

原截图呈现、范围绘制与范围截取

要注意的就是,呈现的像素比率,截取的坐标点

主要思路是,将保存好的原截图,呈现到一个QWidget(副界面)上进行显示。

这里有一个问题,就是关于屏幕DPI不同

重写一下paintEvent方法,这是一个QWidget类中原有方法,是一个绘制组件的事件。被调用的情况有如下:

  1. 窗口初始化和显示
  2. 部件大小或位置发生变化
  3. 强制重绘,使用update()或repaint()时
  4. 系统事件触发,如窗口激活

像素比率 

像素比率 = 物理像素尺寸 / 逻辑像素尺寸

         为了适应不同应用,获得更好的视觉感官,一般可以调整缩放与布局。调整到比较高的DPI,获得一个更好体验。

        屏幕缩放比例为125%,意味着逻辑像素将比物理像素更大,以便内容在屏幕上看起来更大。缩放比例125%可以表示为1.25的倍数。

        在缩放比例为125%的情况下,1920*1080的显示屏中逻辑像素的分辨率将变为1536x864。 

        显示图片时需要转换为逻辑尺寸,以确保在不同DPI的显示器上图像显示的尺寸一致。然而,截图抓取的坐标点是物理像素坐标的,因为截图本质上是对屏幕上实际像素的捕捉。

        所以在显示的时候,按照屏幕的逻辑尺寸进行展示。实际抓取的时候,要转成物理尺寸进行截取,根据像素比率对图片显示进行对应调整后就不影响图片的显示或坐标点的偏差

import typingfrom PIL import ImageGrab
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtGui import QPainter, QPixmap, QPen, QColorclass Ui_jieping(QtWidgets.QWidget):def __init__(self):super().__init__()self.setupUi(self)# 记录截取的第一个坐标点self.firstPoint = QtCore.QPoint()# 记录截取的第二个坐标点self.endPoint = QtCore.QPoint()# 将子窗口设置在屏幕最上层# self.setWindowFlag(QtCore.Qt.WindowType.WindowStaysOnTopHint)# 让其全屏显示self.setWindowState(QtCore.Qt.WindowState.WindowFullScreen)# 重写QWidget的painEvent方法,这个在初始启动的时候会调用def paintEvent(self, a0: typing.Optional[QtGui.QPaintEvent]) -> None:# 生成一个画板painter = QPainter(self)# 读取本地先前在主界面截图的图像# 在QT中图片放到组件上一般要转成pixmappixmap = QPixmap('./屏幕快照.png')# 获取主屏幕对象screen = QtGui.QGuiApplication.primaryScreen()# 获取设备像素比率  物理像素与逻辑像素之间的比率self.device_pixel_ratio = screen.devicePixelRatio()# 计算实际绘制尺寸# 在显示和编程的时候,是按照逻辑像素取进行展示与设计# 逻辑尺寸= 物理尺寸 / 像素比  计算出符合当前屏幕的尺寸actual_width = pixmap.width() / self.device_pixel_ratioactual_height = pixmap.height() / self.device_pixel_ratio# 绘制图片# 0,0的意思是,从屏幕左上角作为起始点,如果此时的逻辑尺寸与屏幕的一致,就作为全屏展示painter.drawPixmap(0, 0, int(actual_width), int(actual_height), pixmap)# 将截图画框显示为红色pen = QPen(QColor(255, 0, 0))painter.setPen(pen)# 绘制矩形的方法,其中的参数来自鼠标事件 显示要截图的范围 在绘制的时候还会调用update来触发paintEvent方法# 从第一个记录点开始# 记住0,0是屏幕最坐上角# 向右self.endPoint.x() - self.firstPoint.x()个像素 作为长# 向下self.endPoint.y() - self.firstPoint.y()个像素 作为高# 得到负数也没关系噢,x方向上负数就是往左, y方向上负数是向上painter.drawRect(self.firstPoint.x(), self.firstPoint.y(), self.endPoint.x() - self.firstPoint.x(),self.endPoint.y() - self.firstPoint.y())# 在鼠标按下的时候触发此事件def mousePressEvent(self, a0: typing.Optional[QtGui.QMouseEvent]) -> None:# 记录按下的第一个坐标点self.firstPoint = a0.pos()# 在鼠标移动的时候触发此事件def mouseMoveEvent(self, a0: typing.Optional[QtGui.QMouseEvent]) -> None:# 记录移动过程中的当前鼠标的坐标点self.endPoint = a0.pos()self.update()  # 触发paintEvent,在移动鼠标的时候不断重绘截图边框# 在鼠标松开的时候触发此事件def mouseReleaseEvent(self, a0: typing.Optional[QtGui.QMouseEvent]) -> None:self.endPoint = a0.pos()  # 锁定最后松开的坐标self.update()  # 更新在Widget上的所选范围矩形# 在原图上进行对所选区域的截取# 此处截图的时候,也要记得调整一下 从逻辑像素转换成为物理像素进行抓取# 不然截图出来会有偏差# 物理像素 = 逻辑像素 * 像素比率self.firstPoint.setX(int(self.firstPoint.x() * self.device_pixel_ratio))self.firstPoint.setY(int(self.firstPoint.y() * self.device_pixel_ratio))self.endPoint.setX(int(self.endPoint.x() * self.device_pixel_ratio))self.endPoint.setY(int(self.endPoint.y() * self.device_pixel_ratio))# 最后借助PIL进行对屏幕固定范围进行抓取# 这里有一个坑 在从右向左,从下到上进行画范围截图的时候,会有一个报错# 因为grab的参数是,左上角和右下角坐标点的x和y值# firstPoint和endPoint又是一开始写死的# 可以比较一下两者的位置,如果endPoint比firstPoint小,就可以互换一下if self.firstPoint.x() > self.endPoint.x() and self.firstPoint.y() > self.endPoint.y():self.firstPoint, self.endPoint = self.endPoint, self.firstPointimage = ImageGrab.grab(bbox=(self.firstPoint.x() + 1, self.firstPoint.y() + 1, self.endPoint.x() - 1, self.endPoint.y() - 1))# 将范围截取下来的进行保存image.save('hello.png')# 就可以将先前截的全屏删掉了# os.remove('./屏幕快照.png')# 关闭全屏显示的子窗口self.close()def setupUi(self, jieping):jieping.setObjectName("jieping")jieping.resize(400, 300)self.retranslateUi(jieping)QtCore.QMetaObject.connectSlotsByName(jieping)def retranslateUi(self, jieping):_translate = QtCore.QCoreApplication.translatejieping.setWindowTitle(_translate("jieping", "Form"))

在不同的DPI下截取出来的图片都是一样滴,大家可以去试一下

相关文章:

[Python]用Qt6和Pillow实现截图小工具

本文章主要讲述的内容是,使用python语言借助PyQt6和Pillow库进行简单截图工具的开发,含义一个简单的范围裁剪和软件界面。 主要解决的问题是,在高DPI显示屏下,坐标点的偏差导致QWidget显示图片不全、剪裁范围偏差问题。 适合有一点…...

Podman和Docker的区别

Podman 和 Docker 都是用于容器化的工具,但它们在架构、安全性、容器编排以及一些设计理念上有显著的区别: 架构设计: Docker 使用客户端-服务器(C/S)架构,包含一个名为 dockerd 的守护进程,该进程以 root …...

Go微服务: 分布式Cap定理和Base理论

分布式中的Cap定理 CAP理论 C: 一致性,是站在分布式的角度,要么读取到数据,要么读取失败,比如数据库主从,同步时的时候加锁,同步完成才能读到同步的数据,同步完成,才返回数据给程序&…...

Mysql学习(四)——SQL通用语法之DQL

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 DQLDQL-语法基本查询条件查询聚合函数分组查询排序查询分页查询 DQL DQL数据查询语言,用来查询数据库中表的记录。 DQL-语法 select 字段列表 from 表…...

【ARFoundation自学05】人脸追踪(AR Face manager)实现

1. 修改摄像机朝向渲染方式-选中user 这个方式就会调用前置摄像头 2 创建 AR Session、XR Origin,然后在XR Origin上面添加组件 注意:XR Origin 老版本仍然叫 AR Session Origin 接下来在XR Origin上面添加AR Face Manager组件,如下图&am…...

Vulnhub-DC-2

靶机IP:192.168.20.135 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) kaliIP:192.168.20.128 扫描靶机端口及服务版本 发现开放了80和7744端口 并且是wordpress建站 dirsearch扫描目录 访问前端界面,发现存在重定向 在hosts文件中增加192.168.2…...

VNC server ubuntu20 配置

介绍 最近想使用实验室的4卡服务器跑一些深度学习实验,因为跑的是三维建图实验,需要配上可视化界面,本来自带的IPMI可以可视化,但分辨率固定在640*480,看起来很别扭,就捣鼓服务器远程可视化访问了两天&…...

c++--priority_queue和仿函数

目录 1.priority_queue 实现: 2.仿函数 priority_queue仿函数 实现代码 1.priority_queue 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的,其实就是个堆,默认是大根堆。…...

Harmony os Next——关系型数据库relationalStore.RdbStore的使用

Harmony os Next——关系型数据库relationalStore.RdbStore的使用 描述数据库的使用建表定义表信息创建数据库表 创建数据库操作对象增更新查询删数据库的初始化 描述 本文通过存储一个简单的用户信息到数据库中为例,进行阐述relationalStore.RdbStore数据库的CRUD…...

快手直播限流怎么办?

直播限流怎么办?这期把直播间限流的所有原因都讲得明明白白,如果你直播间昨天还播的好好的,今天突然间贴地飞行,按照这个思路框架去排查,准没问题。 第一件事情肯定是排查一下评分问题, 信用分、口碑分、…...

【MySQL】数据库入门基础

文章目录 一、数据库的概念1. 什么是数据库2. 主流数据库3. mysql和mysqld的区别 二、MySQL基本使用1. 安装MySQL服务器在 CentOS 上安装 MySQL 服务器在 Ubuntu 上安装 MySQL 服务器验证安装 2. 服务器管理启动服务器查看服务器连接服务器停止服务器重启服务器 3. 服务器&…...

cannot allocate memory in static TLS block

如果不是内存太小,那是不是因为glibc太旧呢? 考虑 glibc 2.22 以后的版本。 glibc-2.22 中加入了如下commit:f8aeae347377f3dfa8cbadde057adf1827fb1d44 https://sourceware.org/git/?pglibc.git;acommit;hf8aeae347377f3dfa8cbadde057adf1…...

Leetcode 654:最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums 构建的 最大二叉树…...

uniapp小程序src引用服务器图片时全局变量与图片路径拼接

理论上&#xff0c;应该在main.js中定义一个全局变量&#xff0c;然后在页面的<image>标签上的是src直接使用即可 main.js 页面上 看上去挺靠谱的&#xff0c;实际上小程序后台会报一个错 很明显这种方式小程序是不认的&#xff0c;这就头疼了&#xff0c;还想过另外一个…...

比较PWM调光和无极调光

在比较PWM调光和无极调光哪种方式更节能时&#xff0c;需要综合考虑多个因素&#xff0c;如灯具类型、光源效率、调光范围以及使用场景等。 PWM调光系统通过调节LED驱动电流的占空比来实现LED亮度的调节&#xff0c;具有高精度、高稳定性、无闪烁现象以及适用范围广等优点。其节…...

【高校科研前沿】新疆生地所陈亚宁研究员团队在GeoSus发文:在1.5°C和2°C全球升温情景下,中亚地区暴露于极端降水的人口增加

目录 文章简介 1.研究内容 2.相关图件 3.文章引用 文章简介 论文名称&#xff1a;Increased population exposures to extreme precipitation in Central Asia under 1.5 ◦C and 2 ◦C global warming scenarios&#xff08;在1.5C和2C全球变暖情景下&#xff0c;中亚地区…...

使用 OKhttp3 实现 智普AI ChatGLM HTTP 调用(SSE、异步、同步)

SSE 调用 SSE&#xff08;Sever-Sent Event&#xff09;&#xff0c;就是浏览器向服务器发送一个HTTP请求&#xff0c;保持长连接&#xff0c;服务器不断单向地向浏览器推送“信息”&#xff08;message&#xff09;&#xff0c;这么做是为了节约网络资源&#xff0c;不用一直…...

智慧校园教学模式的崛起:优化学习体验

在当今数字化时代&#xff0c;智慧校园教学模式正在成为教育界的热门话题。随着科技的不断发展&#xff0c;传统的教学方式已经无法满足现代学生的需求。智慧校园教学模式以其灵活性、互动性和个性化的特点&#xff0c;正逐渐改变着教育的面貌。 首先&#xff0c;智慧校园教学模…...

ffmpeg视频编码原理和实战-(5)对编码过程进行封装并解决丢帧问题

头文件&#xff1a; xencode.h #pragma once #include <mutex> #include<vector> struct AVCodecContext; struct AVPacket; struct AVFrame; class XEncode { public:///// 创建编码上下文/// para codec_id 编码器ID号&#xff0c;对应ffmpeg/// return 编码上…...

halo进阶-主题插件使用

开始捣鼓捣鼓halo&#xff0c;换换主题&#xff0c;加个页面 可参考&#xff1a;Halo 文档 安装/更新主题 主题如同壁纸&#xff0c;萝卜青菜各有所爱&#xff0c;大家按需更换即可&#xff1b; Halo好在一键更换主题&#xff0c;炒鸡方便。 安装/更新插件 此插件还扩展了插件…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...