毕业设计都是做网站吗,二手电商怎么做,甘肃省专业做网站,手机手机网站建设本文章主要讲述的内容是#xff0c;使用python语言借助PyQt6和Pillow库进行简单截图工具的开发#xff0c;含义一个简单的范围裁剪和软件界面。 主要解决的问题是#xff0c;在高DPI显示屏下#xff0c;坐标点的偏差导致QWidget显示图片不全、剪裁范围偏差问题。 适合有一点… 本文章主要讲述的内容是使用python语言借助PyQt6和Pillow库进行简单截图工具的开发含义一个简单的范围裁剪和软件界面。 主要解决的问题是在高DPI显示屏下坐标点的偏差导致QWidget显示图片不全、剪裁范围偏差问题。 适合有一点点基础的朋友来看使用的工具有Qt Designer、PyUIC、Qt6、Pillow
截图与剪裁功能设计思路
一般截图功能的步骤是
启用截图功能将整个屏幕进行截取保存截取的全屏图片呈现出刚刚截取的全屏由用户选择截取的范围。并对所选的范围进行剪裁保存剪裁的图片删除截取的全屏
利用QtDesigner对软件前端的简单制作
mainWindow-主界面
这里不是重点就新建一个Main Window后放置一个pushButton就好了。
并使用PyUIC对保存后的ui转换成.py格式 minorWindow-副界面
创建一个简单的Widget就好了副界面主要是作用是呈现原图提供剪裁的平台。
并使用PyUIC对保存后的ui转换成.py格式 主界面代码编写
主要是作用是
为截图功能提供一个启动方法保存截取的全屏幕截图。
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类中原有方法是一个绘制组件的事件。被调用的情况有如下
窗口初始化和显示部件大小或位置发生变化强制重绘使用update()或repaint()时系统事件触发如窗口激活
像素比率
像素比率 物理像素尺寸 / 逻辑像素尺寸 为了适应不同应用获得更好的视觉感官一般可以调整缩放与布局。调整到比较高的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# 绘制图片# 00的意思是从屏幕左上角作为起始点如果此时的逻辑尺寸与屏幕的一致就作为全屏展示painter.drawPixmap(0, 0, int(actual_width), int(actual_height), pixmap)# 将截图画框显示为红色pen QPen(QColor(255, 0, 0))painter.setPen(pen)# 绘制矩形的方法其中的参数来自鼠标事件 显示要截图的范围 在绘制的时候还会调用update来触发paintEvent方法# 从第一个记录点开始# 记住00是屏幕最坐上角# 向右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下截取出来的图片都是一样滴大家可以去试一下