窗口的透视变换效果
具体代码
"htmlcode">
# coding:utf-8 import cv2 as cv import numpy from PyQt5.QtGui import QImage, QPixmap class PixmapPerspectiveTransform: """ 透视变换基类 """ def __init__(self, pixmap=None): """ 实例化透视变换对象\n Parameter --------- src : numpy数组 """ self.pixmap = pixmap def setPixmap(self, pixmap: QPixmap): """ 设置被变换的QPixmap """ self.pixmap = QPixmap self.src=self.transQPixmapToNdarray(pixmap) self.height, self.width = self.src.shape[:2] # 变换前后的边角坐标 self.srcPoints = numpy.float32( [[0, 0], [self.width - 1, 0], [0, self.height - 1], [self.width - 1, self.height - 1]]) def setDstPoints(self, leftTop: list, rightTop, leftBottom, rightBottom): """ 设置变换后的边角坐标 """ self.dstPoints = numpy.float32( [leftTop, rightTop, leftBottom, rightBottom]) def getPerspectiveTransform(self, imWidth, imHeight, borderMode=cv.BORDER_CONSTANT, borderValue=[255, 255, 255, 0]) -> QPixmap: """ 透视变换图像,返回QPixmap\n Parameters ---------- imWidth : 变换后的图像宽度\n imHeight : 变换后的图像高度\n borderMode : 边框插值方式\n borderValue : 边框颜色 """ # 如果是jpg需要加上一个透明通道 if self.src.shape[-1] == 3: self.src = cv.cvtColor(self.src, cv.COLOR_BGR2BGRA) # 透视变换矩阵 perspectiveMatrix = cv.getPerspectiveTransform( self.srcPoints, self.dstPoints) # 执行变换 self.dst = cv.warpPerspective(self.src, perspectiveMatrix, ( imWidth, imHeight), borderMode=borderMode, borderValue=borderValue) # 将ndarray转换为QPixmap return self.transNdarrayToQPixmap(self.dst) def transQPixmapToNdarray(self, pixmap: QPixmap): """ 将QPixmap转换为numpy数组 """ width, height = pixmap.width(), pixmap.height() channels_count = 4 image = pixmap.toImage() # type:QImage s = image.bits().asstring(height * width * channels_count) # 得到BGRA格式数组 array = numpy.fromstring(s, numpy.uint8).reshape( (height, width, channels_count)) return array def transNdarrayToQPixmap(self, array): """ 将numpy数组转换为QPixmap """ height, width, bytesPerComponent = array.shape bytesPerLine = 4 * width # 默认数组维度为 m*n*4 dst = cv.cvtColor(array, cv.COLOR_BGRA2RGBA) pix = QPixmap.fromImage( QImage(dst.data, width, height, bytesPerLine, QImage.Format_RGBA8888)) return pix
为了解决这个烦人的问题,我又对桌面上的窗口进行截屏,再次透视变换。注意是桌面上看到的窗口,这时的窗口肯定是会有背景的,这时的透视变换就不会存在上述问题,记这个透视变换完的图像为img_2
。但实际上我们本来是不想要img_2中的背景的,所以只要将img_2中的背景替换完img_1中的透明背景,下面是具体代码:
# coding:utf-8 import numpy as np from PyQt5.QtCore import QPoint, Qt from PyQt5.QtGui import QPainter, QPixmap, QScreen, QImage from PyQt5.QtWidgets import QApplication, QWidget from my_functions.get_pressed_pos import getPressedPos from my_functions.perspective_transform_cv import PixmapPerspectiveTransform class PerspectiveWidget(QWidget): """ 可进行透视变换的窗口 """ def __init__(self, parent=None, isTransScreenshot=False): super().__init__(parent) self.__visibleChildren = [] self.__isTransScreenshot = isTransScreenshot self.__perspectiveTrans = PixmapPerspectiveTransform() self.__screenshotPix = None self.__pressedPix = None self.__pressedPos = None @property def pressedPos(self) -> str: """ 返回鼠标点击位置 """ return self.__pressedPos def mousePressEvent(self, e): """ 鼠标点击窗口时进行透视变换 """ super().mousePressEvent(e) self.grabMouse() pixmap = self.grab() self.__perspectiveTrans.setPixmap(pixmap) # 根据鼠标点击位置的不同设置背景封面的透视变换 self.__setDstPointsByPressedPos(getPressedPos(self,e)) # 获取透视变换后的QPixmap self.__pressedPix = self.__getTransformPixmap() # 对桌面上的窗口进行截图 if self.__isTransScreenshot: self.__adjustTransformPix() # 隐藏本来看得见的小部件 self.__visibleChildren = [ child for child in self.children() if hasattr(child, 'isVisible') and child.isVisible()] for child in self.__visibleChildren: if hasattr(child, 'hide'): child.hide() self.update() def mouseReleaseEvent(self, e): """ 鼠标松开时显示小部件 """ super().mouseReleaseEvent(e) self.releaseMouse() self.__pressedPos = None self.update() # 显示小部件 for child in self.__visibleChildren: if hasattr(child, 'show'): child.show() def paintEvent(self, e): """ 绘制背景 """ super().paintEvent(e) painter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform) painter.setPen(Qt.NoPen) # 绘制背景图片 if self.__pressedPos: painter.drawPixmap(self.rect(), self.__pressedPix) def __setDstPointsByPressedPos(self,pressedPos:str): """ 通过鼠标点击位置设置透视变换的四个边角坐标 """ self.__pressedPos = pressedPos if self.__pressedPos == 'left': self.__perspectiveTrans.setDstPoints( [5, 4], [self.__perspectiveTrans.width - 2, 1], [3, self.__perspectiveTrans.height - 3], [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 1]) elif self.__pressedPos == 'left-top': self.__perspectiveTrans.setDstPoints( [6, 5], [self.__perspectiveTrans.width - 1, 1], [1, self.__perspectiveTrans.height - 2], [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 1]) elif self.__pressedPos == 'left-bottom': self.__perspectiveTrans.setDstPoints( [2, 3], [self.__perspectiveTrans.width - 3, 0], [4, self.__perspectiveTrans.height - 4], [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 2]) elif self.__pressedPos == 'top': self.__perspectiveTrans.setDstPoints( [3, 5], [self.__perspectiveTrans.width - 4, 5], [1, self.__perspectiveTrans.height - 2], [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 2]) elif self.__pressedPos == 'center': self.__perspectiveTrans.setDstPoints( [3, 4], [self.__perspectiveTrans.width - 4, 4], [3, self.__perspectiveTrans.height - 3], [self.__perspectiveTrans.width - 4, self.__perspectiveTrans.height - 3]) elif self.__pressedPos == 'bottom': self.__perspectiveTrans.setDstPoints( [2, 2], [self.__perspectiveTrans.width - 3, 3], [3, self.__perspectiveTrans.height - 3], [self.__perspectiveTrans.width - 4, self.__perspectiveTrans.height - 3]) elif self.__pressedPos == 'right-bottom': self.__perspectiveTrans.setDstPoints( [1, 0], [self.__perspectiveTrans.width - 3, 2], [1, self.__perspectiveTrans.height - 2], [self.__perspectiveTrans.width - 5, self.__perspectiveTrans.height - 4]) elif self.__pressedPos == 'right-top': self.__perspectiveTrans.setDstPoints( [0, 1], [self.__perspectiveTrans.width - 7, 5], [2, self.__perspectiveTrans.height - 1], [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 2]) elif self.__pressedPos == 'right': self.__perspectiveTrans.setDstPoints( [1, 1], [self.__perspectiveTrans.width - 6, 4], [2, self.__perspectiveTrans.height - 1], [self.__perspectiveTrans.width - 4, self.__perspectiveTrans.height - 3]) def __getTransformPixmap(self) -> QPixmap: """ 获取透视变换后的QPixmap """ pix = self.__perspectiveTrans.getPerspectiveTransform( self.__perspectiveTrans.width, self.__perspectiveTrans.height).scaled( self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) return pix def __getScreenShot(self) -> QPixmap: """ 对窗口口所在的桌面区域进行截图 """ screen = QApplication.primaryScreen() # type:QScreen pos = self.mapToGlobal(QPoint(0, 0)) # type:QPoint pix = screen.grabWindow( 0, pos.x(), pos.y(), self.width(), self.height()) return pix def __adjustTransformPix(self): """ 对窗口截图再次进行透视变换并将两张图融合,消除可能存在的黑边 """ self.__screenshotPix = self.__getScreenShot() self.__perspectiveTrans.setPixmap(self.__screenshotPix) self.__screenshotPressedPix = self.__getTransformPixmap() # 融合两张透视图 img_1 = self.__perspectiveTrans.transQPixmapToNdarray(self.__pressedPix) img_2 = self.__perspectiveTrans.transQPixmapToNdarray(self.__screenshotPressedPix) # 去除非透明背景部分 mask = img_1[:, :, -1] == 0 img_2[mask] = img_1[mask] self.__pressedPix = self.__perspectiveTrans.transNdarrayToQPixmap(img_2)
在mousePressEvent
中调用了一个全局函数 getPressedPos(widget,e)
,如果将窗口分为九宫格,它就是用来获取判断鼠标的点击位置落在九宫格的哪个格子的,因为我在其他地方有用到它,所以没将其设置为PerspectiveWidget
的方法成员。下面是这个函数的代码:
# coding:utf-8 from PyQt5.QtGui import QMouseEvent def getPressedPos(widget, e: QMouseEvent) -> str: """ 检测鼠标并返回按下的方位 """ pressedPos = None width = widget.width() height = widget.height() leftX = 0 <= e.x() <= int(width / 3) midX = int(width / 3) < e.x() <= int(width * 2 / 3) rightX = int(width * 2 / 3) < e.x() <= width topY = 0 <= e.y() <= int(height / 3) midY = int(height / 3) < e.y() <= int(height * 2 / 3) bottomY = int(height * 2 / 3) < e.y() <= height # 获取点击位置 if leftX and topY: pressedPos = 'left-top' elif midX and topY: pressedPos = 'top' elif rightX and topY: pressedPos = 'right-top' elif leftX and midY: pressedPos = 'left' elif midX and midY: pressedPos = 'center' elif rightX and midY: pressedPos = 'right' elif leftX and bottomY: pressedPos = 'left-bottom' elif midX and bottomY: pressedPos = 'bottom' elif rightX and bottomY: pressedPos = 'right-bottom' return pressedPos
使用方法
很简单,只要将代码中的QWidget替换为PerspectiveWidget就可以享受透视变换带来的无尽乐趣。要想向gif中那样对按钮也进行透视变换,只要按代码中所做的那样重写mousePressEvent
、mouseReleaseEvent
和 paintEven
t 即可,如果有对按钮使用qss,记得在paintEvent中加上super().paintEvent(e)
,这样样式表才会起作用。总之框架已经给出,具体操作取决于你。如果你喜欢这篇博客的话,记得点个赞哦(o゚▽゚)o 。顺便做个下期预告:在gif中可以看到界面切换时带了弹入弹出的动画,在下一篇博客中我会对如何实现QStackedWidget的界面切换动画进行介绍,敬请期待~~
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 《暗喻幻想》顺风耳作用介绍
- 崔健1985-梦中的倾诉[再版][WAV+CUE]
- 黄子馨《追星Xin的恋人们2》HQ头版限量编号[WAV+CUE]
- 孟庭苇《情人的眼泪》开盘母带[低速原抓WAV+CUE]
- 孙露《谁为我停留HQCD》[低速原抓WAV+CUE][1.1G]
- 孙悦《时光音乐会》纯银CD[低速原抓WAV+CUE][1.1G]
- 任然《渐晚》[FLAC/分轨][72.32MB]
- 英雄联盟新英雄安蓓萨上线了吗 新英雄安蓓萨技能介绍
- 魔兽世界奥杜尔竞速赛什么时候开启 奥杜尔竞速赛开启时间介绍
- 无畏契约CGRS准星代码多少 CGRS准星代码分享一览
- 张靓颖.2012-倾听【少城时代】【WAV+CUE】
- 游鸿明.1999-五月的雪【大宇国际】【WAV+CUE】
- 曹方.2005-遇见我【钛友文化】【WAV+CUE】
- Unity6引擎上线:稳定性提升、CPU性能最高提升4倍
- 人皇Sky今日举行婚礼!电竞传奇步入新篇章