Textual Hide 事件深入解析:触发时机、底层机制与实战监听
Textual Hide 事件深入解析触发时机、底层机制与实战监听【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textualHide是 Textual 终端 UI 框架中用于通知控件从界面上消失的内置事件。本指南以 Hide 事件 API 文档 为核心结合框架源码与测试用例完整讲解该事件的触发条件、消息派发链路、监听方式以及与display、visible、visibility等属性之间的关系帮助你在开发自定义控件时准确感知显隐变化并做出正确响应。Hide 事件的定义与基本定位在 Textual 中事件Event是一种特殊的消息Message由框架在输入或状态变化时自动发送。Hide事件定义于 src/textual/events.pyclass Hide(Event, bubbleFalse): Sent when a widget has been hidden. - [ ] Bubbles - [ ] Verbose 从类定义可以看出两个关键特性不冒泡bubbleFalseHide只会发送给发生显隐变化的那个控件本身不会沿 DOM 树向上传播给父控件。这与按键Key、鼠标点击Click等输入类事件默认冒泡截然不同。因此监听Hide时应把处理逻辑写在具体控件或其子类上而不是依赖父容器统一截获。非 Verbose 模式在 Textual 的日志/调试输出中默认不显示该事件避免高频的布局刷新把日志刷屏。与之对应的是 Show 事件定义于同一文件src/textual/events.py在控件首次显示时发送二者通常成对出现共同描述一个控件的可见性生命周期。触发条件详解Hide的 docstring 明确列出了四种触发场景src/textual/events.py控件从 DOM 中被移除例如调用widget.remove()或widget.remove_children()卸载控件时控件因滚动或裁剪而不再显示控件仍在 DOM 中但被滚出终端可视区域或被其容器的overflow裁剪到屏幕之外display属性被设置为False即通过 Python 代码执行widget.display Falsedisplay样式被设置为none既可通过代码widget.styles.display none也可通过 CSS 规则如.hidden { display: none; }实现。理解第 2 条很关键Hide并不等于控件被删除。一个处于长列表中部、因滚动暂时不可见的控件同样会收到Hide反之当它重新滚入视野时会收到Show。这意味着Hide/Show描述的是一种瞬时可见状态而非 DOM 存续状态。display 与 visible 的区别要正确理解触发条件需要分清 Textual 中两个容易混淆的概念实现在 src/textual/dom.py概念属性/样式效果影响Hide触发displaywidget.display/display: none控件不参与布局、不占空间直接消失是visiblewidget.visible/visibility: hidden控件仍占位、保留空间但不绘制内容否也就是说通过visibility: hidden隐藏的控件不会收到Hide事件因为它仍然占据布局空间只有通过display体系代码属性或 CSS 的display: none移除出布局才会触发Hide。源码中display属性的实现印证了这一点src/textual/dom.pydisplay.setter def display(self, new_val: bool | str) - None: if isinstance(new_val, bool): self.styles.display block if new_val else none elif new_val in VALID_DISPLAY: self.styles.display new_val else: raise StyleValueError( finvalid value for display (received {new_val!r}, fexpected {friendly_list(VALID_DISPLAY)}), )布尔值False会被转换为display: none传入字符串时则要求必须是VALID_DISPLAY中合法的display取值否则抛出StyleValueError。底层机制事件是如何被派发的Hide事件并非由某个属性 setter 直接发送而是由Compositor 布局重排reflow的结果驱动。Textual 的屏幕对象Screen在每次布局更新时调用 src/textual/_compositor.py 的reflow方法def reflow(self, parent: Widget, size: Size) - ReflowResult: ... # Newly visible widgets shown_widgets new_widgets - old_widgets # Newly hidden widgets hidden_widgets self.widgets - widgets ... return ReflowResult( hiddenhidden_widgets, shownshown_widgets, resizedresized_widgets, )reflow会把上一帧布局中可见、这一帧不再可见的控件集合hidden_widgets和这一帧新出现的控件集合shown_widgets一并返回。随后屏幕在 src/textual/screen.py 中根据该结果向对应控件发送事件hidden, shown, resized self._compositor.reflow(self, size) self._layout_widgets.clear() Hide events.Hide Show events.Show for widget in hidden: widget.post_message(Hide()) ... for widget in shown: widget.post_message(Show())从调用链可以看出事件发送顺序是先 Hide、后 Show且每次布局更新只发送一次。控件通过post_message将事件投递到自身的消息队列再由控件对应的 asyncio 任务从队列中取出并分发给处理器方法。这也意味着Hide处理器中无法直接通过self.display查询到旧值——事件到达时样式和布局已经完成切换。滚动场景的快路径当滚动发生时scrollTrue且无挂起的布局任务屏幕走的是 src/textual/_compositor.py 的reflow_visible快路径——只对可见子控件做增量布局def reflow_visible(self, parent: Widget, size: Size) - set[Widget]: Reflow only the visible children. This is a fast-path for scrolling. ... exposed_widgets map.keys() - old_map.keys()该路径返回的是因滚动而新暴露的控件集合只发Resize/Show而不重新计算 hidden 集合。这解释了为什么在纯滚动场景下被滚出的控件不一定逐帧收到Hide——框架优先保证性能只有滚动结束后通过完整reflow才精确对齐显隐状态。因此不要把Hide当作实时滚出回调使用它更准确的含义是布局结算后确认不再显示。如何监听 Hide 事件与所有 Textual 事件一致监听Hide只需在控件类上定义on_hide处理器方法from textual.app import App, ComposeResult from textual.widget import Widget class StatusWidget(Widget): 一个简单的状态控件显隐时打印日志。 def on_hide(self) - None: self.log(StatusWidget is now hidden) def on_show(self) - None: self.log(StatusWidget is now visible) class HideDemo(App[None]): def compose(self) - ComposeResult: yield StatusWidget()处理器可以带参数接收事件对象def on_hide(self, event: events.Hide) - None以便在需要时调用event提供的方法或检查事件属性。框架内部的实际用法Textual 内置控件中也有监听显隐事件的实例。例如OptionList在on_show中滚动到当前高亮项确保控件重新出现时用户仍能看到焦点位置src/textual/widgets/_option_list.pydef on_show(self) - None: self.scroll_to_highlight()这是Show/Hide事件的典型应用场景在不可见期间跳过昂贵的渲染或动画在重新可见时恢复正确状态。实战示例显隐驱动的动态行为下面是一个可运行的综合示例演示三种触发方式与事件监听from textual.app import App, ComposeResult from textual.containers import Vertical from textual.widget import Widget from textual.widgets import Button, Label, Static class Logged(Static): 监听自身显隐的静态控件。 def on_hide(self) - None: self.notify(Logged hidden) def on_show(self) - None: self.notify(Logged shown) class HideDemoApp(App[None]): 演示 Hide/Show 事件的完整应用。 CSS #box { height: 8; border: round $primary; } def compose(self) - ComposeResult: yield Button(Toggle display, idtoggle) yield Button(Toggle visibility, idtoggle-vis) with Vertical(idbox): yield Logged(Im in the box) def on_button_pressed(self, event: Button.Pressed) - None: box self.query_one(#box, Vertical) if event.button.id toggle: # 方式一display False 会触发 Hide box.display not box.display elif event.button.id toggle-vis: # 方式二visibility hidden 不会触发 Hide控件仍占位 box.styles.visibility ( visible if box.styles.visibility hidden else hidden ) if __name__ __main__: HideDemoApp().run()运行后点击 Toggle display 按钮Logged控件会收到Hide点击 Toggle visibility 则只会让内容消失而不触发Hide——这正是前文触发条件表格所验证的行为。测试验证框架如何保证显隐语义仓库测试 tests/test_visible.py 对显隐行为做了直接验证可作为理解Hide触发边界的补充依据async def test_visibility_changes() - None: Test changing visibility via code and CSS. class VisibleTester(App[None]): CSS Widget { height: 1fr; } .hidden { visibility: hidden; } def compose(self) - ComposeResult: yield VerticalScroll( Widget(idkeep), Widget(idhide-via-code), Widget(idhide-via-css) ) async with VisibleTester().run_test() as pilot: ... pilot.app.query_one(#hide-via-code).styles.visibility hidden await pilot.pause(0) assert pilot.app.query_one(#hide-via-code).visible is False ... pilot.app.query_one(#hide-via-css).set_class(True, hidden) await pilot.pause(0) assert pilot.app.query_one(#hide-via-css).visible is False同时visible属性具有继承语义若某控件未显式设置visibility则继承最近祖先的值src/textual/dom.py。回归测试 tests/test_visible.py 验证了这一点——父节点设置为visibility: hidden后其所有后代控件的visible均为False但这类继承式隐藏同样不会触发后代控件的Hide事件。注意事项与最佳实践区分三种消失display: none不占位触发Hide、visibility: hidden占位不触发、滚动裁剪布局结算后触发/不触发取决于滚动路径。根据业务语义选择合适的隐藏方式。不要在on_hide中做重量级操作布局重排可能伴随频繁的滚动与尺寸变化Hide处理应保持轻量耗时逻辑可放入Worker异步执行。on_hide与on_unmount的区别Unmount在控件从 DOM 卸载时发送src/textual/events.py且只发生一次Hide则可能在控件生命周期内反复出现。需要区分永久销毁与暂时隐藏两种场景。利用Show恢复状态Hide与Show通常成对出现屏幕更新逻辑先发Hide再发Show见 src/textual/screen.py可在on_show中恢复滚动位置、刷新缓存等。控件不可见时节省资源在on_hide中暂停定时器或动画在on_show中恢复是 Textual 应用常见的性能优化手段。通过本文介绍的触发条件、派发链路与监听方式你可以精准掌控控件显隐时机写出显隐感知的健壮控件。相关主题可进一步参考 Textual 事件与消息指南 以及 Show 事件文档。【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textual创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

电商推荐系统实战:协同过滤算法与Spring Cloud架构

电商推荐系统实战:协同过滤算法与Spring Cloud架构

1. 项目背景与核心价值在电商平台竞争白热化的今天,个性化推荐系统已经成为提升用户留存和转化率的关键武器。我最近刚完成一个基于用户协同过滤的智能推荐引擎项目,这个系统能根据用户历史行为数据,自动发现相似用户群体,实现&qu…

2026/9/19 5:31:28 阅读更多 →
如何用Mapbox GL JS打造3D城市建筑:fill-extrusion与GLTF建筑模型完整实践

如何用Mapbox GL JS打造3D城市建筑:fill-extrusion与GLTF建筑模型完整实践

如何用Mapbox GL JS打造3D城市建筑:fill-extrusion与GLTF建筑模型完整实践 【免费下载链接】mapbox-gl-js Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL 项目地址: https://gitcode.com/gh_mirrors/ma/mapbox-g…

2026/9/20 12:55:13 阅读更多 →
π型滤波电路设计实战:RC与LC选型关键逻辑

π型滤波电路设计实战:RC与LC选型关键逻辑

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

2026/9/19 5:31:28 阅读更多 →

最新新闻

AssetRipper:5 分钟提取 Unity 游戏资源的免费工具

AssetRipper:5 分钟提取 Unity 游戏资源的免费工具

AssetRipper:5 分钟提取 Unity 游戏资源的免费工具 【免费下载链接】AssetRipper GUI application to analyze game files 项目地址: https://gitcode.com/GitHub_Trending/as/AssetRipper AssetRipper 是一款用于 Unity 资产提取的免费开源 GUI 工具。它能解…

2026/9/20 13:14:05 阅读更多 →
BrewUI UI测试框架详解:Page Object与Fixture驱动的完整指南

BrewUI UI测试框架详解:Page Object与Fixture驱动的完整指南

BrewUI UI测试框架详解:Page Object与Fixture驱动的完整指南 【免费下载链接】BrewUI 📺 Homebrews official macOS GUI 项目地址: https://gitcode.com/GitHub_Trending/br/BrewUI BrewUI 是 Homebrew 官方推出的 macOS 图形界面,让不…

2026/9/20 13:14:05 阅读更多 →
Hugo Mount 完全指南:从 source 到 target 的统一文件系统映射

Hugo Mount 完全指南:从 source 到 target 的统一文件系统映射

开发工具前端CLI 【免费下载链接】hugo The world’s fastest framework for building websites. 项目地址: https://gitcode.com/gh_mirrors/hu/hugo 点击查看 免费下载 导读 在 Hugo 中,mount(挂载)是一个配置对象&#xff0c…

2026/9/20 13:14:05 阅读更多 →
ESP-IoT-Solution 气体传感器方案:BME690 驱动组件全解析

ESP-IoT-Solution 气体传感器方案:BME690 驱动组件全解析

物联网嵌入式驱动开发硬件开发 【免费下载链接】esp-iot-solution Espressif IoT Library. IoT Device Drivers, Documentations and Solutions. 项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution 点击查看 免费下载 本指南围绕 ESP-IoT-Soluti…

2026/9/20 13:14:05 阅读更多 →
10 分钟用 TaoToken 跑通 Qwen3.7 Flash 的 MCP 示例仓库

10 分钟用 TaoToken 跑通 Qwen3.7 Flash 的 MCP 示例仓库

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

2026/9/20 13:14:05 阅读更多 →
go-micro Agent 接口设计:Service 承载能力、Agent 承载智能的统一架构指南

go-micro Agent 接口设计:Service 承载能力、Agent 承载智能的统一架构指南

后端微服务AI AgentRPC框架 【免费下载链接】go-micro A Go agent harness and service framework 项目地址: https://gitcode.com/gh_mirrors/go/go-micro 点击查看 免费下载 导读 本文以 go-micro 的 AGENT_DESIGN.md 为核心,系统讲解 Agent 接口的完…

2026/9/20 13:13:05 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →