Skip to content

Clipboard 继承自:Service, BaseControl

提供对系统剪贴板的访问。

在 Web 应用中复制

浏览器只允许页面在处理点击或按键期间写入剪贴板,而从事件处理器中调用 set() 已经太晚了 —— 点击已经传到你的 Python 代码又传了回来。Safari 会静默拒绝;Chrome 和 Firefox 更宽容,所以同一个应用往往在所有平台上都能用,唯独 iPhone 和 iPad 上不行。

改为使用 CopyToClipboard 动作,由客户端在原始手势内部执行。参见 客户端动作。

通过客户端动作复制 ​

python
import flet as ft


def main(page: ft.Page):
    # `action` is performed by the client while it is still handling the click.
    # That is the only moment Safari lets a page write to the clipboard, which
    # is why `Clipboard().set()` - which has to reach Python first - does
    # nothing on iOS.
    token = "flet-1234-5678"

    def handle_copied(e):
        page.show_dialog(ft.SnackBar(ft.Text("Copied to clipboard")))

    page.add(
        ft.SafeArea(
            content=ft.Column(
                controls=[
                    ft.Text(f"Token: {token}", selectable=True),
                    ft.Button(
                        "Copy token",
                        icon=ft.Icons.CONTENT_COPY,
                        action=ft.CopyToClipboard(token),
                        on_click=handle_copied,
                    ),
                    ft.Divider(),
                    ft.Text(
                        "An action's arguments are fixed before the click, so "
                        "to copy something typed just now, update the action "
                        "as the text changes."
                    ),
                    note := ft.TextField(
                        label="Note",
                        value="Edit me, then copy",
                        on_change=lambda e: setattr(
                            copy_note, "action", ft.CopyToClipboard(note.value)
                        ),
                    ),
                    copy_note := ft.Button(
                        "Copy note",
                        icon=ft.Icons.CONTENT_COPY,
                        action=ft.CopyToClipboard("Edit me, then copy"),
                        on_click=handle_copied,
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)

示例 ​

python
import flet as ft


async def main(page: ft.Page):
    async def set_to_clipboard():
        await ft.Clipboard().set(text_to_copy.value)
        text_to_copy.value = ""
        page.show_dialog(ft.SnackBar("Text copied to clipboard"))

    async def get_from_clipboard():
        contents = await ft.Clipboard().get()
        page.add(ft.Text(f"Clipboard contents: {contents}"))

    page.add(
        ft.SafeArea(
            content=ft.Column(
                controls=[
                    ft.Column(
                        controls=[
                            text_to_copy := ft.TextField(label="Text to copy"),
                            ft.Button("Set to clipboard", on_click=set_to_clipboard),
                            ft.Button(
                                "Get from clipboard", on_click=get_from_clipboard
                            ),
                        ],
                    )
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import base64

import flet as ft

SAMPLE_PNG = base64.b64decode(
    "iVBORw0KGgoAAAANSUhEUgAAABkAAAAgCAYAAADnnNMGAAAACXBIWXMAAAORAAADkQFnq8zdAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA6dJREFUSImllltoHFUYx3/fzOzm0lt23ZrQ1AQbtBehNpvQohgkBYVo410RwQctNE3Sh0IfiiBoIAjqi6TYrKnFy4O3oiiRavDJFi3mXomIBmOxNZe63ay52GR3Zj4f2sTEzmx3m//TYf7/c35zvgPnO6KqrESXqpq3muocAikv6m+/zytj3ejik1VN21G31YA9CgJ6xC+bMyQZPVCuarciPAMYC99V6Vw5pLbFSibHmlVoRVj9P3cmPBM8tSJI/M6mzabpfoAQ9fIF7WK4bd5vvuFnLGgy2vi0abg94A0AcJGvMq3hDxGRyar9r4F+iLAm0yIiRk8m37tctS1WsrIhhrI30+Srmg+J87OXUf3lWGS1q89dC6ltsSanxk4Aj2QBABii96300g87P/rtlrWr8l+vyDMfdlXSyyEikqxsiOUAQJCBhfHdXRfCq1LSsSlcWG+KBAGStvvrMkgiuv8lUc2mREukPwLUfHG+uTQv8Eown7VL3XlbBxYhf1c17hbVF3MDwA9bts280TnaU1YYqPby07aeFlUlHt27wSQ4CLo+F8AvoTCvHmyKF+ZbEb/M77P2LgvAwmrTHAHflN3KZxVbMC2jMFNOpgPnrMSOhvvFkMezXdwV4ePbtvHtxnJAMQ0j4JtVnO+eLb5oiSlt5HDbv7t1O90lpYCCCKbhfzW5kAIwUAazR0BlfII8Ow0I6uoVmI9MyAMwbMs8CExmDbk4zgu931MyO4OI4KrYflkRjOoTI+uM9d1vjotwKPu9QMk/sxzuO8POiVFcdZ1M2YBVsMEAKOqLvaPIe7mACuw0z/80SMH58SMplxlfiDhVi7dw2pltRhjKBQTQdrSja2KKTfE551NHuaZ0QVPvWYQUn31/Vm2nDvgjF4grVJx6suSvrvrSJ/6cSW2Oz9mf264uNrB806xZ1k/CZ49dUKgDEtlCROX2hfHpx8pGuuo3PpqYulw8fjndOp1yhgtNKRevJ1FyR2Ola+jXAjdnwTkZ6o896GdWdxDw7IxFg+0DpmXchTKSBWQnIuJn9u4j7dt+13UfHXEkXQOcuQ4kMhVtqsgUyPiQiPQfHw1NB2sRjmXKuTg1NwwBYLhtPtQX26eqTwGXPDOqvmcC4Hnwfrrad94GrVsOYTqUTkQY+iTlNe/6O1miSP/x0VB/+wMIDwHn/vtV1iQC4Xv95uUEWVCoL9Y5Z+gdovoyMHUFJHv88jmVy0vTuw7cZNv2YaA61Bfb7ZX5F8SaUv2xwZevAAAAAElFTkSuQmCC"
)


async def main(page: ft.Page):
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

    async def set_image_to_clipboard(e: ft.Event[ft.Button]):
        await ft.Clipboard().set_image(SAMPLE_PNG)
        status.value = f"Sample image copied to clipboard ({len(SAMPLE_PNG)} bytes)."
        preview.content = ft.Image(src=SAMPLE_PNG)

    async def get_image_from_clipboard(e: ft.Event[ft.Button]):
        clipboard_image = await ft.Clipboard().get_image()
        if clipboard_image is None:
            status.value = "No image found in clipboard."
            preview.content = None
            return
        else:
            preview.content = ft.Image(src=clipboard_image)
            status.value = (
                f"Image loaded from clipboard ({len(clipboard_image)} bytes)."
            )

    page.add(
        ft.SafeArea(
            ft.Column(
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    ft.Button(
                        "Set example image to clipboard",
                        on_click=set_image_to_clipboard,
                        disabled=not (page.web or page.platform.is_mobile()),
                        tooltip="Supported on mobile platforms only."
                        if not (page.web or page.platform.is_mobile())
                        else None,
                    ),
                    ft.Button(
                        "Get image from clipboard",
                        on_click=get_image_from_clipboard,
                    ),
                    status := ft.Text(),
                    preview := ft.Container(width=360, height=240),
                ],
            )
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import os
import subprocess
import sys
import tempfile
from pathlib import Path

import flet as ft


def open_file(_, path: str):
    """Open a desktop file in the default application."""
    if os.path.isfile(path):
        if sys.platform == "darwin":  # macOS
            subprocess.run(["open", path])
        elif sys.platform == "win32":  # Windows
            os.startfile(path)
        else:  # Linux / BSD
            subprocess.run(["xdg-open", path])


async def main(page: ft.Page):
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

    # supported-platform checkers
    supports_set_files = not page.web and page.platform.is_desktop()
    supports_get_files = not page.web and (
        page.platform.is_desktop() or page.platform == ft.PagePlatform.ANDROID
    )

    def get_sample_files() -> list[str]:
        # create temporary files
        sample_dir = Path(tempfile.gettempdir()) / "flet_clipboard_files_example"
        sample_dir.mkdir(parents=True, exist_ok=True)
        sample_files = [
            sample_dir / "sample_1.txt",
            sample_dir / "sample_2.txt",
        ]
        sample_files[0].write_text("Clipboard sample file #1\n", encoding="utf-8")
        sample_files[1].write_text("Clipboard sample file #2\n", encoding="utf-8")
        return [str(p) for p in sample_files]

    async def set_files_to_clipboard(e: ft.Event[ft.Button]):
        files = get_sample_files()
        ok = await ft.Clipboard().set_files(files)
        status.value = (
            f"Set {len(files)} file references to clipboard (result: {ok})."
            if ok
            else "Failed to set file references to clipboard."
        )

    async def get_files_from_clipboard(e: ft.Event[ft.Button]):
        files = await ft.Clipboard().get_files()
        if files:
            files_lv.controls = [
                ft.Row(
                    alignment=ft.MainAxisAlignment.CENTER,
                    controls=[
                        ft.Text(f, selectable=True),
                        ft.IconButton(
                            icon=ft.Icons.OPEN_IN_NEW,
                            icon_size=15,
                            on_click=lambda e, f=f: open_file(e, f),
                            tooltip="Open file",
                        ),
                    ],
                )
                for f in files
            ]
        else:
            files_lv.controls = []
        status.value = f"Read {len(files)} file reference(s) from clipboard."

    page.add(
        ft.SafeArea(
            ft.Column(
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    ft.Button(
                        "Set example files to clipboard",
                        on_click=set_files_to_clipboard,
                        disabled=not supports_set_files,
                        tooltip="Supported on desktop platforms only."
                        if not supports_set_files
                        else None,
                    ),
                    ft.Button(
                        "Get files from clipboard",
                        on_click=get_files_from_clipboard,
                        disabled=not supports_get_files,
                        tooltip="Supported on Android and desktop platforms only."
                        if not supports_get_files
                        else None,
                    ),
                    status := ft.Text(),
                    files_lv := ft.Column(
                        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                        spacing=0,
                        controls=[],
                    ),
                ],
            )
        )
    )


if __name__ == "__main__":
    ft.run(main)

属性 ​

名称类型默认值说明
dataAnyskip_任意类型的自定义数据。
keyOptional['KeyValue']None用于在更新之间保持控件标识稳定的键。

只读属性 ​

名称类型默认值说明
parentOptional['BaseControl']—此控件的直接祖先(父级)。 默认为 None,只有在此控件被挂载(加入页面树)后才有值。 Page 控件(页面树的根)是个例外 —— 它的 parent 始终为 None。
page'Page'—此控件所属的页面。

方法 ​

名称类型默认值说明
setNone—将给定的数据存储到剪贴板。 参数: value:要存储到剪贴板的字符串数据。
getOptional[str]—从剪贴板检索数据。 返回: 从剪贴板检索到的字符串数据;如果剪贴板为空或不包含字符串数据则返回 None。
set_imageNone—将图片字节存储到剪贴板。 参数: value:要存储到剪贴板的图片数据(字节)。 抛出异常: FletUnsupportedPlatformException:如果调用的平台不是以下之一:Android、iOS、Web。
get_imageOptional[bytes]—从剪贴板检索图片数据。 返回: 从剪贴板检索到的图片数据(字节);如果剪贴板为空或不包含图片数据则返回 None。
set_filesbool—将文件引用存储到剪贴板。 参数: files:文件路径的列表。 返回: 如果操作成功则返回 True,否则返回 False。 抛出异常: FletUnsupportedPlatformException:如果调用的平台不是以下之一:macOS、Windows、Linux。
get_fileslist[str]—从剪贴板检索文件引用。 返回: 剪贴板中可用的文件引用列表。 在 Android 上,这些通常是 content URI。 抛出异常: FletUnsupportedPlatformException:如果调用的平台不是以下之一:Android、macOS、Windows、Linux。
init———
is_isolated——返回此控件是否被标记为隔离。 隔离控件会被排除在父级驱动的更新遍历之外,并需要自行管理更新边界。
build——在控件初始化期间调用一次,用于定义其子控件。在此方法中 page 属性可用。
before_update——每当此控件被更新时都会调用此方法。 注意: 务必不要在此处调用或请求 update()。
before_event——在事件处理器执行之前拦截事件。 返回 False 可取消分发。返回 True 或 None 则继续正常的事件处理。
did_mount——在控件挂载到页面树之后调用。 重写此方法以启动需要已挂载页面的资源,例如订阅、定时器或服务监听器。
will_unmount——在控件从页面树移除之前调用。 重写此方法以释放 did_mount() 中创建的资源,例如订阅、定时器或外部句柄。
get_data_channel——解析为此 widget 在 Dart 侧分配的 [DataChannel]。模式: on_data_channel_open: Optional[ft.EventHandler[DataChannelOpenEvent]] = None def init(self): self.on_data_channel_open = self._on_open def _on_open(self, e): self._channel = self.get_data_channel(e.channel_id) 幂等 —— 底层的 Connection 会按 id 缓存 DataChannel,因此重复调用会返回同一实例。没有错误路径:该 id 始终来自框架触发的事件,所以当此方法运行时,通道在两端都已存在。
updateNone—为此控件请求一次 UI 更新。 在更改控件状态或属性后调用。控件必须已挂载到页面且未被标记为冻结。

关注我,学习大模型、大数据、Python等热门技术。