news 2026/10/14 1:41:03

PyQt5/PyQt6 实现 Windows 亚克力效果:原理、代码与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyQt5/PyQt6 实现 Windows 亚克力效果:原理、代码与避坑指南

1. 为什么要在 PyQt 里折腾亚克力效果

做桌面端工具超过五六年之后,我越来越在意一件事:功能跑通只是及格线,界面能不能让人愿意多打开几次,才是决定一个小工具能不能活下来的关键。Windows 10 从 1803 版本开始引入的 Fluent Design 体系里,亚克力(Acrylic)材质算是辨识度最高的一个视觉元素——半透明的磨砂背景、背后隐约透出的桌面壁纸、边缘一层极细的高光描边,整体观感比传统的纯色窗口高级不少。很多用 PyQt 写工具的朋友,第一反应是去搜"PyQt 亚克力",然后发现资料零散、版本对不上、跑起来要么全黑要么直接崩,最后放弃。

这篇内容就是把我自己踩过的路完整梳理一遍。核心要解决的问题很明确:在 PyQt5 / PyQt6 里,如何让一个普通窗口获得接近 Win10 原生应用的亚克力背景效果。适合两类人看——一类是已经能用 PyQt 写出完整界面、想再往上抬一档视觉质感的开发者;另一类是刚接触桌面 GUI、想拿这个效果当练手项目的朋友。我会把原理、选型、代码、坑点全部摊开讲,尤其是那些官方文档里不会写、只有真正跑过一遍才知道的细节。

先说结论,避免你看到一半才发现方向不对:PyQt 本身没有提供亚克力 API,这个效果必须借助 Windows 系统层面的接口来实现。主流做法有三条路——调用未公开的SetWindowCompositionAttribute、使用 DwmExtendFrameIntoClientArea 配合系统背景、以及走 Win11 才原生支持的DWMWA_SYSTEMBACKDROP_TYPE。三条路各有各的适用边界,选错了就是白忙活。下面我会一条条拆。

2. 亚克力效果的底层原理与三条实现路线对比

2.1 亚克力到底是什么,和普通半透明有什么区别

很多人把亚克力和"窗口设置成半透明"混为一谈,其实差别很大。普通的半透明窗口(比如setWindowOpacity(0.8))是把整个窗口连同里面的控件一起做透明度混合,文字和按钮也会跟着变淡,看起来发虚。而亚克力是只对窗口背景做模糊和着色,前景内容保持完全不透明。它背后其实做了三件事:对窗口后方的屏幕内容做高斯模糊、叠加一层带色调的半透明色板、再根据窗口是否处于激活状态调整亮度和饱和度。

Windows 系统里负责这套合成的是桌面窗口管理器(DWM)。应用程序没法直接命令 DWM"给我加个模糊",只能通过特定的窗口属性去"申请"。这就是为什么纯 PyQt 代码做不到,必须调用系统接口。

理解这一点很关键:亚克力不是画出来的,是申请来的。你的窗口只是告诉系统"我希望背景用这种材质",具体渲染由 DWM 完成。这也解释了为什么有些实现方案在窗口失焦后效果会变——因为系统会根据激活状态自动调整材质参数,这是设计行为,不是 bug。

2.2 三条实现路线的适用边界

我把三条路线整理成一张表,方便你按自己的环境对号入座:

方案依赖接口适用系统效果完整度稳定性
SetWindowCompositionAttribute未公开 APIWin10 1803+高,可调色调中,接口未公开
DwmExtendFrameIntoClientArea公开 DWM APIWin7+中,偏毛玻璃高
DWMWA_SYSTEMBACKDROP_TYPE公开 DWM APIWin11 22H2+最高,原生材质高

第一条路SetWindowCompositionAttribute是社区里流传最广的方案,它来自user32.dll里一个没有正式文档的导出函数。通过它传入ACCENT_ENABLE_ACRYLICBLURBEHIND标志,就能让系统给窗口加上亚克力。优点是 Win10 上就能用,还能自定义色调;缺点是接口未公开,微软随时可能改,而且不同小版本行为有差异。

第二条路DwmExtendFrameIntoClientArea是公开 API,原理是把窗口边框延伸到整个客户区,让 DWM 的毛玻璃透出来。它在 Win7 的 Aero 时代就有了,兼容性最好,但效果偏"毛玻璃"而不是现代亚克力,色调控制也弱。

第三条路是 Win11 22H2 之后新增的DWMWA_SYSTEMBACKDROP_TYPE,直接告诉系统用哪种背景材质(Mica、Acrylic、Tabbed 等)。这是最"正统"的做法,效果和系统原生应用完全一致,但只能在较新的 Win11 上用。

我的建议是:如果你的目标用户主要在 Win10,走第一条;如果只考虑 Win11,走第三条;想要最大兼容性且能接受效果打折,走第二条。实际项目里我通常做成运行时探测系统版本,自动选择方案,后面会给代码。

2.3 为什么不用现成的第三方库

搜这个话题时你会看到一些封装好的库,比如某些专门做窗口特效的包。我试过几个,结论是:小项目直接用,大项目慎用。原因有三。一是这些库大多也是包装上面那套系统调用,多一层依赖就多一个版本兼容的雷;二是它们往往把窗口样式写死,你想微调色调、圆角、边框就得去改源码;三是 PyQt 本身版本迭代快,第三方库跟进不及时,很容易出现"库支持 PyQt5 但你的项目已经迁到 PyQt6"的尴尬。

自己写这几十行调用代码,反而更可控。而且理解了原理之后,出问题你能自己定位,不用等别人更新。这也是我写这篇内容的初衷——把黑盒拆开给你看。

3. 用 ctypes 调用系统接口的完整实现

3.1 环境准备与依赖确认

动手之前先把环境理清楚。你需要的是 Python 3.7 以上、PyQt5 或 PyQt6 任意一个、以及 Windows 10 1803 以上的系统。ctypes是 Python 标准库,不用额外装。检查系统版本可以用platform模块:

import platform import sys def get_windows_build(): if sys.platform != "win32": return 0 ver = platform.version() # 形如 '10.0.19045' try: return int(ver.split(".")[-1]) except (ValueError, IndexError): return 0

这里有个容易忽略的点:platform.version()返回的第三段才是 build 号。Win10 1803 对应 build 17134,1809 是 17763,Win11 21H2 是 22000,22H2 是 22621。判断亚克力是否可用,至少要 build >= 17134。我见过有人用platform.release()判断,那个只返回 "10" 或 "11",粒度太粗,判断不了小版本,别踩这个坑。

PyQt 版本方面,PyQt5 和 PyQt6 在窗口句柄获取上写法不同,这是迁移时最容易翻车的地方。PyQt5 用winId()返回的是sip.voidptr,PyQt6 返回的是int。传给 ctypes 之前都要转成整数:

hwnd = int(self.winId())

PyQt5 里int()能直接吃sip.voidptr,PyQt6 里本身就是 int,两种写法都兼容。但如果你在 PyQt5 里忘了转,直接把winId()丢给 ctypes,会报类型错误,这个报错信息还特别不直观,我第一次遇到时查了半天。

3.2 SetWindowCompositionAttribute 的结构体定义

这条路的核心是构造正确的结构体。SetWindowCompositionAttribute接收两个参数:窗口句柄和一个指向WINDOWCOMPOSITIONATTRIBDATA的指针。这个结构体里又嵌套了ACCENTPOLICY。用 ctypes 定义如下:

import ctypes from ctypes import wintypes class ACCENTPOLICY(ctypes.Structure): _fields_ = [ ("AccentState", ctypes.c_uint), ("AccentFlags", ctypes.c_uint), ("GradientColor", ctypes.c_uint), ("AnimationId", ctypes.c_uint), ] class WINDOWCOMPOSITIONATTRIBDATA(ctypes.Structure): _fields_ = [ ("Attribute", ctypes.c_uint), ("Data", ctypes.POINTER(ACCENTPOLICY)), ("SizeOfData", ctypes.c_size_t), ]

几个关键常量的取值必须记准,写错了效果就是全黑或者没反应:

  • WCA_ACCENT_POLICY = 19,这是属性编号,固定值。
  • ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,亚克力标志。
  • ACCENT_ENABLE_BLURBEHIND = 3,普通模糊,效果弱一些但更稳。
  • ACCENT_ENABLE_HOSTBACKDROP = 5,Win11 上更接近原生。

GradientColor这个字段名字有迷惑性,它其实是个 ABGR 格式的颜色值,不是 RGB。也就是说,如果你想要一个偏深灰的色调,得按0xAABBGGRR的顺序拼。比如想要 60% 不透明的深灰#202020,计算方式是:alpha=0x99,B=0x20,G=0x20,R=0x20,拼起来就是0x99202020。这个字节序问题坑过无数人,包括我,第一次调出来的颜色和预期完全对不上,排查了好久才反应过来是 ABGR 不是 RGBA。

3.3 完整的应用函数与调用时机

把结构体拼好之后,调用逻辑本身很短:

def enable_acrylic(hwnd, tint=0x99202020): user32 = ctypes.windll.user32 set_attr = user32.SetWindowCompositionAttribute set_attr.argtypes = [ wintypes.HWND, ctypes.POINTER(WINDOWCOMPOSITIONATTRIBDATA), ] set_attr.restype = ctypes.c_int accent = ACCENTPOLICY() accent.AccentState = 4 # ACCENT_ENABLE_ACRYLICBLURBEHIND accent.AccentFlags = 0x20 | 0x40 | 0x80 | 0x100 accent.GradientColor = tint accent.AnimationId = 0 data = WINDOWCOMPOSITIONATTRIBDATA() data.Attribute = 19 data.Data = ctypes.pointer(accent) data.SizeOfData = ctypes.sizeof(accent) set_attr(hwnd, ctypes.byref(data))

AccentFlags那几个位标志控制的是边框和阴影的绘制方式,0x20 | 0x40 | 0x80 | 0x100这组值能让窗口四边都带上细微的高光描边,视觉上更接近原生。如果你不想要边框,把它设成 0 就行,但实测下来没有边框的亚克力窗口会显得"糊"在桌面上,缺少层次感,建议保留。

调用时机是另一个大坑。必须在窗口真正创建、拿到有效句柄之后调用,在__init__里直接调往往无效,因为那时窗口还没被系统创建。正确的位置是重写showEvent,并且加一个标志位防止重复调用:

def showEvent(self, event): super().showEvent(event) if not self._acrylic_applied: enable_acrylic(int(self.winId())) self._acrylic_applied = True

为什么用标志位?因为showEvent在窗口每次显示时都会触发,最小化再恢复也会触发。重复调用虽然一般不会崩,但会造成不必要的系统调用,而且在某些系统版本上重复设置会导致背景闪烁。加个标志位是最省心的做法。

4. 让亚克力窗口真正好用的几个关键处理

4.1 无边框窗口与拖动、缩放的取舍

亚克力效果要好看,通常得配合无边框窗口。因为系统默认的标题栏是纯色的,压在亚克力背景上会显得割裂。设置无边框很简单:

self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground)

但这两行一加,问题就来了:窗口不能拖动、不能缩放、没有关闭按钮。这是新手最容易卡住的地方。我的处理方式是自定义一个标题栏控件,用鼠标事件手动实现拖动:

def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self._drag_pos = event.globalPos() - self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton and self._drag_pos: self.move(event.globalPos() - self._drag_pos) event.accept()

这里有个细节:event.globalPos()在 PyQt6 里改成了event.globalPosition().toPoint(),迁移时要注意。另外拖动逻辑最好只绑定在标题栏区域,不要绑在整个窗口上,否则你在窗口里点按钮、选文字时都会触发拖动,体验很糟。我的做法是把拖动逻辑封装进自定义标题栏类,主窗口不处理。

缩放比拖动麻烦一些,需要判断鼠标是否在窗口边缘、然后手动改resize。如果项目对缩放要求不高,我建议直接setFixedSize固定窗口大小,省掉一大堆边界判断的代码。很多工具类应用其实不需要用户随意缩放,固定尺寸反而让布局更可控。

4.2 圆角、阴影与内容区的视觉分层

原生亚克力窗口是带圆角的,但无边框窗口默认是直角。想要圆角,得用setMask配合QPainterPath:

from PyQt5.QtGui import QPainterPath, QRegion from PyQt5.QtCore import QRectF path = QPainterPath() path.addRoundedRect(QRectF(self.rect()), 12, 12) self.setMask(QRegion(path.toFillPolygon().toPolygon()))

半径我一般用 8 到 12 像素,太小看不出圆角,太大又显得不够克制。但setMask有个副作用:它会把窗口裁成硬边缘,抗锯齿效果一般,圆角处可能有轻微锯齿。如果对边缘质量要求高,可以改用WA_TranslucentBackground配合在paintEvent里自绘圆角背景,代价是要自己处理背景绘制逻辑,复杂度上升不少。

阴影方面,无边框窗口默认没有系统阴影。想要阴影可以用QGraphicsDropShadowEffect,但注意它作用在窗口内部的控件上,不是窗口本身。真正给窗口加系统阴影需要额外调用 DWM 接口,或者干脆留一圈透明边距、在内部画一个带阴影的容器。我通常的做法是:窗口整体留 10 像素透明边距,内部放一个圆角容器承载所有内容,阴影画在容器上。这样视觉层次最自然。

内容区的分层也很重要。亚克力背景本身是半透明的,如果所有控件都直接铺在上面,文字可读性会很差。我的经验是:在亚克力背景之上再叠一层半透明的深色或浅色面板,把主要内容和背景隔开。面板透明度控制在 30% 到 50% 之间,既能透出背景的质感,又保证文字清晰。这一步做不做,成品观感差距非常大。

4.3 失焦状态下的效果变化与手动接管

前面提过,系统会根据窗口激活状态自动调整亚克力参数。窗口失焦时,背景会变得更暗、饱和度更低。这个行为在原生应用里是设计好的,但在你自己的工具里可能不想要——比如你希望窗口一直保持同样的亮度。

想接管这个行为,可以在changeEvent里监听激活状态变化,失焦时重新调用一次enable_acrylic并传入固定的色调:

def changeEvent(self, event): if event.type() == QEvent.ActivationChange: if self.isActiveWindow(): enable_acrylic(int(self.winId()), tint=0x99202020) else: enable_acrylic(int(self.winId()), tint=0x99202020) super().changeEvent(event)

上面两个分支传了同样的色调,意思就是"不管激活与否都用这个颜色"。如果你想要失焦时稍微暗一点,把 else 分支的 alpha 调高即可。这个技巧在需要窗口常驻显示的工具里特别有用,比如悬浮的监控面板。

不过要注意,频繁在changeEvent里调用系统接口,在低配机器上可能造成轻微卡顿。如果发现切换窗口时背景有闪烁,可以加个节流,或者只在色调确实需要变化时才调用。

5. 实测中踩过的坑与排查思路

5.1 背景全黑:最常见也最好排查

跑起来背景一片漆黑,是这条路线上最高频的问题。原因通常有三个,按概率排序:一是AccentState值写错,比如写成了 3(普通模糊)在某些系统上表现就是黑;二是GradientColor的 alpha 设成了 0,等于完全透明,系统可能渲染成黑;三是窗口没有设置WA_TranslucentBackground,导致系统绘制的背景被窗口自己的不透明背景盖住。

排查顺序我建议这样:先确认WA_TranslucentBackground设了,再打印一下GradientColor的值确认 alpha 不为 0,最后检查AccentState是不是 4。这三步走完,九成的全黑问题都能解决。如果还是黑,那可能是系统版本太低,build 号不够,回去查get_windows_build()的返回值。

还有一个隐蔽的情况:如果你在__init__里就调用了enable_acrylic,而此时窗口还没 show,句柄可能无效,调用会静默失败,然后窗口显示出来就是黑的。这就是前面强调必须在showEvent里调用的原因。

5.2 拖动时背景闪烁或残影

窗口拖动过程中背景闪烁、或者移动后留下残影,这个问题困扰了我很久。根因是亚克力背景的渲染和窗口移动的刷新不同步。系统在窗口移动时会重新计算背景模糊,如果你的窗口同时在做重绘(比如有动画、有定时刷新),两者就会打架。

缓解办法有几个。一是拖动时暂停不必要的重绘,比如停掉定时器;二是给窗口设置WA_StaticContents属性,减少无效重绘;三是如果用了setMask做圆角,拖动时残影会更明显,可以考虑拖动期间临时取消 mask,停下后再加回来。最后这个技巧有点 hack,但实测有效。

另外,如果你在拖动逻辑里用了move()而不是直接改窗口位置,每次 move 都会触发一次完整的窗口重绘,闪烁会更严重。用event.globalPos()计算偏移再 move 是标准做法,但要注意别在 move 里再触发其他重绘逻辑,形成连锁。

5.3 多显示器与高 DPI 下的坐标错乱

在多显示器环境或者系统缩放不是 100% 时,亚克力窗口经常出现位置偏移、大小不对的问题。这是因为 Qt 的逻辑坐标和 Windows 的物理像素之间有个缩放系数。PyQt5 默认可能没开启高 DPI 支持,需要手动设置:

QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

这两行必须在创建QApplication之前调用,放在之后无效。PyQt6 里高 DPI 缩放默认开启,不用手动设,但如果你从 PyQt5 迁移过来还留着这两行,会报属性不存在的警告,记得删掉。

多显示器方面,winId()拿到的句柄是当前窗口所在的显示器上下文,跨屏拖动时一般不用特殊处理。但如果你的窗口有自定义的鼠标坐标计算(比如拖动逻辑),要用globalPos而不是pos,否则跨屏时会跳。这个坑我在一个双屏项目里踩过,窗口从主屏拖到副屏时位置会突然偏移一大截,查了半天才发现是坐标参照系的问题。

5.4 打包成 exe 后的行为差异

开发时跑得好好的,用打包工具打成 exe 之后亚克力失效,这也是常见情况。原因通常是打包工具把某些系统 DLL 的调用路径改了,或者打包时的 Python 环境和你开发环境不一致。排查方法是:先确认打包后的 exe 在目标机器上的系统版本够不够,再确认 ctypes 调用有没有抛异常(打包后异常可能被吞掉,加个 try-except 打印出来)。

我的经验是,ctypes.windll.user32这种调用在打包后一般没问题,但如果用了ctypes.WinDLL加载特定路径的 DLL,打包后路径会变。统一用windll走系统搜索路径最稳。另外打包时建议把 Python 版本和开发环境对齐,避免 ctypes 结构体对齐方式在不同 Python 版本上有差异。

6. 跨版本兼容的封装思路与代码组织

6.1 运行时探测与方案自动选择

把三条路线封装成一个统一入口,根据系统版本自动选,是实际项目里最省心的做法。我的封装思路是这样:

def apply_backdrop(window): build = get_windows_build() hwnd = int(window.winId()) if build >= 22621: # Win11 22H2+,优先用系统背景材质 if try_system_backdrop(hwnd): return "system" if build >= 17134: # Win10 1803+,用亚克力 if try_acrylic(hwnd): return "acrylic" # 兜底:普通模糊 if try_blur(hwnd): return "blur" return "none"

每个try_xxx函数内部用 try-except 包住,失败返回 False,让上层继续降级。这样即使某个接口在特定系统上不可用,程序也不会崩,只是效果降级。返回值可以用来做日志或者调试,方便定位问题。

这种"探测-尝试-降级"的模式,是我做跨版本兼容时最常用的套路。核心思想是:永远不要假设某个接口一定可用,永远准备一个能跑的兜底方案。哪怕兜底方案只是纯色背景,也比程序直接崩溃强。

6.2 把效果逻辑和业务逻辑解耦

我见过不少项目把亚克力调用代码直接塞在主窗口类里,几十行系统调用和业务代码混在一起,后期想换方案或者调试都很难受。更好的做法是抽成一个独立的 mixin 或者工具模块:

class AcrylicMixin: def init_acrylic(self, tint=0x99202020): self._acrylic_tint = tint self._acrylic_applied = False def showEvent(self, event): super().showEvent(event) if not self._acrylic_applied: apply_backdrop(self) self._acrylic_applied = True

主窗口继承这个 mixin,业务代码完全不用关心亚克力怎么实现。想换方案只改 mixin,想关掉效果把init_acrylic的调用去掉就行。这种解耦在项目变大之后价值特别明显,尤其是当你需要同时维护 Win10 和 Win11 两套表现时。

6.3 性能与资源占用的实测观察

最后聊聊性能。亚克力效果本身对 GPU 有一定要求,因为它涉及实时模糊。我在几台不同配置的机器上测过,结论是:现代核显完全够用,老机器上会有感知。具体表现是窗口拖动时帧率下降,或者背景刷新有延迟。

如果你的应用是常驻后台、偶尔才显示窗口的工具,亚克力带来的开销基本可以忽略。但如果是需要频繁刷新、有动画的应用,就要注意了。缓解办法是:窗口隐藏时不要保持亚克力状态,可以在hideEvent里把AccentState设回 0(关闭),显示时再打开。这样能省下不少后台渲染开销。

另外,亚克力窗口的截图和录屏表现和普通窗口不同,因为背景是系统合成的,某些截图工具可能截不到模糊背景,只截到前景。这不是 bug,是合成机制决定的。如果你的工具有截图功能,要提前测试一下,必要时用grab()抓取窗口内容而不是截屏。

7. 一些个人实践中的取舍心得

做这类视觉效果,最大的体会是:克制比炫技重要。亚克力刚调出来的时候特别兴奋,恨不得整个界面全用上,结果做出来的东西花里胡哨,用两天就腻了。后来我给自己定了几条规矩:亚克力只用在主窗口背景,弹窗和对话框用纯色;色调永远偏中性,不要用高饱和的颜色;内容区一定要有足够的不透明度保证可读性。

还有一点是关于降级的。我现在的习惯是,任何依赖系统特性的效果,都要在代码里留一个"效果关闭"的开关,最好还能让用户自己选。因为总有一部分用户的系统环境不支持,或者就是不喜欢这种风格。把选择权交出去,比强行统一体验要友好得多。

代码组织上,我越来越倾向于把这类"平台相关"的代码集中放在一个模块里,用清晰的函数名标注它依赖什么系统版本。这样将来系统接口变了,或者要支持别的平台,改动范围是可控的。最怕的就是系统调用散落在各个文件里,改一处漏一处。

最后说个小事。亚克力效果的调试很依赖肉眼观察,不同显示器、不同壁纸下观感差异很大。我建议在浅色壁纸和深色壁纸下各测一遍,确保文字在两种情况下都清晰。有条件的话,找一台色彩偏冷的显示器和一台偏暖的对比看看,你会发现同一个色调值在不同屏幕上的表现能差出不少。这种细节,只有真正把东西做出来、反复看,才能慢慢找到那个"刚刚好"的平衡点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 1:41:00

手持示波器实战指南:从吃灰到现场调试利器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:40:58

Python邮件远程关机:无需公网IP的自动化控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:40:44

x265多线程调度:JobProvider与WorkerThread机制与调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:40:41

SpringBoot集成阿里云支付的生产级落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:40:29

参考资源怎么用?从官方文档到个人知识库的完整方法论

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华