interactive_image_documentation.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. from nicegui import ui
  2. from . import doc
  3. @doc.demo(ui.interactive_image)
  4. def main_demo() -> None:
  5. from nicegui import events
  6. def mouse_handler(e: events.MouseEventArguments):
  7. color = 'SkyBlue' if e.type == 'mousedown' else 'SteelBlue'
  8. ii.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="15" fill="none" stroke="{color}" stroke-width="4" />'
  9. ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')
  10. src = 'https://picsum.photos/id/565/640/360'
  11. ii = ui.interactive_image(src, on_mouse=mouse_handler, events=['mousedown', 'mouseup'], cross=True)
  12. @doc.demo('Adding layers', '''
  13. In some cases you might want to add different groups of SVG elements to an image.
  14. Maybe there is one element that needs frequent updates, while the other elements are rarely changed.
  15. Putting all elements in the same SVG can lead to performance issues,
  16. because the whole SVG needs to be sent to the client whenever one of the elements changes.
  17. The solution is to add multiple layers to the image.
  18. Each layer is a separate SVG element, which means that each layer can be updated independently.
  19. The following demo shows this concept in action, even though both layers are changed at the same time.
  20. *Added in version 2.17.0*
  21. ''')
  22. def adding_layers():
  23. from nicegui import events
  24. def mouse_handler(e: events.MouseEventArguments):
  25. image.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="30" fill="none" stroke="red" stroke-width="4" />'
  26. highlight.content = f'<circle cx="{e.image_x}" cy="{e.image_y}" r="28" fill="yellow" opacity="0.5" />'
  27. src = 'https://picsum.photos/id/674/640/360'
  28. image = ui.interactive_image(src, on_mouse=mouse_handler, cross=True)
  29. highlight = image.add_layer()
  30. @doc.demo('Nesting elements', '''
  31. You can nest elements inside an interactive image.
  32. Use Tailwind classes like "absolute top-0 left-0" to position the label absolutely with respect to the image.
  33. Of course this can be done with plain CSS as well.
  34. ''')
  35. def nesting_elements():
  36. with ui.interactive_image('https://picsum.photos/id/147/640/360'):
  37. ui.button(on_click=lambda: ui.notify('thumbs up'), icon='thumb_up') \
  38. .props('flat fab color=white') \
  39. .classes('absolute bottom-0 left-0 m-2')
  40. @doc.demo('Force reload', '''
  41. You can force an image to reload by calling the `force_reload` method.
  42. It will append a timestamp to the image URL, which will make the browser reload the image.
  43. ''')
  44. def force_reload():
  45. img = ui.interactive_image('https://picsum.photos/640/360').classes('w-64')
  46. ui.button('Force reload', on_click=img.force_reload)
  47. @doc.demo('Blank canvas', '''
  48. You can also create a blank canvas with a given size.
  49. This is useful if you want to draw something without loading a background image.
  50. ''')
  51. def blank_canvas():
  52. ui.interactive_image(
  53. size=(800, 600), cross=True,
  54. on_mouse=lambda e: e.sender.set_content(f'''
  55. <circle cx="{e.image_x}" cy="{e.image_y}" r="50" fill="orange" />
  56. '''),
  57. ).classes('w-64 bg-blue-50')
  58. @doc.demo('Loaded event', '''
  59. You can listen to the "loaded" event to know when the image has been loaded.
  60. ''')
  61. def loaded_event():
  62. import time
  63. ii = ui.interactive_image('https://picsum.photos/640/360')
  64. ii.on('loaded', lambda e: ui.notify(f'loaded {e.args}'))
  65. ui.button('Change Source', on_click=lambda: ii.set_source(f'https://picsum.photos/640/360?time={time.time()}'))
  66. @doc.demo('Crosshairs', '''
  67. You can show crosshairs by passing `cross=True`.
  68. You can also change the color of the crosshairs by passing a color string.
  69. *Since version 2.4.0:*
  70. You can use the `add_slot` method to add a custom "cross" slot with your own SVG template.
  71. The `props.x` and `props.y` variables will be available in the template, representing the crosshair position.
  72. ''')
  73. def crosshairs():
  74. ui.interactive_image('https://picsum.photos/id/565/640/360', cross='red')
  75. ui.interactive_image('https://picsum.photos/id/565/640/360').add_slot('cross', '''
  76. <circle :cx="props.x" :cy="props.y" r="30" stroke="red" fill="none" />
  77. <line :x1="props.x - 30" :y1="props.y" :x2="props.x + 30" :y2="props.y" stroke="red" />
  78. <line :x1="props.x" :y1="props.y - 30" :x2="props.x" :y2="props.y + 30" stroke="red" />
  79. ''')
  80. @doc.demo('SVG events', '''
  81. You can subscribe to events of the SVG elements by using the `on` method with an "svg:" prefix.
  82. Make sure to set `pointer-events="all"` for the SVG elements you want to receive events from.
  83. Currently the following SVG events are supported:
  84. - pointermove
  85. - pointerdown
  86. - pointerup
  87. - pointerover
  88. - pointerout
  89. - pointerenter
  90. - pointerleave
  91. - pointercancel
  92. ''')
  93. def svg_content():
  94. ui.interactive_image('https://picsum.photos/id/565/640/360', cross=True, content='''
  95. <rect id="A" x="85" y="70" width="80" height="60" fill="none" stroke="red" pointer-events="all" cursor="pointer" />
  96. <rect id="B" x="180" y="70" width="80" height="60" fill="none" stroke="red" pointer-events="all" cursor="pointer" />
  97. ''').on('svg:pointerdown', lambda e: ui.notify(f'SVG clicked: {e.args}'))
  98. doc.reference(ui.interactive_image)