page.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. # Copyright 2021-2024 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. from __future__ import annotations
  12. import inspect
  13. import typing as t
  14. from types import FrameType
  15. from .utils import _filter_locals, _get_module_name_from_frame
  16. if t.TYPE_CHECKING:
  17. from ._renderers import _Element # noqa: F401
  18. class Page:
  19. """Generic page generator.
  20. The `Page` class transforms template text into actual pages that can be displayed
  21. on a web browser.
  22. When a page is requested to be displayed, it is converted into HTML
  23. code that can be sent to the client. All control placeholders are
  24. replaced by their respective graphical component so you can show
  25. your application variables and interact with them.
  26. """
  27. def __init__(self, **kwargs) -> None:
  28. from .custom import Page as CustomPage
  29. self._class_module_name = ""
  30. self._class_locals: t.Dict[str, t.Any] = {}
  31. self._frame: t.Optional[FrameType] = None
  32. self._renderer: t.Optional[Page] = self.create_page()
  33. if "frame" in kwargs:
  34. self._frame = kwargs.get("frame")
  35. elif self._renderer:
  36. self._frame = self._renderer._frame
  37. elif isinstance(self, CustomPage):
  38. self._frame = t.cast(FrameType, t.cast(FrameType, inspect.stack()[2].frame))
  39. elif len(inspect.stack()) < 4:
  40. raise RuntimeError(f"Can't resolve module. Page '{type(self).__name__}' is not registered.")
  41. else:
  42. self._frame = t.cast(FrameType, t.cast(FrameType, inspect.stack()[3].frame))
  43. if self._renderer:
  44. # Extract the page module's attributes and methods
  45. cls = type(self)
  46. cls_locals = dict(vars(self))
  47. funcs = [
  48. i[0]
  49. for i in inspect.getmembers(cls)
  50. if not i[0].startswith("_") and (inspect.ismethod(i[1]) or inspect.isfunction(i[1]))
  51. ]
  52. for f in funcs:
  53. func = getattr(self, f)
  54. if hasattr(func, "__func__") and func.__func__ is not None:
  55. cls_locals[f] = func.__func__
  56. self._class_module_name = cls.__name__
  57. self._class_locals = cls_locals
  58. def create_page(self) -> t.Optional[Page]:
  59. """Create the page content for page modules.
  60. If this page is a page module, this method must be overloaded and return the page content.
  61. This method should never be called directly: only the Taipy GUI internals will.
  62. The default implementation returns None, indicating that this class does not implement
  63. a page module.
  64. Returns:
  65. The page content for this Page subclass, making it a page module.
  66. """
  67. return None
  68. def _get_locals(self) -> t.Optional[t.Dict[str, t.Any]]:
  69. return (
  70. self._class_locals
  71. if self._is_class_module()
  72. else None if (frame := self._get_frame()) is None else _filter_locals(frame.f_locals)
  73. )
  74. def _is_class_module(self):
  75. return self._class_module_name != ""
  76. def _get_frame(self):
  77. if not hasattr(self, "_frame"):
  78. raise RuntimeError(f"Page '{type(self).__name__}' was not registered correctly.")
  79. return self._frame
  80. def _get_module_name(self) -> t.Optional[str]:
  81. return (
  82. None
  83. if (frame := self._get_frame()) is None
  84. else f"{_get_module_name_from_frame(frame)}{'.' if self._class_module_name else ''}{self._class_module_name}" # noqa: E501
  85. )
  86. def _get_content_detail(self, gui) -> str:
  87. return f"in class {type(self).__name__}"
  88. def render(self, gui) -> str:
  89. if self._renderer is not None:
  90. return self._renderer.render(gui)
  91. return "<h1>No renderer found for page</h1>"