Flet CanvasResizeEvent 详解:监听 Canvas 尺寸变化并实现响应式图形重绘
2026/9/24 5:01:11 网站建设 项目流程
  • 前端
  • 跨平台
  • 桌面应用
  • 移动开发

【免费下载链接】flet

Build realtime web, mobile and desktop apps in Python only. No frontend experience required.

项目地址:https://gitcode.com/gh_mirrors/fl/flet
点击查看免费下载

导读

CanvasResizeEvent是 Flet 画布(Canvas)在渲染尺寸发生变化时派发的事件对象,它承载画布最新的宽高数据(单位为逻辑像素),通过Canvas.on_resize回调传递给 Python 侧开发者。本文将以 canvasresizeevent.md 为主题,结合源码与官方示例,完整讲解该事件的字段语义、底层触发链路(Python ↔ Flutter)、resize_interval采样节流机制,以及如何用它写出随窗口缩放自动重绘的响应式绘图程序。

一、事件定义:CanvasResizeEvent 是什么

CanvasResizeEvent定义在 canvas.py,它是一个继承自Event["Canvas"]dataclass,源码 docstring 明确说明:

Event emitted when aCanvasreports a new rendered size. This event is delivered throughCanvas.on_resizeand carries the latest measured dimensions in logical pixels.

即:当画布实际渲染出来的尺寸发生变化时触发,事件中携带的是逻辑像素(logical pixels)下的最新宽高,与设备像素比(pixel ratio)无关。

字段一览

字段类型序列化键(data_field)含义
widthfloatw画布新的宽度(逻辑像素)
heightfloath画布新的高度(逻辑像素)

注意data_field元数据:Python 侧字段名为width/height,而在底层事件通道(Flutter → Python)中对应的键是w/h。这一点在 Dart 端有直接印证——canvas.dart 触发事件时发送的正是{"w": size.width, "h": size.height},随后由事件解析层依据data_field反序列化填充为CanvasResizeEvent.widthheight

导出路径

CanvasResizeEventCanvas一起通过以下路径对外公开,因此业务代码中最常见的导入方式是:

import flet.canvas as cv # cv.CanvasResizeEvent

也可直接使用flet.canvas.CanvasResizeEvent。其导出位置可见 flet/canvas/init.py 中的__all__列表。

二、事件从何而来:Canvas.on_resize 与 resize_interval

CanvasResizeEvent不会凭空产生,它由Canvas控件的on_resize事件处理器派发。在 canvas.py 中,与尺寸变化相关的有两个关键成员:

resize_interval: Number = 10 """ Sampling interval in milliseconds for `on_resize` event. Setting to `0` calls :attr:`on_resize` immediately on every change. """ on_resize: Optional[EventHandler[CanvasResizeEvent]] = None """ Called when the size of this canvas has changed. """
  • on_resize:类型为EventHandler[CanvasResizeEvent],回调签名即为(e: CanvasResizeEvent) -> None。当画布尺寸变化时被调用。
  • resize_interval采样间隔,单位毫秒,默认 10。这是事件触发的节流(throttle)参数——即使底层渲染在连续变化,on_resize最多每resize_interval毫秒派发一次,避免高频重绘导致 Python 侧回调风暴。设置为0则禁用节流,尺寸每次变化都会立即触发。

底层触发链路(源码印证)

从 Flutter 侧实现可以完整还原事件产生的过程。在 canvas.dart 的build方法中:

var resizeInterval = widget.control.getInt("resize_interval", 10)!;

Dart 端读取resize_interval(默认 10),随后在自定义画笔FletCustomPainteronPaintCallback回调中检测尺寸变化:

onPaintCallback: (size) { var now = DateTime.now().millisecondsSinceEpoch; if ((now - _lastResize > resizeInterval && _lastSize != size) || _lastSize.isEmpty) { _lastSize = size; _lastResize = now; widget.control .triggerEvent("resize", {"w": size.width, "h": size.height}); } },

这段代码清晰地揭示了三个事实:

  1. 测量时机:尺寸在每次绘制(paint)时被重新测量,因此能实时反映窗口缩放、布局变化带来的实际渲染尺寸。
  2. 节流逻辑:只有「距上次派发超过resize_interval毫秒」且「尺寸确实与上次不同」(或首次绘制)时才触发resize事件,Payload 为{"w": ..., "h": ...}
  3. 通道对接:事件名resize与 Python 侧on_resize对应;Payload 键w/hCanvasResizeEvent字段的data_field一一对应,完成跨端数据还原。

三、官方示例:随窗口缩放重绘对角线

仓库自带了完整可运行的官方示例 resize/main.py,演示了「监听画布尺寸变化 → 动态更新图形坐标」的标准写法:

import flet as ft import flet.canvas as cv def main(page: ft.Page): def paint_resize(e: cv.CanvasResizeEvent): print("On resize:", e.width, e.height) canvas.shapes[0].x2 = e.width canvas.shapes[0].y2 = e.height canvas.shapes[1].y1 = e.height canvas.shapes[1].x2 = e.width canvas.update() page.add( ft.SafeArea( expand=True, content=ft.Container( width=float("inf"), expand=True, content=( canvas := cv.Canvas( resize_interval=10, on_resize=paint_resize, shapes=[ cv.Line(x1=0, y1=0, x2=100, y2=100), cv.Line(x1=0, y1=100, x2=100, y2=0), ], ) ), ), ) ) if __name__ == "__main__": ft.run(main)

拆解其中的关键设计:

  1. 事件处理器签名paint_resize(e: cv.CanvasResizeEvent)接收类型化的事件对象,直接读取e.widthe.height获得画布当前宽高。
  2. 画布必须可伸展Container使用width=float("inf")配合expand=TrueSafeArea同样expand=True,使 Canvas 随页面/窗口尺寸变化。从源码看,Canvas.before_update()expand=True且未显式设置width/height时会自动填充无穷大值(见 canvas.py),从而铺满可用空间——这正是画布「能被 resize」的前提。
  3. 响应式更新图形:两条对角线的端点坐标被动态改写为画布边界(如第一条线终点(e.width, e.height)),随后调用canvas.update()将新坐标同步回 Flutter 端重绘。窗口拉大时对角线随之延伸,实现视觉上的「画布跟随窗口缩放」。
  4. 节流参数resize_interval=10与默认值一致,实际运行时可尝试调大(如 100/200)观察回调频率变化,或设为0观察无节流时的即时触发。

该示例在 pyproject.toml 中被登记为官方 Gallery 示例(title = "Canvas resize"features = ["on_resize event", "responsive line drawing"]),可作为新建应用时的最小模板参考。

四、事件对象继承能力:Event 基类

CanvasResizeEvent继承自泛型基类Event["Canvas"](定义于 control_event.py),因此事件处理器中除了e.width/e.height,还可以访问以下通用成员:

成员类型说明
e.namestr事件名称(此处为"resize"
e.dataAny原始事件载荷(dictstr,未解析前的原始数据)
e.controlCanvas派发该事件的画布控件实例,可直接访问并修改其属性
e.pagePage事件源控件所属的页面(若控件未挂载到页面会抛出RuntimeError
e.targetint派发事件控件的内部 id

借助e.control可以写出更精简的回调——例如在处理器内直接通过e.control.shapes[...]修改图形,而不必依赖外部闭包变量canvas。类型化的EventHandler[CanvasResizeEvent]意味着 IDE 与类型检查器能正确推断e的类型,这也是 Flet 事件系统「按控件类型解析事件载荷」机制的体现——相关解析逻辑get_event_field_type同样位于 control_event.py,并在 test_events.py 中有对应的类型断言测试。

五、实战场景与使用建议

1. 响应式绘图

最常见的用途就是本节官方示例展示的「坐标跟随画布尺寸」。适用于:自适应边框、随窗口缩放的网格线、图表背景、签名板清空重绘等。核心套路固定为三步:

def on_canvas_resize(e: cv.CanvasResizeEvent): # 1. 根据 e.width / e.height 重新计算图形坐标 # 2. 修改 canvas.shapes 中对应 shape 的属性 # 3. 调用 canvas.update() 推送更新 pass

2. 高频场景的节流取舍

resize_interval的单位是毫秒,默认 10ms 意味着每秒最多约 100 次回调,足以保证交互流畅性。如果回调体内包含重计算(如逐点重算贝塞尔曲线、遍历上千个 shape),建议调大该值(50–200ms);如果追求极致的即时反馈(如拖拽分隔条实时预览),可设为0,但要注意连续拖动窗口时回调可能非常密集。

3. 与布局控件配合

只有画布实际占据的空间发生变化,事件才会派发。要让画布具备「可 resize」的布局前提,通常采用:

  • 外层容器expand=True(或配合width=float("inf"));
  • 或为Canvas直接设置expand=True
  • 或显式固定width/height后由父级布局(如ColumnStack)驱动尺寸变化。

从源码看,Canvas继承自LayoutControl,本身具备布局能力,且before_update()会在expand=True时自动补全无穷大尺寸,这保证了展开模式下画布能跟随父容器铺满。

4. 与其他「尺寸类事件」区分

需要注意CanvasResizeEvent与页面级事件(如Pageon_resize,对应PageResizeEvent)作用对象不同:前者反映单个画布控件的渲染尺寸,后者反映整个页面/窗口尺寸。在test_events.py中可以看到PageResizeEvent的事件类型解析同样依赖get_event_field_type机制,二者属于同一套类型化事件体系的不同实例。绘制逻辑若只关心画布本身,优先使用Canvas.on_resize,无需关心窗口级状态。

六、小结与深入学习

CanvasResizeEvent是 Flet Canvas 响应式能力的核心入口:Python 侧通过Canvas.on_resize接收,字段width/height(序列化键w/h)携带逻辑像素尺寸,resize_interval控制派发频率,底层由 Flutter 端在每次绘制时测量并节流触发。掌握了它,即可让任何绘图应用随窗口缩放实时重绘。

继续深入可参考以下仓库资源:

  • 类型定义与Canvas完整成员:sdk/python/packages/flet/src/flet/controls/core/canvas/canvas.py
  • 官方可运行示例:sdk/python/examples/controls/core/canvas/resize/main.py
  • 底层 Flutter 实现与节流逻辑:packages/flet/lib/src/controls/canvas.dart
  • 事件基类与类型解析机制:sdk/python/packages/flet/src/flet/controls/control_event.py
  • 事件类型解析测试:sdk/python/packages/flet/tests/test_events.py
  • Canvas 控件总览文档:website/docs/controls/canvas/index.md
  • 前端
  • 跨平台
  • 桌面应用
  • 移动开发

【免费下载链接】flet

Build realtime web, mobile and desktop apps in Python only. No frontend experience required.

项目地址:https://gitcode.com/gh_mirrors/fl/flet
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询