Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add read_image api to ui.clipboard #4144

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions nicegui/functions/clipboard.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import io
from PIL import Image

from .. import json
from ..logging import log
from .javascript import run_javascript


async def read() -> str:
async def read_text() -> str:
"""Read text from the clipboard.

Note: This function only works in secure contexts (HTTPS or localhost).
Expand All @@ -21,7 +24,7 @@ async def read() -> str:
return result or ''


def write(text: str) -> None:
def write_text(text: str) -> None:
"""Write text to the clipboard.

Note: This function only works in secure contexts (HTTPS or localhost).
Expand All @@ -36,3 +39,32 @@ def write(text: str) -> None:
console.error('Clipboard API is only available in secure contexts (HTTPS or localhost).')
}}
''')


async def read_image() -> Image.Image | None:
"""
Read images from the clipboard.
Note: This function only works in secure contexts (HTTPS or localhost).
"""
content = await run_javascript('''
if (navigator.clipboard) {
var items = await navigator.clipboard.read()
var images = []
for(var item of items){
if(item.types.length>0 && /^image/.test(item.types[0])){
var blob = await item.getType(item.types[0])
images.push(blob)
break
}
}
//console.log(images)
return images
}
else {
console.error('Clipboard API is only available in secure contexts (HTTPS or localhost).')
return []
}
''', timeout=5)
if content:
buffer = io.BytesIO(content[0])
return Image.open(buffer)
4 changes: 2 additions & 2 deletions tests/test_clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
def test_clipboard(screen: Screen):
@ui.page('/')
def page():
ui.button('Copy to clipboard', on_click=lambda: ui.clipboard.write('Hello, World!'))
ui.button('Copy to clipboard', on_click=lambda: ui.clipboard.write_text('Hello, World!'))

async def read_clipboard():
ui.notify('Clipboard: ' + await ui.clipboard.read())
ui.notify('Clipboard: ' + await ui.clipboard.read_text())
ui.button('Read from clipboard', on_click=read_clipboard)

screen.open('/')
Expand Down
24 changes: 17 additions & 7 deletions website/documentation/content/clipboard_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


@doc.demo('Read and write to the clipboard', '''
The following demo shows how to use `ui.clipboard.read()` and `ui.clipboard.write()` to interact with the clipboard.
The following demo shows how to use `ui.clipboard.read_text()`, `ui.clipboard.write_text()` and `ui.clipboard.read_image()` to interact with the clipboard.

Because auto-index page can be accessed by multiple browser tabs simultaneously, reading the clipboard is not supported on this page.
This is only possible within page-builder functions decorated with `ui.page`, as shown in this demo.
Expand All @@ -15,22 +15,32 @@ def main_demo() -> None:
# @ui.page('/')
# async def index():
with ui.column(): # HIDE
ui.button('Write', on_click=lambda: ui.clipboard.write('Hi!'))
ui.button('Write Text', on_click=lambda: ui.clipboard.write_text('Hi!'))

async def read() -> None:
ui.notify(await ui.clipboard.read())
ui.button('Read', on_click=read)
async def read_text() -> None:
ui.notify(await ui.clipboard.read_text())
ui.button('Read Text', on_click=read_text)

image = ui.image()

async def read_image() -> None:
img = ui.clipboard.read_image()
if not img:
ui.notify("you must copy an image to clipboard first.")
else:
image.set_source(img)
ui.button('Read Image', on_click=read_image)


@doc.demo('Client-side clipboard', '''
In order to avoid the round-trip to the server, you can also use the client-side clipboard API directly.
This might be supported by more browsers because the clipboard access is directly triggered by a user action.
''')
def client_side_clipboard() -> None:
ui.button('Write').on('click', js_handler='''
ui.button('Write Text').on('click', js_handler='''
() => navigator.clipboard.writeText("Ho!")
''')
ui.button('Read').on('click', js_handler='''
ui.button('Read Text').on('click', js_handler='''
async () => emitEvent("clipboard", await navigator.clipboard.readText())
''')
ui.on('clipboard', lambda e: ui.notify(e.args))