使用 未公开的 SetWindowCompositionAttribute (SWCA) 和公开的 DwmSetWindowAttribute ,
完成的亚克力窗口 Python pyside6 第一步 QT 设置无边框窗口 第二步 win32 设置new_style ,窗口为 1 直角窗口 此时 亚克力效果正常显示 , 但是 关闭亚克力 之后 窗口的 样式 变得 很奇怪, 上部分圆角 下部分 直角 边框 和 阴影 都消失了 , 但是 设置 大圆角 和 小圆角 又显示正常 


目前 我使用的 直角窗口 亚克力 关闭 回退到大圆角 运行一切正常 但是 无法设置窗口直角了 除非 一开始就完全的不启用 亚克力 不然 启用一次 直角窗口的 边框的阴影 就失效 窗口变为图2 
就是无法维持这个边框加阴影的直角样式
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ——————————————————————————————————————————————————————————————————————————————
# 文件名:Acrylic_Control_Final.py
# 功能:PySide6 无边框窗口 + 亚克力开关 + 圆角/直角切换 + 阴影 + 鼠标修复
# 更新:添加了控制按钮和亚克力状态切换逻辑
# ——————————————————————————————————————————————————————————————————————————————
import sys
import ctypes
from ctypes import wintypes, c_int, c_uint, c_bool, byref, sizeof, Structure, POINTER
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QCursor, QPainter, QColor
# ==============================================================================
# 第一部分:Win32 API 底层定义
# ==============================================================================
# --- 常量定义 ---
GWL_STYLE = -16
WS_CAPTION = 0x00C00000
WS_THICKFRAME = 0x00040000
WS_SYSMENU = 0x00080000
WS_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000
SWP_NOSIZE = 0x0001
SWP_NOMOVE = 0x0002
SWP_NOZORDER = 0x0004
SWP_NOACTIVATE = 0x0010
SWP_FRAMECHANGED = 0x0020
SWP_NOOWNERZORDER = 0x0200
# DWM 属性
DWMWA_USE_IMMERSIVE_DARK_MODE = 20
DWMWA_WINDOW_CORNER_PREFERENCE = 33 # Win11 圆角属性 ID
# 圆角枚举值
DWMWCP_DEFAULT = 0
DWMWCP_DONOTROUND = 1 # 直角
DWMWCP_ROUND = 2 # 圆角
DWMWCP_ROUNDSMALL = 3 # 小圆角
WCA_ACCENT_POLICY = 19
ACCENT_DISABLED = 0
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
# --- 结构体定义 ---
class MARGINS(Structure):
_fields_ = [("cxLeftWidth", c_int), ("cxRightWidth", c_int),
("cyTopHeight", c_int), ("cyBottomHeight", c_int)]
class ACCENT_POLICY(Structure):
_fields_ = [("AccentState", c_uint), ("AccentFlags", c_uint),
("GradientColor", c_uint), ("AnimationId", c_uint)]
class WINDOWCOMPOSITIONATTRIBDATA(Structure):
_fields_ = [("Attribute", c_int), ("Data", POINTER(ACCENT_POLICY)), ("SizeOfData", c_uint)]
# --- DLL 绑定 ---
user32 = ctypes.windll.user32
dwmapi = ctypes.windll.dwmapi
SetWindowLongPtrW = user32.SetWindowLongPtrW
SetWindowLongPtrW.argtypes = [wintypes.HWND, c_int, ctypes.c_longlong]
SetWindowLongPtrW.restype = ctypes.c_longlong
GetWindowLongPtrW = user32.GetWindowLongPtrW
GetWindowLongPtrW.argtypes = [wintypes.HWND, c_int]
GetWindowLongPtrW.restype = ctypes.c_longlong
SetWindowPos = user32.SetWindowPos
SetWindowPos.argtypes = [wintypes.HWND, wintypes.HWND, c_int, c_int, c_int, c_int, c_uint]
SetWindowPos.restype = wintypes.BOOL
DwmExtendFrameIntoClientArea = dwmapi.DwmExtendFrameIntoClientArea
DwmExtendFrameIntoClientArea.argtypes = [wintypes.HWND, POINTER(MARGINS)]
DwmExtendFrameIntoClientArea.restype = c_int
DwmSetWindowAttribute = dwmapi.DwmSetWindowAttribute
DwmSetWindowAttribute.argtypes = [wintypes.HWND, c_int, ctypes.c_void_p, c_int]
DwmSetWindowAttribute.restype = c_int
SetWindowCompositionAttribute = user32.SetWindowCompositionAttribute
SetWindowCompositionAttribute.argtypes = [c_int, POINTER(WINDOWCOMPOSITIONATTRIBDATA)]
SetWindowCompositionAttribute.restype = c_bool
# ==============================================================================
# 第二部分:Win32 功能封装 (添加了关闭和圆角设置)
# ==============================================================================
def 设置亚克力状态(hwnd: int, enable: bool, hex_color: str = "CC202020"):
""" 开关亚克力效果 """
hwnd = int(hwnd)
# 扩展边框 (无论开关都需要保证框架完整)
margins = MARGINS(-1, -1, -1, -1)
DwmExtendFrameIntoClientArea(hwnd, byref(margins))
accent = ACCENT_POLICY()
if enable:
# 开启
try:
gradient_col = int(hex_color, 16)
except ValueError:
gradient_col = 0xCC202020
accent.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND
accent.AccentFlags = 0x20 | 0x40 | 0x80 | 0x100
accent.GradientColor = gradient_col
else:
# 关闭 (设置为 DISABLED)
accent.AccentState = ACCENT_DISABLED
accent.AccentFlags = 0
accent.GradientColor = 0
data = WINDOWCOMPOSITIONATTRIBDATA()
data.Attribute = WCA_ACCENT_POLICY
data.SizeOfData = sizeof(accent)
data.Data = ctypes.cast(ctypes.pointer(accent), POINTER(ACCENT_POLICY))
SetWindowCompositionAttribute(hwnd, byref(data))
def 设置窗口圆角(hwnd: int, mode_id: int):
"""
mode_id:
1 = DWMWCP_DONOTROUND (直角)
2 = DWMWCP_ROUND (圆角)
3 = DWMWCP_ROUNDSMALL (小圆角)
"""
hwnd = int(hwnd)
val = c_int(mode_id)
DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, byref(val), sizeof(val))
def 窗口框架子类化(hwnd):
""" 初始化窗口样式以支持阴影 """
hwnd = int(hwnd)
old_style = GetWindowLongPtrW(hwnd, GWL_STYLE)
new_style = (old_style | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU)
SetWindowLongPtrW(hwnd, GWL_STYLE, new_style)
# 开启暗色模式标题栏
dark_mode = c_int(1)
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, byref(dark_mode), sizeof(dark_mode))
SetWindowPos(hwnd, None, -1, -1, -1, -1,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE |
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED)
# ==============================================================================
# 第三部分:Qt 主窗口类
# ==============================================================================
class ControlWindow(QWidget):
全局边框宽度 = 5
def __init__(self):
super().__init__()
self.hwnd = None
self.亚克力开启中 = True # 状态标志位
self.resize(1000, 600)
self.setWindowTitle("控制面板")
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setup_ui()
def setup_ui(self):
# 使用垂直布局
layout = QVBoxLayout(self)
# 【关键】设置上边距为 80,避开顶部 60px 的标题栏区域
# 否则按钮会被 nativeEvent 判定为标题栏,导致点击变成拖动
layout.setContentsMargins(50, 80, 50, 50)
# 信息标签
self.label_status = QLabel("当前状态:亚克力开启 | 圆角模式")
self.label_status.setStyleSheet("color: white; font-size: 18px; font-weight: bold; background: transparent;")
self.label_status.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.label_status)
layout.addSpacing(20)
# --- 按钮区域 ---
btn_layout = QHBoxLayout()
# 按钮样式
btn_style = """
QPushButton {
background-color: rgba(255, 255, 255, 0.15);
color: white;
border: 1px solid rgba(255,255,255,0.4);
padding: 12px;
border-radius: 6px;
font-size: 14px;
}
QPushButton:hover { background-color: rgba(255, 255, 255, 0.3); }
QPushButton:pressed { background-color: rgba(255, 255, 255, 0.1); }
"""
# 1. 开关亚克力按钮
self.btn_acrylic = QPushButton("切换亚克力 (开/关)")
self.btn_acrylic.setStyleSheet(btn_style)
self.btn_acrylic.clicked.connect(self.切换亚克力模式)
btn_layout.addWidget(self.btn_acrylic)
# 2. 圆角按钮
btn_round = QPushButton("设置圆角 (Win11)")
btn_round.setStyleSheet(btn_style)
btn_round.clicked.connect(lambda: self.设置圆角模式("round"))
btn_layout.addWidget(btn_round)
# 3. 直角按钮
btn_rect = QPushButton("设置直角 (Rect)")
btn_rect.setStyleSheet(btn_style)
btn_rect.clicked.connect(lambda: self.设置圆角模式("rect"))
btn_layout.addWidget(btn_rect)
layout.addLayout(btn_layout)
# 底部关闭按钮
layout.addStretch()
btn_close = QPushButton("关闭程序")
btn_close.setStyleSheet("QPushButton { background-color: #d84343; color: white; border-radius: 5px; padding: 10px; font-weight: bold; } QPushButton:hover { background-color: #ff5555; }")
btn_close.clicked.connect(self.close)
layout.addWidget(btn_close)
def showEvent(self, event):
super().showEvent(event)
if not self.hwnd:
self.hwnd = int(self.winId())
QTimer.singleShot(50, self.初始化Win32效果)
def 初始化Win32效果(self):
if not self.hwnd: return
窗口框架子类化(self.hwnd)
# 默认开启
设置亚克力状态(self.hwnd, True, "CC202020")
# 默认圆角
设置窗口圆角(self.hwnd, DWMWCP_ROUND)
# --- 槽函数 ---
def 切换亚克力模式(self):
if not self.hwnd: return
self.亚克力开启中 = not self.亚克力开启中
if self.亚克力开启中:
# 开启:设置亚克力 API,更新状态文字
设置亚克力状态(self.hwnd, True, "CC202020")
self.label_status.setText("当前状态:亚克力开启")
else:
# 关闭:关闭亚克力 API
设置亚克力状态(self.hwnd, False)
self.label_status.setText("当前状态:亚克力关闭 (实色背景)")
# 强制重绘,这一步会触发 paintEvent
# paintEvent 会根据 self.亚克力开启中 决定画透明还是画实色
self.update()
def 设置圆角模式(self, mode):
if not self.hwnd: return
if mode == "rect":
设置窗口圆角(self.hwnd, DWMWCP_DONOTROUND) # 直角
self.label_status.setText(self.label_status.text().split("|")[0] + " | 直角模式")
else:
设置窗口圆角(self.hwnd, DWMWCP_ROUND) # 圆角
self.label_status.setText(self.label_status.text().split("|")[0] + " | 圆角模式")
def paintEvent(self, event):
"""
核心绘制逻辑
"""
painter = QPainter(self)
if self.亚克力开启中:
# 【亚克力模式】
# 绘制几乎透明的像素 (Alpha=1),让 Windows 拦截鼠标,同时展示背后的模糊
painter.fillRect(self.rect(), QColor(0, 0, 0, 1))
else:
# 【关闭模式】
# 必须绘制一个不透明(或半透明)的实色背景。
# 因为我们开启了 WA_TranslucentBackground,如果不画背景,关闭亚克力后窗口就是完全透明(隐形)的。
# 这里画一个深灰色背景,模拟普通窗口
painter.fillRect(self.rect(), QColor(30, 30, 30, 255))
def nativeEvent(self, eventType, message):
""" Windows 消息处理 """
try:
msg = wintypes.MSG.from_address(int(message))
if msg.message == 0x0084: # WM_NCHITTEST
p = self.mapFromGlobal(QCursor.pos())
x, y = p.x(), p.y()
w, h = self.width(), self.height()
b = self.全局边框宽度
# 边缘缩放判定
if x <= b and y <= b: return True, 13
if x >= w - b and y <= b: return True, 14
if x <= b and y >= h - b: return True, 16
if x >= w - b and y >= h - b: return True, 17
if y <= b: return True, 12
if y >= h - b: return True, 15
if x <= b: return True, 10
if x >= w - b: return True, 11
# 标题栏判定 (Top 60px)
# 注意:因为我们的 UI 内容区 margin top 设为了 80,
# 所以 60px 以内的点击不会点到按钮,放心返回 HTCAPTION (拖拽)
if y <= 60:
return True, 2
return True, 1 # HTCLIENT
except Exception:
pass
return super().nativeEvent(eventType, message)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ControlWindow()
window.show()
sys.exit(app.exec())