# coding:utf-8 import warnings from typing import Union from PySide6.QtCore import Qt, QThread, Signal, QRect from PySide6.QtGui import QBrush, QColor, QImage, QPainter, QPixmap, QPainterPath from PySide6.QtWidgets import QLabel, QApplication, QWidget from ...common.screen import getCurrentScreen try: from ...common.image_utils import gaussianBlur isAcrylicAvailable = True except ImportError as e: isAcrylicAvailable = False def gaussianBlur(imagePath, blurRadius=18, brightFactor=1, blurPicSize=None): return QPixmap(imagePath) def checkAcrylicAvailability(): if not isAcrylicAvailable: warnings.warn( 'Acrylic is not supported in current qfluentwidgets, use `pip install PySide6-Fluent-Widgets[full]` to enable it.') return isAcrylicAvailable class BlurCoverThread(QThread): """ Blur album cover thread """ blurFinished = Signal(QPixmap) def __init__(self, parent=None): super().__init__(parent) self.imagePath = "" self.blurRadius = 7 self.maxSize = None def run(self): if not self.imagePath: return pixmap = gaussianBlur( self.imagePath, self.blurRadius, 0.85, self.maxSize) self.blurFinished.emit(pixmap) def blur(self, imagePath: str, blurRadius=6, maxSize: tuple = (450, 450)): self.imagePath = imagePath self.blurRadius = blurRadius self.maxSize = maxSize or self.maxSize self.start() class AcrylicTextureLabel(QLabel): """ Acrylic texture label """ def __init__(self, tintColor: QColor, luminosityColor: QColor, noiseOpacity=0.03, parent=None): """ Parameters ---------- tintColor: QColor RGB tint color luminosityColor: QColor luminosity layer color noiseOpacity: float noise layer opacity parent: parent window """ super().__init__(parent=parent) self.tintColor = QColor(tintColor) self.luminosityColor = QColor(luminosityColor) self.noiseOpacity = noiseOpacity self.noiseImage = QImage(':/qfluentwidgets/images/acrylic/noise.png') self.setAttribute(Qt.WA_TranslucentBackground) def setTintColor(self, color: QColor): self.tintColor = color self.update() def paintEvent(self, e): acrylicTexture = QImage(64, 64, QImage.Format_ARGB32_Premultiplied) # paint luminosity layer acrylicTexture.fill(self.luminosityColor) # paint tint color painter = QPainter(acrylicTexture) painter.fillRect(acrylicTexture.rect(), self.tintColor) # paint noise painter.setOpacity(self.noiseOpacity) painter.drawImage(acrylicTexture.rect(), self.noiseImage) acrylicBrush = QBrush(acrylicTexture) painter = QPainter(self) painter.fillRect(self.rect(), acrylicBrush) class AcrylicLabel(QLabel): """ Acrylic label """ def __init__(self, blurRadius: int, tintColor: QColor, luminosityColor=QColor(255, 255, 255, 0), maxBlurSize: tuple = None, parent=None): """ Parameters ---------- blurRadius: int blur radius tintColor: QColor tint color luminosityColor: QColor luminosity layer color maxBlurSize: tuple maximum image size parent: parent window """ super().__init__(parent=parent) checkAcrylicAvailability() self.imagePath = '' self.blurPixmap = QPixmap() self.blurRadius = blurRadius self.maxBlurSize = maxBlurSize self.acrylicTextureLabel = AcrylicTextureLabel( tintColor, luminosityColor, parent=self) self.blurThread = BlurCoverThread(self) self.blurThread.blurFinished.connect(self.__onBlurFinished) def __onBlurFinished(self, blurPixmap: QPixmap): """ blur finished slot """ self.blurPixmap = blurPixmap self.setPixmap(self.blurPixmap) self.adjustSize() def setImage(self, imagePath: str): """ set the image to be blurred """ self.imagePath = imagePath self.blurThread.blur(imagePath, self.blurRadius, self.maxBlurSize) def setTintColor(self, color: QColor): self.acrylicTextureLabel.setTintColor(color) def resizeEvent(self, e): super().resizeEvent(e) self.acrylicTextureLabel.resize(self.size()) if not self.blurPixmap.isNull() and self.blurPixmap.size() != self.size(): self.setPixmap(self.blurPixmap.scaled( self.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation)) class AcrylicBrush: """ Acrylic brush """ def __init__(self, device: QWidget, blurRadius: int, tintColor=QColor(242, 242, 242, 150), luminosityColor=QColor(255, 255, 255, 10), noiseOpacity=0.03): self.device = device self.blurRadius = blurRadius self.tintColor = QColor(tintColor) self.luminosityColor = QColor(luminosityColor) self.noiseOpacity = noiseOpacity self.noiseImage = QImage(':/qfluentwidgets/images/acrylic/noise.png') self.originalImage = QPixmap() self.image = QPixmap() self.clipPath = QPainterPath() def setBlurRadius(self, radius: int): if radius == self.blurRadius: return self.blurRadius = radius self.setImage(self.originalImage) def setTintColor(self, color: QColor): self.tintColor = QColor(color) self.device.update() def setLuminosityColor(self, color: QColor): self.luminosityColor = QColor(color) self.device.update() def isAvailable(self): return isAcrylicAvailable def grabImage(self, rect: QRect): """ grab image from screen Parameters ---------- rect: QRect grabbed region """ screen = getCurrentScreen() if not screen: screen = QApplication.screens()[0] x, y, w, h = rect.x(), rect.y(), rect.width(), rect.height() x -= screen.geometry().x() y -= screen.geometry().y() self.setImage(screen.grabWindow(0, x, y, w, h)) def setImage(self, image: Union[str, QImage, QPixmap]): """ set blurred image """ if isinstance(image, str): image = QPixmap(image) elif isinstance(image, QImage): image = QPixmap.fromImage(image) self.originalImage = image if not image.isNull(): checkAcrylicAvailability() self.image = gaussianBlur(image, self.blurRadius) self.device.update() def setClipPath(self, path: QPainterPath): self.clipPath = path self.device.update() def textureImage(self): texture = QImage(64, 64, QImage.Format_ARGB32_Premultiplied) texture.fill(self.luminosityColor) # paint tint color painter = QPainter(texture) painter.fillRect(texture.rect(), self.tintColor) # paint noise painter.setOpacity(self.noiseOpacity) painter.drawImage(texture.rect(), self.noiseImage) return texture def paint(self): device = self.device painter = QPainter(device) painter.setRenderHints(QPainter.Antialiasing) if not self.clipPath.isEmpty(): painter.setClipPath(self.clipPath) # paint image image = self.image.scaled(device.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation) painter.drawPixmap(0, 0, image) # paint acrylic texture painter.fillRect(device.rect(), QBrush(self.textureImage()))