主题
拖放控件 (Drag and Drop)
Flet 中的拖放(Drag-and-Drop)机制非常简单 —— 用户开始拖拽 Draggable 控件,并将其“释放”在 DragTarget 控件上。如果拖拽控件和拖拽目标属于同一个分组(group),拖拽目标将调用 on_accept 事件处理函数,并将拖拽控件本身作为 e.src(其 ID 作为 e.src_id)传入。在这种情况下,拖拽控件将作为拖放操作的源"数据"。
让我们来看下面的例子。在下面的程序中,你可以拖动左侧显示 “1” 的控件,并将其放置在右侧显示 “0” 的控件上。当拖动操作完成后,左侧控件会被替换为 “0”,而右侧控件会变成 “1”:
python
import flet as ft
def main(page: ft.Page):
page.title = "Drag and Drop example"
def drag_accept(e):
# 更新拖拽源(Draggable)控件内部 the text
e.src.content.content.value = "0"
# 更新拖拽目标(DragTarget)控件内部 the text
e.control.content.content.value = "1"
page.update()
page.add(
ft.Row(
controls=[
ft.Draggable(
group="number",
content=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.CYAN_200,
border_radius=5,
content=ft.Text("1", size=20),
alignment=ft.Alignment.CENTER,
),
),
ft.Container(width=100),
ft.DragTarget(
group="number",
content=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.PINK_200,
border_radius=5,
content=ft.Text("0", size=20),
alignment=ft.Alignment.CENTER,
),
on_accept=drag_accept,
),
]
)
)
ft.run(main)
因此,当触发 on_accept 事件时,决定“源”控件(Draggable)和“目标”控件(DragTarget)如何变化是开发者的职责。
尝试一些操作
将 DragTarget 的 group 属性更改为 number1,然后注意:当你再次将 “1” 拖放到目标上时,on_accept 事件将不会被触发。
Flet 还提供了其他的属性和事件处理函数,可以让拖放操作更加富有交互性。例如,Draggable 拥有 content_when_dragging 属性,用于在拖动过程中展示一个不同的控件来代替原本的 content 控件。此外,还有一个 content_feedback 属性,用于在指针下方显示一个不同的控件。默认情况下,拖动时指针下方会显示相同的 content 控件,但其不透明度(opacity)会降低为 50%。
让我们修改示例中的 Draggable,在控件被拖走的原位置处显示一个“虚位(hole)”,并在拖动时让指针下方仅显示一个简单的 “1”:
python
...
ft.Draggable(
group="number",
content=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.CYAN_200,
border_radius=5,
content=ft.Text("1", size=20),
alignment=ft.Alignment.CENTER,
),
content_when_dragging=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.BLUE_GREY_200,
border_radius=5,
),
content_feedback=ft.Text("1"),
),
...
拖拽目标(DragTarget)控件还额外提供了 on_will_accept 和 on_leave 事件处理函数,它们可以更好地可视化提示何时适合将物体“释放”在目标上。让我们修改示例中的 DragTarget,当其准备好接受拖入的元素时,在目标控件的周围绘制一个边框:
python
import flet as ft
def main(page: ft.Page):
page.title = "Drag and Drop example 2"
def drag_accept(e):
# 更新拖拽源(Draggable)控件内部 the text
e.src.content.content.value = "0"
# 重置源控件的分组,使其不能再被拖放到目标上
e.src.group = ""
# 更新拖拽目标(DragTarget)控件内部 the text
e.control.content.content.value = "1"
# 重置边框
e.control.content.border = None
page.update()
def drag_will_accept(e):
# 允许释放时显示黑色边框,不允许时显示红色边框
e.control.content.border = ft.Border.all(
2, ft.Colors.BLACK_45 if e.data == "true" else ft.Colors.RED
)
e.control.update()
def drag_leave(e):
e.control.content.border = None
e.control.update()
page.add(
ft.Row(
controls=[
ft.Draggable(
group="number",
content=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.CYAN_200,
border_radius=5,
content=ft.Text("1", size=20),
alignment=ft.Alignment.CENTER,
),
content_when_dragging=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.BLUE_GREY_200,
border_radius=5,
),
content_feedback=ft.Text("1"),
),
ft.Container(width=100),
ft.DragTarget(
group="number",
content=ft.Container(
width=50,
height=50,
bgcolor=ft.Colors.PINK_200,
border_radius=5,
content=ft.Text("0", size=20),
alignment=ft.Alignment.CENTER,
),
on_accept=drag_accept,
on_will_accept=drag_will_accept,
on_leave=drag_leave,
),
]
)
)
ft.run(main)