十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Textual 鼠标滚轮上滑事件 MouseScrollUp 完全指南:触发原理、属性与自定义滚动处理

Textual 鼠标滚轮上滑事件 MouseScrollUp 完全指南:触发原理、属性与自定义滚动处理 Textual 鼠标滚轮上滑事件 MouseScrollUp 完全指南触发原理、属性与自定义滚动处理【免费下载链接】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导读本文聚焦 TextualThe lean application framework for Python事件体系中的MouseScrollUp事件深入讲解该事件在终端应用中的触发时机、完整的坐标与修饰键属性、终端底层SGR 鼠标协议的产生原理、冒泡与分发机制以及如何监听并自定义滚轮向上滚动行为。阅读完本文你将能够在自己的 Textual 应用中准确捕获滚轮上滑、区分修饰键组合、实现自定义滚动逻辑并理解其与MouseScrollDown等滚动事件的协作方式。事件概述什么时候收到 MouseScrollUpMouseScrollUp是 Textual 中代表鼠标滚轮向上滚动的专用事件类定义于 src/textual/events.pyrich.repr.auto class MouseScrollUp(MouseEvent, bubbleTrue, verboseTrue): Sent when the mouse wheel is scrolled *up*. - [X] Bubbles - [X] Verbose 从源码类签名可以确认该事件的两个关键特性Bubbles冒泡事件会沿组件树从目标控件向父级逐层传播父控件可以在子控件未消费事件时获得处理机会Verbose详细日志事件在 Textual 的--dev开发模式日志中会被完整记录便于调试。与之一一对应的还有滚轮向下、向左、向右三个兄弟事件MouseScrollDownsrc/textual/events.py、MouseScrollRightsrc/textual/events.py、MouseScrollLeftsrc/textual/events.py。四个事件共享完全相同的类结构与行为仅语义方向不同本文以MouseScrollUp为代表展开其余三个可直接类推。继承体系与 MouseEvent 的关系MouseScrollUp直接继承自MouseEvent定义于 src/textual/events.py后者是所有鼠标事件MouseDown、MouseUp、MouseMove、Click等的公共基类本身又继承自InputEvent。在 Textual 官方 API 文档中MouseScrollUp页面明确指出SeeMouseEventfor the full list of properties and methods.——即MouseScrollUp自身不新增任何属性其携带的全部数据都来自MouseEvent。因此理解MouseScrollUp就是理解MouseEvent的属性面板。MouseEvent 携带的完整属性面板根据 src/textual/events.py 中MouseEvent的__slots__声明与属性定义MouseScrollUp事件上可用的属性如下属性类型含义widgetWidget \| None鼠标所在位置的控件control属性是其别名同样返回widgetx/yint鼠标相对于目标控件的单元格坐标已取整pointer_x/pointer_yfloat鼠标指针的相对精确坐标未取整screen_x/screen_yint鼠标相对于整个屏幕终端左上角的绝对坐标pointer_screen_x/pointer_screen_yfloat指针的绝对坐标精确值delta_x/delta_yint自上一次鼠标消息以来x/y的变化量buttonint被按下按钮的索引滚轮事件中固定为0shiftbool事件发生时 Shift 键是否被按住metabool事件发生时 Meta 键是否被按住ctrlbool事件发生时 Ctrl 键是否被按住styleStyle鼠标光标下的 RichStyleoffsetOffset(x, y)坐标组成的Offset对象在事件处理函数中最常使用的组合是event.x/event.y确定滚动发生在哪个单元格区域与event.ctrl/event.shift区分普通滚动与修饰键滚动。底层原理终端 SGR 鼠标协议如何产生 MouseScrollUpMouseScrollUp并非凭空产生而是 Textual 驱动层与终端模拟器协商鼠标上报模式后由 src/textual/_xterm_parser.py 中的 XTerm 解析器解析而来。驱动层启用鼠标上报Textual 在各平台驱动Linux、Windows、Web、Inline启动时会向终端写入启用鼠标的转义序列。以 src/textual/drivers/linux_driver.py 的_enable_mouse_support为例write(\x1b[?1000h) # SET_VT200_MOUSE write(\x1b[?1003h) # SET_ANY_EVENT_MOUSE write(\x1b[?1015h) # SET_VT200_HIGHLIGHT_MOUSE write(\x1b[?1006h) # SET_SGR_EXT_MODE_MOUSE其中关键的\x1b[?1006hSGR 扩展鼠标模式让终端以CSI b ; x ; y M/m的格式上报鼠标状态该格式携带按钮编码与修饰键信息是滚轮方向得以区分的基础。windows_driver.py、web_driver.py、linux_inline_driver.py也都会写入同样的\x1b[?1006h序列。此外驱动层还有可选的像素级鼠标上报\x1b[?1016h_enable_mouse_pixels用于像素精度场景如绘图类控件。解析器如何区分滚轮方向src/textual/_xterm_parser.py 的parse_mouse_code方法用正则\x1b\[(\d);(-?\d);(-?\d)([Mm])捕获按钮编码、坐标与按下/释放标记然后按按钮编码高位判定事件类型if buttons 64: event_class [ events.MouseScrollUp, events.MouseScrollDown, events.MouseScrollLeft, events.MouseScrollRight, ][buttons 3] button 0也就是说滚轮事件的判据是按钮编码第 7 位数值 64被置位滚轮方向则由低 2 位buttons 3决定按钮编码解析结果64MouseScrollUp向上65MouseScrollDown向下66MouseScrollLeft向左67MouseScrollRight向右修饰键信息同样编码在按钮数值中buttons 4为 Shift、buttons 8为 Meta、buttons 16为 Ctrl。例如编码68 64 4表示Shift 按住的滚轮上滑72 64 8表示Meta 按住的滚轮上滑。测试 tests/test_xterm_parser.py 对此做了完整验证序列\x1b[64;18;25M被断言为MouseScrollUp且x 17、y 24\x1b[68;18;25M则额外断言shift is True。分发与冒泡事件如何到达你的控件MouseScrollUp的bubbleTrue意味着当某个控件收到滚轮事件但未调用event.stop()时事件会继续向父级控件传播。这种机制让子控件消费优先、父控件兜底的滚动层级设计成为可能。在 src/textual/widget.py 中还有一个值得注意的细节——滚轮事件是禁用豁免的_MOUSE_EVENTS_DISALLOW_IF_DISABLED (events.MouseEvent, events.Enter, events.Leave) _MOUSE_EVENTS_ALLOW_IF_DISABLED ( events.MouseScrollDown, events.MouseScrollUp, events.MouseScrollRight, events.MouseScrollLeft, )即被disabled的控件会拦截大多数鼠标事件但四个滚轮滚动事件依然允许派发。这意味着即便一个控件处于禁用状态其容器仍然可以依靠滚轮事件完成页面滚动——这是 Textual 有意为之的可用性设计。内置滚动行为Widget基类本身提供了默认的滚轮处理逻辑见 src/textual/widget.pydef _on_mouse_scroll_up(self, event: events.MouseScrollUp) - None: if event.ctrl or event.shift: if self.allow_horizontal_scroll: if self._scroll_left_for_pointer(animateFalse): event.stop() else: if self.allow_vertical_scroll: if self._scroll_up_for_pointer(animateFalse): event.stop()从该实现可以读出两个实用规则无修饰键滚轮上滑触发垂直方向向上滚动_scroll_up_for_pointer前提是控件允许垂直滚动allow_vertical_scroll按住 Ctrl 或 Shift滚轮上滑转换为水平方向向左滚动_scroll_left_for_pointer前提是allow_horizontal_scroll。无论是哪种情况只要滚动实际发生了事件就会被stop()掉不再向上冒泡如果控件本身不可滚动则事件继续传播给祖先控件处理——这正是 Textual 中滚轮冒泡至可滚动容器的经典链路的源码依据。实战监听并自定义 MouseScrollUp方式一命名约定处理函数Textual 会按on_事件名小写化的命名约定自动将事件路由到处理函数from textual.app import App, ComposeResult from textual.containers import VerticalScroll from textual.widgets import Label, Static class ScrollLogger(VerticalScroll): def on_mouse_scroll_up(self, event: events.MouseScrollUp) - None: # 记录滚动事件但不阻止默认滚动行为 self.notify( fscrolled up at ({event.x}, {event.y}) fctrl{event.ctrl} shift{event.shift} ) def on_mouse_scroll_down(self, event: events.MouseScrollDown) - None: self.notify(fscrolled down, delta_y{event.delta_y})方式二on 装饰器对于跨类复用或需要集中管理的场景推荐使用on(MouseScrollUp)显式声明from textual import on from textual.events import MouseScrollUp class ScrollDemoApp(App): on(MouseScrollUp) def handle_scroll_up(self, event: MouseScrollUp) - None: # 仅在按住 Ctrl 时接管事件否则放行给默认滚动逻辑 if event.ctrl: self.notify(fCtrlscroll up at screen ({event.screen_x}, {event.screen_y})) event.stop()这里event.stop()的调用是关键一旦调用事件将不再冒泡也不会触发Widget基类的默认滚动行为不调用则默认滚动照常执行你的处理逻辑仅作为旁路观察。完整可运行示例下面的例子组合了上述要点实现一个滚轮上滑步进、下滑步退的自定义计数器控件from textual.app import App, ComposeResult from textual.containers import VerticalScroll from textual.events import MouseScrollDown, MouseScrollUp from textual.widgets import Static class WheelCounter(Static): def __init__(self) - None: super().__init__(value: 0) self.value 0 def on_mouse_scroll_up(self, event: MouseScrollUp) - None: if event.ctrl: self.value 10 else: self.value 1 self.update(fvalue: {self.value}) event.stop() # 消费事件防止冒泡触发容器的默认滚动 def on_mouse_scroll_down(self, event: MouseScrollDown) - None: self.value - 1 self.update(fvalue: {self.value}) event.stop() class WheelApp(App): def compose(self) - ComposeResult: yield VerticalScroll(WheelCounter()) if __name__ __main__: WheelApp().run()运行后在计数控件上滚动滚轮即可观察值的变化按住 Ctrl 上滑则每次步进 10。若去掉event.stop()事件还会冒泡到VerticalScroll容器触发页面滚动。调试与验证MouseScrollUp标记为verboseTrue因此在开发模式下运行应用时滚动事件会被记录到日志中python app.py --dev在 Textual Devtools 控制台中可以看到类似MouseScrollUp(widget..., x17, y24, pointer_x..., delta_x0, delta_y0, button0, shiftFalse, metaFalse, ctrlFalse)的完整事件快照对应 src/textual/events.py 中__rich_repr__定义的字段顺序便于确认坐标与修饰键状态是否符合预期。仓库测试 tests/test_xterm_parser.py 是理解该事件触发条件的权威参考test_mouse_scroll_up与test_mouse_scroll_down分别用\x1b[64;18;25M与\x1b[65;18;25M验证了解析结果可作为自行扩展滚动协议处理的回归基线。相关事件一览MouseScrollUp在 Textual 事件体系中与下列事件紧密关联官方文档以 See also 形式给出完整清单对应仓库路径如下ClickEnterLeaveMouseDownMouseMoveMouseScrollDownMouseScrollLeftMouseScrollRightMouseUp基类MouseEvent完整属性与方法列表见 src/textual/events.py其中MouseScrollDown与MouseScrollUp在终端上报编码上仅相差最低位65 对 64在控件处理逻辑上互为镜像_scroll_down_for_pointer对_scroll_up_for_pointer是最常成对出现的滚动事件。【免费下载链接】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),仅供参考
返回列表