Skip to content

Commit

Permalink
feat: Display image in a graphics_card #1817 (#2373)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbranley authored Aug 7, 2024
1 parent dd512ac commit 3ad44d6
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 5 deletions.
19 changes: 19 additions & 0 deletions py/examples/graphics_background.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Graphics / Background Image
# Set a background image behind your #graphics.
# Original example: https://docs.python.org/3/library/turtle.html
# ---
from h2o_wave import site, ui, graphics as g

t = g.turtle().f(100).r(90).pd()
for _ in range(36):
t.f(200).l(170)
spirograph = t.pu(1).path(stroke='red', fill='yellow')

page = site['/demo']
page['example'] = ui.graphics_card(
box='1 1 3 4', view_box='0 0 220 220', width='100%', height='100%',
scene=g.scene(foo=spirograph),
image_path='https://images.pexels.com/photos/1269968/pexels-photo-1269968.jpeg?auto=compress',
)

page.save()
1 change: 1 addition & 0 deletions py/examples/tour.conf
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,5 @@ graphics_clock.py
graphics_path.py
graphics_turtle.py
graphics_hilbert.py
graphics_background.py
glider_gun.py
30 changes: 30 additions & 0 deletions py/h2o_lightwave/h2o_lightwave/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8984,12 +8984,18 @@ def __init__(
scene: Optional[PackedData] = None,
width: Optional[str] = None,
height: Optional[str] = None,
image: Optional[str] = None,
image_path: Optional[str] = None,
image_type: Optional[str] = None,
commands: Optional[List[Command]] = None,
):
_guard_scalar('GraphicsCard.box', box, (str,), False, False, False)
_guard_scalar('GraphicsCard.view_box', view_box, (str,), False, False, False)
_guard_scalar('GraphicsCard.width', width, (str,), False, True, False)
_guard_scalar('GraphicsCard.height', height, (str,), False, True, False)
_guard_scalar('GraphicsCard.image', image, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_path', image_path, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_type', image_type, (str,), False, True, False)
_guard_vector('GraphicsCard.commands', commands, (Command,), False, True, False)
self.box = box
"""A string indicating how to place this component on the page."""
Expand All @@ -9003,6 +9009,12 @@ def __init__(
"""The displayed width of the rectangular viewport. (Not the width of its coordinate system.)"""
self.height = height
"""The displayed height of the rectangular viewport. (Not the height of its coordinate system.)"""
self.image = image
"""Background image data, base64-encoded."""
self.image_path = image_path
"""The path or URL or data URL of the background image, e.g. `/foo.png` or `http://example.com/foo.png` or `data:image/png;base64,???`."""
self.image_type = image_type
"""The background image MIME subtype. One of `apng`, `bmp`, `gif`, `x-icon`, `jpeg`, `png`, `webp`. Required only if `image` is set."""
self.commands = commands
"""Contextual menu commands for this component."""

Expand All @@ -9012,6 +9024,9 @@ def dump(self) -> Dict:
_guard_scalar('GraphicsCard.view_box', self.view_box, (str,), False, False, False)
_guard_scalar('GraphicsCard.width', self.width, (str,), False, True, False)
_guard_scalar('GraphicsCard.height', self.height, (str,), False, True, False)
_guard_scalar('GraphicsCard.image', self.image, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_path', self.image_path, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_type', self.image_type, (str,), False, True, False)
_guard_vector('GraphicsCard.commands', self.commands, (Command,), False, True, False)
return _dump(
view='graphics',
Expand All @@ -9021,6 +9036,9 @@ def dump(self) -> Dict:
scene=self.scene,
width=self.width,
height=self.height,
image=self.image,
image_path=self.image_path,
image_type=self.image_type,
commands=None if self.commands is None else [__e.dump() for __e in self.commands],
)

Expand All @@ -9037,6 +9055,12 @@ def load(__d: Dict) -> 'GraphicsCard':
_guard_scalar('GraphicsCard.width', __d_width, (str,), False, True, False)
__d_height: Any = __d.get('height')
_guard_scalar('GraphicsCard.height', __d_height, (str,), False, True, False)
__d_image: Any = __d.get('image')
_guard_scalar('GraphicsCard.image', __d_image, (str,), False, True, False)
__d_image_path: Any = __d.get('image_path')
_guard_scalar('GraphicsCard.image_path', __d_image_path, (str,), False, True, False)
__d_image_type: Any = __d.get('image_type')
_guard_scalar('GraphicsCard.image_type', __d_image_type, (str,), False, True, False)
__d_commands: Any = __d.get('commands')
_guard_vector('GraphicsCard.commands', __d_commands, (dict,), False, True, False)
box: str = __d_box
Expand All @@ -9045,6 +9069,9 @@ def load(__d: Dict) -> 'GraphicsCard':
scene: Optional[PackedData] = __d_scene
width: Optional[str] = __d_width
height: Optional[str] = __d_height
image: Optional[str] = __d_image
image_path: Optional[str] = __d_image_path
image_type: Optional[str] = __d_image_type
commands: Optional[List[Command]] = None if __d_commands is None else [Command.load(__e) for __e in __d_commands]
return GraphicsCard(
box,
Expand All @@ -9053,6 +9080,9 @@ def load(__d: Dict) -> 'GraphicsCard':
scene,
width,
height,
image,
image_path,
image_type,
commands,
)

Expand Down
9 changes: 9 additions & 0 deletions py/h2o_lightwave/h2o_lightwave/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3121,6 +3121,9 @@ def graphics_card(
scene: Optional[PackedData] = None,
width: Optional[str] = None,
height: Optional[str] = None,
image: Optional[str] = None,
image_path: Optional[str] = None,
image_type: Optional[str] = None,
commands: Optional[List[Command]] = None,
) -> GraphicsCard:
"""Create a card for displaying vector graphics.
Expand All @@ -3132,6 +3135,9 @@ def graphics_card(
scene: Foreground layer for rendering dynamic SVG elements.
width: The displayed width of the rectangular viewport. (Not the width of its coordinate system.)
height: The displayed height of the rectangular viewport. (Not the height of its coordinate system.)
image: Background image data, base64-encoded.
image_path: The path or URL or data URL of the background image, e.g. `/foo.png` or `http://example.com/foo.png` or `data:image/png;base64,???`.
image_type: The background image MIME subtype. One of `apng`, `bmp`, `gif`, `x-icon`, `jpeg`, `png`, `webp`. Required only if `image` is set.
commands: Contextual menu commands for this component.
Returns:
A `h2o_wave.types.GraphicsCard` instance.
Expand All @@ -3143,6 +3149,9 @@ def graphics_card(
scene,
width,
height,
image,
image_path,
image_type,
commands,
)

Expand Down
30 changes: 30 additions & 0 deletions py/h2o_wave/h2o_wave/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8984,12 +8984,18 @@ def __init__(
scene: Optional[PackedData] = None,
width: Optional[str] = None,
height: Optional[str] = None,
image: Optional[str] = None,
image_path: Optional[str] = None,
image_type: Optional[str] = None,
commands: Optional[List[Command]] = None,
):
_guard_scalar('GraphicsCard.box', box, (str,), False, False, False)
_guard_scalar('GraphicsCard.view_box', view_box, (str,), False, False, False)
_guard_scalar('GraphicsCard.width', width, (str,), False, True, False)
_guard_scalar('GraphicsCard.height', height, (str,), False, True, False)
_guard_scalar('GraphicsCard.image', image, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_path', image_path, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_type', image_type, (str,), False, True, False)
_guard_vector('GraphicsCard.commands', commands, (Command,), False, True, False)
self.box = box
"""A string indicating how to place this component on the page."""
Expand All @@ -9003,6 +9009,12 @@ def __init__(
"""The displayed width of the rectangular viewport. (Not the width of its coordinate system.)"""
self.height = height
"""The displayed height of the rectangular viewport. (Not the height of its coordinate system.)"""
self.image = image
"""Background image data, base64-encoded."""
self.image_path = image_path
"""The path or URL or data URL of the background image, e.g. `/foo.png` or `http://example.com/foo.png` or `data:image/png;base64,???`."""
self.image_type = image_type
"""The background image MIME subtype. One of `apng`, `bmp`, `gif`, `x-icon`, `jpeg`, `png`, `webp`. Required only if `image` is set."""
self.commands = commands
"""Contextual menu commands for this component."""

Expand All @@ -9012,6 +9024,9 @@ def dump(self) -> Dict:
_guard_scalar('GraphicsCard.view_box', self.view_box, (str,), False, False, False)
_guard_scalar('GraphicsCard.width', self.width, (str,), False, True, False)
_guard_scalar('GraphicsCard.height', self.height, (str,), False, True, False)
_guard_scalar('GraphicsCard.image', self.image, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_path', self.image_path, (str,), False, True, False)
_guard_scalar('GraphicsCard.image_type', self.image_type, (str,), False, True, False)
_guard_vector('GraphicsCard.commands', self.commands, (Command,), False, True, False)
return _dump(
view='graphics',
Expand All @@ -9021,6 +9036,9 @@ def dump(self) -> Dict:
scene=self.scene,
width=self.width,
height=self.height,
image=self.image,
image_path=self.image_path,
image_type=self.image_type,
commands=None if self.commands is None else [__e.dump() for __e in self.commands],
)

Expand All @@ -9037,6 +9055,12 @@ def load(__d: Dict) -> 'GraphicsCard':
_guard_scalar('GraphicsCard.width', __d_width, (str,), False, True, False)
__d_height: Any = __d.get('height')
_guard_scalar('GraphicsCard.height', __d_height, (str,), False, True, False)
__d_image: Any = __d.get('image')
_guard_scalar('GraphicsCard.image', __d_image, (str,), False, True, False)
__d_image_path: Any = __d.get('image_path')
_guard_scalar('GraphicsCard.image_path', __d_image_path, (str,), False, True, False)
__d_image_type: Any = __d.get('image_type')
_guard_scalar('GraphicsCard.image_type', __d_image_type, (str,), False, True, False)
__d_commands: Any = __d.get('commands')
_guard_vector('GraphicsCard.commands', __d_commands, (dict,), False, True, False)
box: str = __d_box
Expand All @@ -9045,6 +9069,9 @@ def load(__d: Dict) -> 'GraphicsCard':
scene: Optional[PackedData] = __d_scene
width: Optional[str] = __d_width
height: Optional[str] = __d_height
image: Optional[str] = __d_image
image_path: Optional[str] = __d_image_path
image_type: Optional[str] = __d_image_type
commands: Optional[List[Command]] = None if __d_commands is None else [Command.load(__e) for __e in __d_commands]
return GraphicsCard(
box,
Expand All @@ -9053,6 +9080,9 @@ def load(__d: Dict) -> 'GraphicsCard':
scene,
width,
height,
image,
image_path,
image_type,
commands,
)

Expand Down
9 changes: 9 additions & 0 deletions py/h2o_wave/h2o_wave/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3121,6 +3121,9 @@ def graphics_card(
scene: Optional[PackedData] = None,
width: Optional[str] = None,
height: Optional[str] = None,
image: Optional[str] = None,
image_path: Optional[str] = None,
image_type: Optional[str] = None,
commands: Optional[List[Command]] = None,
) -> GraphicsCard:
"""Create a card for displaying vector graphics.
Expand All @@ -3132,6 +3135,9 @@ def graphics_card(
scene: Foreground layer for rendering dynamic SVG elements.
width: The displayed width of the rectangular viewport. (Not the width of its coordinate system.)
height: The displayed height of the rectangular viewport. (Not the height of its coordinate system.)
image: Background image data, base64-encoded.
image_path: The path or URL or data URL of the background image, e.g. `/foo.png` or `http://example.com/foo.png` or `data:image/png;base64,???`.
image_type: The background image MIME subtype. One of `apng`, `bmp`, `gif`, `x-icon`, `jpeg`, `png`, `webp`. Required only if `image` is set.
commands: Contextual menu commands for this component.
Returns:
A `h2o_wave.types.GraphicsCard` instance.
Expand All @@ -3143,6 +3149,9 @@ def graphics_card(
scene,
width,
height,
image,
image_path,
image_type,
commands,
)

Expand Down
14 changes: 14 additions & 0 deletions r/R/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -3639,6 +3639,11 @@ ui_frame_card <- function(
#' (Not the width of its coordinate system.)
#' @param height The displayed height of the rectangular viewport.
#' (Not the height of its coordinate system.)
#' @param image Background image data, base64-encoded.
#' @param image_path The path or URL or data URL of the background image,
#' e.g. `/foo.png` or `http://example.com/foo.png` or `data:image/png;base64,???`.
#' @param image_type The background image MIME subtype. One of `apng`, `bmp`, `gif`, `x-icon`, `jpeg`, `png`, `webp`.
#' Required only if `image` is set.
#' @param commands Contextual menu commands for this component.
#' @return A GraphicsCard instance.
#' @export
Expand All @@ -3649,13 +3654,19 @@ ui_graphics_card <- function(
scene = NULL,
width = NULL,
height = NULL,
image = NULL,
image_path = NULL,
image_type = NULL,
commands = NULL) {
.guard_scalar("box", "character", box)
.guard_scalar("view_box", "character", view_box)
# TODO Validate stage: Recs
# TODO Validate scene: Data
.guard_scalar("width", "character", width)
.guard_scalar("height", "character", height)
.guard_scalar("image", "character", image)
.guard_scalar("image_path", "character", image_path)
.guard_scalar("image_type", "character", image_type)
.guard_vector("commands", "WaveCommand", commands)
.o <- list(
box=box,
Expand All @@ -3664,6 +3675,9 @@ ui_graphics_card <- function(
scene=scene,
width=width,
height=height,
image=image,
image_path=image_path,
image_type=image_type,
commands=commands,
view='graphics')
class(.o) <- append(class(.o), c(.wave_obj, "WaveGraphicsCard"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1473,13 +1473,16 @@
<option name="Python" value="true"/>
</context>
</template>
<template name="w_full_graphics_card" value="ui.graphics_card(box='$box$',view_box='$view_box$',stage=$stage$,scene=$scene$,width='$width$',height='$height$',commands=[&#10; $commands$ &#10;])$END$" description="Create Wave GraphicsCard with full attributes." toReformat="true" toShortenFQNames="true">
<template name="w_full_graphics_card" value="ui.graphics_card(box='$box$',view_box='$view_box$',stage=$stage$,scene=$scene$,width='$width$',height='$height$',image='$image$',image_path='$image_path$',image_type='$image_type$',commands=[&#10; $commands$ &#10;])$END$" description="Create Wave GraphicsCard with full attributes." toReformat="true" toShortenFQNames="true">
<variable name="box" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="view_box" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="stage" expression="" defaultValue="&quot;None&quot;" alwaysStopAt="true"/>
<variable name="scene" expression="" defaultValue="&quot;None&quot;" alwaysStopAt="true"/>
<variable name="width" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="height" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="image" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="image_path" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="image_type" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="commands" expression="" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
Expand Down
2 changes: 1 addition & 1 deletion tools/vscode-extension/component-snippets.json
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,7 @@
"Wave Full GraphicsCard": {
"prefix": "w_full_graphics_card",
"body": [
"ui.graphics_card(box='$1', view_box='$2', stage=${3:None}, scene=${4:None}, width='$5', height='$6', commands=[\n\t\t$7\t\t\n])$0"
"ui.graphics_card(box='$1', view_box='$2', stage=${3:None}, scene=${4:None}, width='$5', height='$6', image='$7', image_path='$8', image_type='$9', commands=[\n\t\t$10\t\t\n])$0"
],
"description": "Create a full Wave GraphicsCard."
},
Expand Down
26 changes: 23 additions & 3 deletions ui/src/graphics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ interface State {
* (Not the height of its coordinate system.)
*/
height?: S
/** Background image data, base64-encoded. */
image?: S
/**
* The path or URL or data URL of the background image,
* e.g. `/foo.png` or `http://example.com/foo.png` or `data:image/png;base64,???`.
*/
image_path?: S
/**
* The background image MIME subtype. One of `apng`, `bmp`, `gif`, `x-icon`, `jpeg`, `png`, `webp`.
* Required only if `image` is set.
*/
image_type?: S
}

const
Expand Down Expand Up @@ -169,14 +181,22 @@ export const
},
render = () => {
const
{ view_box, width, height, stage, scene } = state,
{ view_box, width, height, stage, scene, image_type, image, image_path } = state,
stageEls = stage ? unpack<Recs>(stage).map(renderEl) : [],
sceneEls = scene ? unpack<El[]>(scene).map(({ d, o }, i) => renderEl({
...(d ? JSON.parse(d) : {}),
...(o ? JSON.parse(o) : {}),
}, i)) : []
}, i)) : [],
backgroundImageSrc = image_path
? 'url(' + image_path + ')'
: image ? `url(data:image/${image_type};base64,${image})`
: undefined

return (
<div data-test={name} className={css.card}>
<div data-test={name} className={css.card} style={backgroundImageSrc ? {
backgroundImage: backgroundImageSrc,
backgroundSize: 'cover',
} : {}}>
<svg viewBox={view_box} width={width} height={height}>
<g>{stageEls}</g>
<g>{sceneEls}</g>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports = {
"widgets/content/tabs",
"widgets/content/markdown",
"widgets/content/image",
"widgets/content/graphics",
"widgets/content/article",
"widgets/content/tall_article_preview",
"widgets/content/wide_article_preview",
Expand Down
Loading

0 comments on commit 3ad44d6

Please sign in to comment.