You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
4.9 KiB

4 years ago
  1. from traitlets import Any, Unicode, List, Dict
  2. from ipywidgets import DOMWidget
  3. from ipywidgets.widgets.widget import widget_serialization
  4. from ._version import semver
  5. from .ForceLoad import force_load_instance
  6. import inspect
  7. from importlib import import_module
  8. OBJECT_REF = 'objectRef'
  9. FUNCTION_REF = 'functionRef'
  10. class Events(object):
  11. def __init__(self, **kwargs):
  12. self.on_msg(self._handle_event)
  13. self.events = [item[4:] for item in dir(self) if item.startswith("vue_")]
  14. def _handle_event(self, _, content, buffers):
  15. def resolve_ref(value):
  16. if isinstance(value, dict):
  17. if OBJECT_REF in value.keys():
  18. obj = getattr(self, value[OBJECT_REF])
  19. for path_item in value.get('path', []):
  20. obj = obj[path_item]
  21. return obj
  22. if FUNCTION_REF in value.keys():
  23. fn = getattr(self, value[FUNCTION_REF])
  24. args = value.get('args', [])
  25. kwargs = value.get('kwargs', {})
  26. return fn(*args, **kwargs)
  27. return value
  28. if 'create_widget' in content.keys():
  29. module_name = content['create_widget'][0]
  30. class_name = content['create_widget'][1]
  31. props = {k: resolve_ref(v) for k, v in content['props'].items()}
  32. module = import_module(module_name)
  33. widget = getattr(module, class_name)(**props, model_id=content['id'])
  34. self._component_instances = [*self._component_instances, widget]
  35. elif 'update_ref' in content.keys():
  36. widget = DOMWidget.widgets[content['id']]
  37. prop = content['prop']
  38. obj = resolve_ref(content['update_ref'])
  39. setattr(widget, prop, obj)
  40. elif 'destroy_widget' in content.keys():
  41. self._component_instances = [w for w in self._component_instances
  42. if w.model_id != content['destroy_widget']]
  43. elif 'event' in content.keys():
  44. event = content.get("event", "")
  45. data = content.get("data", {})
  46. getattr(self, 'vue_' + event)(data)
  47. def _value_to_json(x, obj):
  48. if inspect.isclass(x):
  49. return {
  50. 'class': [x.__module__, x.__name__],
  51. 'props': x.class_trait_names()
  52. }
  53. return widget_serialization['to_json'](x, obj)
  54. def _class_to_json(x, obj):
  55. if not x:
  56. return widget_serialization['to_json'](x, obj)
  57. return {k: _value_to_json(v, obj) for k, v in x.items()}
  58. def as_refs(name, data):
  59. def to_ref_structure(obj, path):
  60. if isinstance(obj, list):
  61. return [to_ref_structure(item, [*path, index]) for index, item in enumerate(obj)]
  62. if isinstance(obj, dict):
  63. return {k: to_ref_structure(v, [*path, k]) for k, v in obj.items()}
  64. # add object id to detect a new object in the same structure
  65. return {OBJECT_REF: name, 'path': path, 'id': id(obj)}
  66. return to_ref_structure(data, [])
  67. class VueTemplate(DOMWidget, Events):
  68. class_component_serialization = {
  69. 'from_json': widget_serialization['to_json'],
  70. 'to_json': _class_to_json
  71. }
  72. # Force the loading of jupyter-vue before dependent extensions when in a static context (embed,
  73. # voila)
  74. _jupyter_vue = Any(force_load_instance, read_only=True).tag(sync=True, **widget_serialization)
  75. _model_name = Unicode('VueTemplateModel').tag(sync=True)
  76. _view_name = Unicode('VueView').tag(sync=True)
  77. _view_module = Unicode('jupyter-vue').tag(sync=True)
  78. _model_module = Unicode('jupyter-vue').tag(sync=True)
  79. _view_module_version = Unicode(semver).tag(sync=True)
  80. _model_module_version = Unicode(semver).tag(sync=True)
  81. template = Unicode(None, allow_none=True).tag(sync=True)
  82. css = Unicode(None, allow_none=True).tag(sync=True)
  83. methods = Unicode(None, allow_none=True).tag(sync=True)
  84. data = Unicode(None, allow_none=True).tag(sync=True)
  85. events = List(Unicode(), default_value=None, allow_none=True).tag(sync=True)
  86. components = Dict(default_value=None, allow_none=True).tag(
  87. sync=True, **class_component_serialization)
  88. _component_instances = List().tag(sync=True, **widget_serialization)
  89. def __init__(self, *args, **kwargs):
  90. super().__init__(*args, **kwargs)
  91. sync_ref_traitlets = [v for k, v in self.traits().items()
  92. if 'sync_ref' in v.metadata.keys()]
  93. def create_ref_and_observe(traitlet):
  94. data = traitlet.get(self)
  95. ref_name = traitlet.name + '_ref'
  96. self.add_traits(**{ref_name: Any(as_refs(traitlet.name, data)).tag(sync=True)})
  97. def on_ref_source_change(change):
  98. setattr(self, ref_name, as_refs(traitlet.name, change['new']))
  99. self.observe(on_ref_source_change, traitlet.name)
  100. for traitlet in sync_ref_traitlets:
  101. create_ref_and_observe(traitlet)
  102. __all__ = ['VueTemplate']