news 2026/11/5 21:57:49

Qt无边框窗口在macOS无法缩放?根因拆解与两种解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt无边框窗口在macOS无法缩放?根因拆解与两种解决方案

最近在一个自用的小项目里,我遇到了一个特别拧巴的问题:窗口用了Qt::FramelessWindowHint,自定义标题栏、深色背景、圆角阴影,UI 做出来确实好看,可当我想从窗口边缘把窗口拉小一点时,鼠标指针明明已经变成了两边箭头的形态,拖了半天窗口却纹丝不动。更奇怪的是,同样一套代码在 Windows 上完全没有问题,唯独在 mac + Qt 5.12.1 + Xcode 12 这个组合下彻底失灵。

当时第一反应是搜资料,结果发现踩这个坑的人不在少数。但大部分答案都是面向 Windows 的WM_NCHITTEST处理,或者干脆说"mac 无解,换个框架吧"。折腾了几天之后,我找到了一套相对稳定、可落地、跨平台也能兼顾的解决方案。今天把整个排查过程和最终方案完整写下来,希望能帮到遇到同样问题的朋友。

1. 同样代码 Windows 正常、mac 失灵:先复现这个诡异现象

1.1 复现条件与最小示例

先说清楚我的复现环境,方便你对照:

依赖项版本
操作系统macOS 10.15.x / 11.x
Qt 版本5.12.1
编译环境Xcode 12.x
构建方式CMake + Qt Widgets

最关键的一个设置,就是setWindowFlags(Qt::FramelessWindowHint)。我用一个非常简单的窗口测试,只保留一个 QWidget,然后设置无边框标志,运行起来之后想通过边缘拖拽缩放,结果发现确实无效。

最小复现代码大概是这样:

#include <QWidget> #include <QApplication> class FramelessDemo : public QWidget { public: FramelessDemo() { setWindowFlags(Qt::FramelessWindowHint); setMinimumSize(400, 300); resize(800, 600); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); FramelessDemo w; w.show(); return app.exec(); }

这段代码在 Windows 上运行,你可以正常用鼠标拖拽窗口右下角或者边缘,自由调整大小。但是在 mac 上,窗口是"固若金汤"的——边缘没有任何反应,尝试拖拽时光标虽然会变成调整尺寸的箭头,但只要你一移动,窗口大小根本不会变化。

1.2 这个组合为什么特别容易触发问题

我一开始怀疑是 Qt 5.12.1 在 Xcode 12 上编译时存在某个隐性问题。Xcode 12 对应的 macOS SDK 版本比较新,而 Qt 5.12.1 发布得早,两者之间确实有一些兼容性裂缝。在几个技术社区里翻了翻,发现不仅是我,很多用这个组合的人都在吐槽同样的问题。

不过这里要区分两个层面:

第一,不是只有 Qt 5.12.1 才有这个问题。Qt::FramelessWindowHint在 macOS 上默认不可缩放,是一个长期存在的行为,只是旧版本在某些 macOS/SDK 组合下可能表现得没那么明显,或者由于其他细节被掩盖了。到了 Xcode 12 这个环境下,AppKit 对窗口 styleMask 的处理更加"严格",问题就暴露得更充分。

第二,底层的锅主要在 AppKit,不在 Qt 的封装层。macOS 的窗口系统和 Windows 不一样。Windows 在WM_NCHITTEST里允许"无边框但可调整大小",而 macOS 的NSWindow默认把"拥有边框/标题栏"和"可以调整大小"绑在了一起。去掉边框,相当于告诉系统"这窗口不需要常规的窗口管理交互",缩放能力自然就被拿掉了。

理解了这个根因,后续的方案思路就清晰了:要么我们自己接管边缘鼠标事件,要么想办法在原生层把那部分"被拿掉的调整能力"重新加回来。

2. 根因拆解:macOS 窗口机制与 Qt 的实现差异

2.1 无边框并不等于无缩放,但 macOS 偏偏默认禁止

在 Windows 上,无边框窗口可以通过命中测试机制(WM_NCHITTEST)告诉系统"我的边缘区域是 $right$、$bottom$ 之类,请帮我处理缩放",所以 Qt 把无边框窗口翻译成 Win32 窗口时,仍然保留缩放能力,甚至圆角、阴影都还在。

到了 macOS,Qt::FramelessWindowHint翻译过去后,Qt 会创建一个无边框的 NSWindow。而 AppKit 在无边框状态下,styleMask里默认不包含NSWindowStyleMaskResizable。它不像 Windows 那样有一个独立的"无边框但可调整大小"标志。所以 macOS 的选择是:你连边框都不要了,那窗口的"外围热区"我也不给你做。

这个设计初衷其实不难理解:macOS 原本就鼓励开发者不要随便做自定义窗口外观,系统窗口的圆角、阴影、动画都有一套统一的视觉语言。当你主动选择无边框时,Mac 系统认为你想要的是"类似浮层、工具面板或全屏内容"的窗口,这种窗口在系统层面通常不需要边缘缩放。

2.2 Qt 5.12.1 在 Xcode 12 下的实际行为变化

我在排查中做了几个尝试,能直观感受到 Qt 5.12.1 和 Xcode 12 组合在某些"暗处"确实不对劲。

比如,我尝试过下面的代码:

setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);

类似这种组合虽然会给窗口一些额外的 window flags,但在 macOS 上并不会自动让窗口边缘可缩放。我也尝试过在showEvent里重新设置 size grip 一类的控件,依然没有用。

后来我用Qt::WindowType做过一个简单的对比表:

测试组合Windows 缩放结果mac 缩放结果
普通带边框窗口正常正常
FramelessWindowHint正常无法缩放
FramelessWindowHint + WindowMinMaxButtonsHint正常无法缩放
FramelessWindowHint + WindowSystemMenuHint正常无法缩放

这个表格说明一个事实:在 Qt 5.12.1 + Xcode 12 下,只靠 Qt 提供的窗口 flag 组合,是无法在 mac 上得到可缩放无边框窗口的。继续在这个层面钻牛角尖没有任何意义。

所以,解决思路必须往两个方向走:要么完全自己处理鼠标事件来实时调整窗口几何尺寸,要么越过 Qt 的封装,直接操作底层 NSWindow。

3. 通用解法:自行实现边缘命中与拖拽缩放

3.1 思路:把系统不做的活自己扛下来

第一个方案,不依赖任何平台私有代码,完全在 Qt 的跨平台接口下实现。思路就是:自己检测鼠标是否落在窗口边缘的热区内,如果在,就把光标切换成对应的缩放光标;用户按下左键并拖动时,根据鼠标位移不断计算窗口新的 geometry 并setGeometry。

这个思路听起来简单,实际写好并不容易。有四个细节必须处理好:

  1. 边缘热区判定。窗口边缘不能太宽,否则盖住内部控件交互;也不能太窄,否则用户很难命中。一般 6~8 个逻辑像素比较合适。除了四条边,还要照顾四个角。
  2. 光标切换。需要根据鼠标所在区域,切换SizeFDiagCursor、SizeBDiagCursor、SizeVerCursor、SizeHorCursor等光标。如果不做状态记录,每次mouseMoveEvent都调用setCursor,会出现光标闪烁和性能损耗。
  3. 拖拽缩放时的基准点处理。比如拖动右下角时,窗口的左上角保持不动,改变宽高;拖动左上角时,右下角保持不动,同时调整 x、y、宽、高。每一种边缘/角落都要单独写逻辑。
  4. 最小尺寸约束。setGeometry时不能让宽高缩到小于minimumWidth/minimumHeight,否则窗口内容布局会被压垮。同时也应该先判断缩放后的 x/y 是否会超出屏幕范围。

3.2 代码:边缘检测 + 光标切换 + 动态 setGeometry

我直接给出一个完整封装。这个类可以作为基类,也可以作为容器窗口的内部逻辑。这里我写成一个可继承的FramelessResizableWidget:

#include <QWidget> #include <QMouseEvent> #include <QCursor> #include <QApplication> class FramelessResizableWidget : public QWidget { Q_OBJECT public: enum class Edge { None = 0, Left = 1 << 0, Top = 1 << 1, Right = 1 << 2, Bottom = 1 << 3, TopLeft = Top | Left, TopRight = Top | Right, BottomLeft = Bottom | Left, BottomRight = Bottom | Right, }; explicit FramelessResizableWidget(QWidget *parent = nullptr) : QWidget(parent) { setMouseTracking(true); setMinimumSize(200, 150); } protected: bool eventFilter(QObject *obj, QEvent *ev) override { // 如果使用了子控件,需要把事件过滤到本组件上 return QWidget::eventFilter(obj, ev); } void mousePressEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { m_edge = hitTest(event->pos()); if (m_edge != Edge::None) { m_pressedGlobalPos = event->globalPos(); m_pressedGeometry = geometry(); event->accept(); return; } } QWidget::mousePressEvent(event); } void mouseMoveEvent(QMouseEvent *event) override { if (m_edge != Edge::None && (event->buttons() & Qt::LeftButton)) { resizeWidget(event->globalPos()); event->accept(); return; } Edge edge = hitTest(event->pos()); updateCursor(edge); m_edge = edge; // 非拖拽状态下也记录下来,用于判断是否按下 QWidget::mouseMoveEvent(event); } void mouseReleaseEvent(QMouseEvent *event) override { m_edge = Edge::None; QWidget::mouseReleaseEvent(event); } private: Edge hitTest(const QPoint &pos) { const int border = 8; int x = pos.x(); int y = pos.y(); bool left = x < border; bool right = x >= width() - border; bool top = y < border; bool bottom = y >= height() - border; Edge edge = Edge::None; if (left) edge = static_cast<Edge>(static_cast<int>(edge) | static_cast<int>(Edge::Left)); if (right) edge = static_cast<Edge>(static_cast<int>(edge) | static_cast<int>(Edge::Right)); if (top) edge = static_cast<Edge>(static_cast<int>(edge) | static_cast<int>(Edge::Top)); if (bottom)edge = static_cast<Edge>(static_cast<int>(edge) | static_cast<int>(Edge::Bottom)); return edge; } void updateCursor(Edge edge) { QCursor cursor; switch (edge) { case Edge::Left: case Edge::Right: cursor = Qt::SizeHorCursor; break; case Edge::Top: case Edge::Bottom: cursor = Qt::SizeVerCursor; break; case Edge::TopLeft: case Edge::BottomRight: cursor = Qt::SizeFDiagCursor; break; case Edge::TopRight: case Edge::BottomLeft: cursor = Qt::SizeBDiagCursor; break; default: cursor = Qt::ArrowCursor; break; } if (cursor.shape() != this->cursor().shape()) { setCursor(cursor); } } void resizeWidget(const QPoint &globalPos) { QPoint delta = globalPos - m_pressedGlobalPos; QRect newGeo = m_pressedGeometry; int minW = minimumWidth(); int minH = minimumHeight(); if (m_edge & Edge::Left) { newGeo.setLeft(m_pressedGeometry.left() + delta.x()); if (newGeo.width() < minW) { newGeo.setLeft(newGeo.right() - minW + 1); } } if (m_edge & Edge::Right) { newGeo.setRight(m_pressedGeometry.right() + delta.x()); if (newGeo.width() < minW) { newGeo.setRight(newGeo.left() + minW - 1); } } if (m_edge & Edge::Top) { newGeo.setTop(m_pressedGeometry.top() + delta.y()); if (newGeo.height() < minH) { newGeo.setTop(newGeo.bottom() - minH + 1); } } if (m_edge & Edge::Bottom) { newGeo.setBottom(m_pressedGeometry.bottom() + delta.y()); if (newGeo.height() < minH) { newGeo.setBottom(newGeo.top() + minH - 1); } } setGeometry(newGeo); } Edge m_edge = Edge::None; QPoint m_pressedGlobalPos; QRect m_pressedGeometry; };

3.3 让缩放手感接近原生窗口的细节

代码写出来只是第一步,真正好用还需要打磨几个细节,否则手感非常"塑料"。

细节一:事件过滤与子控件冲突。如果你的窗口里有子控件,比如按钮、文本框,鼠标移动到这些子控件上时,鼠标事件会被子控件拦截,边缘热区判定会失效。这时你需要在构造函数里给子控件安装事件过滤器:

// 假设有子控件 childWidget childWidget->installEventFilter(this);

然后在eventFilter里,根据event->pos()(要映射到父窗口坐标)同样走 hitTest 逻辑。我上面代码里预留了eventFilter的重写,目的就在这里。

细节二:最小尺寸的"纠偏"逻辑。为什么我在resizeWidget里要加minW、minH判断?因为当用户拖到很小位置时,delta可能是负值,直接setGeometry可能把宽高原地拉没。必须要做钳位。但如果缩小到极限后,用户继续反方向拖大,刚才的钳位逻辑也依然有效,因为基准点是按下时记录的矩形,不是实时矩形。这一点很容易写错,建议在脑内模拟一遍拖拽路径再动手。

细节三:在高 DPI 屏幕下的坐标。mac 的 Retina 屏上,Qt 的 QPoint 默认使用"逻辑点",不是物理像素。所以你不用除devicePixelRatio。但如果使用原生坐标,就一定要小心。在 Qt 层直接用event->globalPos()和geometry(),不会踩这个坑。

细节四:窗口缩放时的刷新性能。如果窗口内容比较复杂,setGeometry会触发整窗重绘,缩放手感可能存在轻微 "飘影"。一种优化办法是在缩放过程中临时给窗口设置WA_OpaquePaintEvent,或者简单的setUpdatesEnabled(false)来降低同步刷新频率:按下的瞬间setUpdatesEnabled(false),松开后再setUpdatesEnabled(true)。不过如果你窗口没有大量自绘内容,不建议加,因为关闭更新会让背景变黑一块,观感反而更差。

这个方案的优点是纯粹、可控、跨平台统一。缺点也很明显:边缘事件做得再好,也不如原生窗口那样"零代码"地支持系统级手势,比如双击边缘自动最大化、macOS 的 Snap 分屏等功能都需要自己实现。

4. 原生路线:从 winId 拿到 NSWindow 强制开启 Resizable

4.1 方案原理:绕过 Qt 的属性限制

既然问题是 AppKit 的NSWindowStyleMaskResizable没有加上,那就想办法在原生层加上。Qt 在 macOS 上最终还是要创建一个NSWindow,QWidget里的winId()返回的WId本质上就是这个窗口对应的NSView指针。通过这个指针,我们可以拿到NSWindow,然后修改它的styleMask。

理论上,只要给NSWindow加上NSWindowStyleMaskResizable,无边框窗口就能恢复边缘缩放能力,而且缩放过程中的下拉菜单行为也是系统原生处理,手感最好。

4.2 具体代码与触发时机

实现思路分三步:

  1. 从当前QWidget拿到winId()。
  2. 把这个WId强转成NSView *。
  3. 通过[view window]拿到NSWindow,给styleMask加上NSWindowStyleMaskResizable。

我封装了一个工具函数,同时支持QWindow和QWidget:

#include <QWidget> #include <QWindow> #include <QtPlatformHeaders/QWindowPrivate> // 不一定需要 #include <AppKit/AppKit.h> void enableNativeResizableForWidget(QWidget *widget) { if (!widget) return; NSView *view = (NSView *)widget->winId(); if (!view) return; NSWindow *window = [view window]; if (!window) return; NSWindowStyleMask mask = [window styleMask]; if (!(mask & NSWindowStyleMaskResizable)) { [window setStyleMask:mask | NSWindowStyleMaskResizable]; [window setContentMinSize:NSMakeSize(200, 150)]; } }

调用时机非常关键。如果放在QWidget构造函数里,窗口还没有真正创建,winId()可能拿不到有效的NSView,更别说NSWindow。所以,我选择在showEvent里调用:

void MyFramelessWindow::showEvent(QShowEvent *event) { QWidget::showEvent(event); // 第一次 show 之后,原生窗口才真正存在 enableNativeResizableForWidget(this); }

如果窗口不是初始显示,而是可能被隐藏后再次显示,showEvent会再次触发,这时重复调用也没问题,无非是再设置一次styleMask。另外,如果你在窗口构造后已经 show 过,也可以这样手动触发:

QTimer::singleShot(0, this, [](){ // 确保原生窗口对象已经创建 enableNativeResizableForWidget(this); });

4.3 为什么这个方案在某些场景下不可靠

原生方案看着漂亮,我一测,确实能缩放,但稳定性上有坑。

第一,Qt 的窗口状态同步可能会在某个时机把 styleMask 重置回去。比如你调用了setWindowFlags、setWindowState、setWindowOpacity、或者其他触发 native window 重建的操作,Qt 可能会重新应用自己的样式策略,把你手动设置的NSWindowStyleMaskResizable又抹掉。所以你要在多个时机反复设置,比如resizeEvent、moveEvent里都要调用一次,代码比较"脏"。

第二,在 macOS 11 Big Sur 之后,改变styleMask会有一个不可避免的窗口淡入淡出效果。就是因为这个问题,我一度以为窗口闪退了。后来才知道,系统在 styleMask 变化时会把窗口重新"涂"一遍,视觉上就会闪一下。如果你只是启动时设置一次,可能只闪一下;如果在每次 resize 的时候设置,窗口就会一直疯狂闪烁,体验非常糟糕。

第三,无边框窗口本身不是keyWindow。即使加上 Resizable,用户点击窗口先要能成为 key window。Qt 在无边框下默认可能需要自己处理canBecomeKeyWindow。如果你发现按钮点击没反应、输入框不聚焦,多半是这个问题。

所以我的结论是:原生方案适合"启动时偷偷设置一次"的轻量场景,但如果你后续要做自定义标题栏、动态改变窗口样式,它很容易失控。它更适合作为方案一的补充,而不是唯一手段。

5. 两种方案选择与最终落地效果

5.1 直接对比:控制力、跨平台一致性、实现成本

我把这两个方案放在一起做了个对比表:

维度手动边缘检测方案原生 NSWindow 方案
边缘缩放能力完全自主实现系统原生实现
支持系统手势(如双击边缘、分屏)不支持支持
跨平台一致性高,Windows/mac/Linux 行为一致仅 mac 生效,平台分支明显
代码量约 200 行左右约 30 行
维护成本依赖 Qt 鼠标事件稳定,成本中等依赖 SDK/系统行为,成本中高
DPI/分辨率适配Qt 自己处理,基本无感需要额外处理 contentMinSize 等原生参数
后期扩展自定义标题栏优,事件流完全可控一般,容易和原生交互冲突

说实话,追求手感和系统级体验,原生方案最理想。但结合我的实际情况——项目要同时支持 Windows、mac、Linux,代码不能为 mac 单独开一条"野生"逻辑链,所以手动边缘检测方案是更稳的底座。

5.2 我的最终取舍与测试结果

我最后选择的是以手动边缘检测为主,在 mac 上利用原生方案做一次性增强。具体策略:

  1. 所有平台统一使用方案一,保证行为一致。
  2. 在 mac 上启动时调用一次原生enableNativeResizableForWidget,让系统也知道这个窗口支持缩放。这样即使我的手动边缘检测在某些边缘场景下漏了,用户也能通过系统原生热区完成缩放,算是一道保险。
  3. 在 mac 上不复用原生方案来做实时缩放,避免 styleMask 变化带来的闪烁。

实测下来,窗口在 mac 上的缩放手感非常接近原生窗口,不会再出现"指针变箭头却拖不动"的尴尬。最小化、移动、关闭也都没问题。

这里值得补充一句:手动方案虽然叫"手动",但它并没有绕过事件系统。QWidget::mouseMoveEvent的触发频率已经足够支撑实时缩放。唯一需要注意的是,如果你的机器性能比较差,缩放时窗口跟随鼠标的响应会有轻微延迟,这时候可以考虑把边缘热区从 8px 加大到 10px,并适当降低setGeometry的调用频率,比如限制 60 帧刷新一次。

6. 连带坑位:mac 无边框窗口的移动、最小化与全屏问题

6.1 拖动窗口:别让鼠标事件被标题栏控件吃掉

解决了缩放,接下来的问题就是拖动窗口。无边框窗口在 mac 上也是默认不能移动的,你只能通过mousePressEvent+mouseMoveEvent手动移动窗口。

我一开始直接在mouseMoveEvent里写:

move(event->globalPos() - offset);

然后发现窗口经常"跳一下"才跟随鼠标,非常不跟手。原因是move()的坐标是基于整个屏幕的,而鼠标globalPos()也是屏幕坐标,所以第一帧没问题,但鼠标移动时如果窗口 move 了,事件里的globalPos()会受窗口自身位置变化影响,导致偏移量计算出现累积误差。

正确的写法是在按下时记录鼠标全局位置与窗口几何位置的偏移量,整个拖拽过程中一直用这个偏移量计算:

void FramelessWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_dragOffset = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } void FramelessWidget::mouseMoveEvent(QMouseEvent *event) { if (m_dragOffset.isNull()) return; move(event->globalPos() - m_dragOffset); }

6.2 缩放时的光标闪烁与高DPI偏移

在 mac 上还有一个很隐蔽的坑:如果你在mouseMoveEvent里频繁调用setCursor,或者内部控件改变了光标,窗口边缘的光标会在几个像素内来回切换,视觉上就是光标疯狂闪烁。

解决办法就是我上面说的,把"当前光标类型"记录下来,只在类型变化时才调用setCursor:

if (cursor.shape() != this->cursor().shape()) { setCursor(cursor); }

至于高 DPI 偏移,我实测如果是纯 Qt 逻辑坐标,基本不会遇到。但如果你一定要用原生坐标,比如在QNativeEvent里读取 NSEvent 的屏幕坐标,就要手动处理devicePixelRatio。这块建议直接用 Qt 的event->globalPos(),省心。

6.3 全屏按钮和圆角处理的补充记录

mac 无边框窗口没有系统自带的全屏按钮,这也符合预期。如果你需要全屏能力,可以自己加一个按钮,然后:

showFullScreen();

不过全屏后窗口还是无边框的样子,mac 原生的全屏动画、空间切换等都会缺失。如果你的应用需要这块体验,建议优先考虑使用QMainWindow加上隐藏标题栏的方案,而不是完全无边框。

圆角处理上,mac 上的无边框窗口默认是没有圆角的,看起来方方正正,在 macOS 的视觉风格下会很突兀。可以在窗口上套一个QGraphicsDropShadowEffect,同时用setMask做圆角裁剪。但注意setMask在 Retina 上会有一圈锯齿,需要配合QPainterPath绘制。这是个不小的工作量,如果不是必须,建议先缓一缓。

写了这么多,最大的感受是:macOS 是"既要又要"的平台——它希望你能做出漂亮的界面,又不希望你绕过系统原生的窗口机制。解决无边框窗口问题,本质上不是找一段魔术代码,而是搞明白"哪部分系统能做,哪部分必须自己写,哪部分写了也会被系统重新覆盖"。希望这篇记录能让你少走一点弯路。

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

赫夫曼树与编码实战:从原理到文件压缩解压的Java实现

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

作者头像 李华
网站建设 2026/11/3 20:19:52

labelImg目标检测标注工具:安装、格式转换与避坑指南

简介&#xff1a;labelImg-master.zip 是开源图像标注工具 labelImg 的源代码压缩包&#xff0c;面向计算机视觉与深度学习方向的开发者、研究人员&#xff0c;用于高效创建和编辑目标检测、语义分割所需的像素级标注。压缩包共 123 个文件&#xff0c;其中以 Python 源码&…

作者头像 李华
网站建设 2026/11/5 7:38:24

周易排盘源码工程化拆解:从历法换算到卦象渲染

简介&#xff1a;《周易排盘源码》是一套基于ASP语言开发的在线周易预测工具源码&#xff0c;面向易学爱好者、Web开发者以及希望研究传统术数算法实现的技术人员&#xff0c;可解决八字、六爻、奇门遁甲等排盘功能从零搭建的问题。资源以rar压缩包形式提供&#xff0c;整体约5…

作者头像 李华
网站建设 2026/11/3 23:00:00

Redis事务与Lua脚本:原子操作原理、选型对比及生产实践

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

作者头像 李华
网站建设 2026/11/4 3:24:48

ESP32上做应用商店:动态加载与沙箱隔离实战

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

作者头像 李华
网站建设 2026/10/22 23:32:17

ADC0809详解:从逐次逼近原理到51单片机驱动代码与避坑指南

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

作者头像 李华