调用SetWindowCompositionAttribute实现亚克力效果时的窗口圆角方案问询
解决亚克力效果与圆角窗口兼容的问题
我之前也踩过这个坑!核心原因是亚克力效果是由DWM(桌面窗口管理器)直接渲染在窗口底层的,SetWindowRgn只能裁剪窗口的客户区控件内容,根本碰不到DWM绘制的亚克力背景层,所以才会出现圆角遮不住亚克力的情况。下面给你几个经过实际验证的可行思路:
方案一:用系统原生DWM圆角API(推荐)
Windows 10 1809及以上版本提供了原生的圆角窗口支持,通过DwmSetWindowAttribute设置DWMWA_WINDOW_CORNER_PREFERENCE属性,就能让系统自动给窗口加圆角,而且亚克力效果会完美适配圆角裁剪,完全不用自己处理遮罩逻辑。
在PyQt中可以这么实现:
import win32api import win32con from PyQt5.QtWidgets import QMainWindow from PyQt5.QtCore import Qt class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) self.resize(500, 500) # 调用DWM设置圆角 hwnd = int(self.winId()) # 定义需要的常量(如果win32con未包含) DWMWA_WINDOW_CORNER_PREFERENCE = 33 DWMWCP_ROUND = 2 # 完全圆角,还有DWMWCP_ROUNDSMALL等选项 # 先移除标题栏和边框(按需操作) current_style = win32api.GetWindowLongPtr(hwnd, win32con.GWL_STYLE) new_style = current_style & ~win32con.WS_CAPTION & ~win32con.WS_THICKFRAME win32api.SetWindowLongPtr(hwnd, win32con.GWL_STYLE, new_style) # 设置圆角偏好 win32api.DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, DWMWCP_ROUND, 4)
这个方案的优势是系统原生支持,适配高DPI、多显示器都很友好,而且代码量最少。
方案二:手动模拟亚克力+Qt遮罩(兼容旧系统)
如果需要兼容Windows 10 1809以下版本,或者要自定义圆角大小,可以放弃系统原生的亚克力,改用DwmEnableBlurBehindWindow实现模糊背景,再配合Qt的setMask做圆角裁剪,最后自己绘制半透明背景模拟亚克力质感:
from PyQt5.QtWidgets import QWidget from PyQt5.QtGui import QPainter, QPainterPath, QRegion, QColor from PyQt5.QtCore import Qt import win32api import win32con class AcrylicRoundWindow(QWidget): def __init__(self): super().__init__() self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.resize(500, 500) # 启用窗口背景模糊 hwnd = int(self.winId()) class DWM_BLURBEHIND(win32api.Structure): _fields_ = [("dwFlags", win32con.DWORD), ("fEnable", win32con.BOOL), ("hRgnBlur", win32con.HRGN), ("fTransitionOnMaximized", win32con.BOOL)] blur_behind = DWM_BLURBEHIND() blur_behind.dwFlags = 1 # DWM_BB_ENABLE blur_behind.fEnable = True blur_behind.hRgnBlur = 0 win32api.DwmEnableBlurBehindWindow(hwnd, blur_behind) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 创建圆角路径 round_path = QPainterPath() round_path.addRoundedRect(self.rect(), 50, 50) # 自定义圆角半径 # 设置窗口遮罩,裁剪出圆角 self.setMask(QRegion(round_path.toFillPolygon().toPolygon())) # 绘制半透明背景模拟亚克力效果 painter.fillPath(round_path, QColor(240, 240, 245, 190)) # 颜色和透明度可调整
这个方案虽然需要自己处理背景绘制,但兼容性更好,而且圆角大小完全可控。
方案三:用Qt原生的亚克力支持(Qt 5.14+)
Qt 5.14及以上版本提供了Qt.WinAcrylicWindow窗口组合提示,结合WA_TranslucentBackground和setMask可以实现圆角亚克力窗口:
from PyQt5.QtWidgets import QWidget from PyQt5.QtGui import QPainterPath, QRegion from PyQt5.QtCore import Qt class AcrylicRoundWindow(QWidget): def __init__(self): super().__init__() self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) # 启用Qt原生亚克力效果 self.setWindowCompositionHint(Qt.WinAcrylicWindow) self.resize(500, 500) # 设置圆角遮罩 round_path = QPainterPath() round_path.addRoundedRect(self.rect(), 50, 50) self.setMask(QRegion(round_path.toFillPolygon().toPolygon()))
注意:这个方法需要确保Qt版本符合要求,而且部分环境下可能需要调整窗口背景色来匹配亚克力的视觉效果。
内容的提问来源于stack exchange,提问作者zhiyi




