From a1de9de63229bdacbb4f50bbb9b2fb05da7a004d Mon Sep 17 00:00:00 2001 From: Markus Binsteiner Date: Wed, 8 Nov 2023 10:05:40 +0100 Subject: [PATCH] refactor: move terminal client to its own plugin --- pyproject.toml | 4 - src/kiara/interfaces/tui/__init__.py | 0 src/kiara/interfaces/tui/pager.py | 133 ------- src/kiara/interfaces/tui/widgets/__init__.py | 0 src/kiara/interfaces/tui/widgets/pager.py | 364 ------------------- 5 files changed, 501 deletions(-) delete mode 100644 src/kiara/interfaces/tui/__init__.py delete mode 100644 src/kiara/interfaces/tui/pager.py delete mode 100644 src/kiara/interfaces/tui/widgets/__init__.py delete mode 100644 src/kiara/interfaces/tui/widgets/pager.py diff --git a/pyproject.toml b/pyproject.toml index d7d32e935..1161c9994 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -291,10 +291,6 @@ jupyter = [ "ipydagred3" ] -terminal = [ - "textual>=0.9.0,<0.11.0", -] - [project.urls] homepage = "https://github.com/DHARPA-Project/kiara" documentation = "https://dharpa.org/kiara.documentation" diff --git a/src/kiara/interfaces/tui/__init__.py b/src/kiara/interfaces/tui/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/kiara/interfaces/tui/pager.py b/src/kiara/interfaces/tui/pager.py deleted file mode 100644 index 5ac8c314f..000000000 --- a/src/kiara/interfaces/tui/pager.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Any, Mapping, Union - -import structlog -from textual import events -from textual.app import App, ComposeResult -from textual.binding import Binding -from textual.widgets import Footer, Header - -from kiara.interfaces import get_console -from kiara.interfaces.python_api import KiaraAPI -from kiara.interfaces.tui.widgets.pager import DataViewControl, DataViewPane -from kiara.models.rendering import RenderValueResult - -logger = structlog.getLogger() - -RESERVED_KEYS = ("q", "r") - - -class PagerApp(App): - - CSS = """ -DataViewPane { -} -DataViewControl { - dock: bottom; - padding: 1 0; -} -""" - BINDINGS = [Binding(key="q", action="quit", description="Quit")] - - def __init__( - self, - api: Union[None, KiaraAPI] = None, - value: Union[str, None] = None, - *args, - **kwargs, - ): - - if api is None: - api = KiaraAPI.instance() - - self._base_id = "data_preview" - - self._api: KiaraAPI = api - - self._init_value: Union[None, str] = value - - self._current_value: Union[None, str] = value - - self._current_render_config: Union[Mapping[str, Any], None] = None # type: ignore - self._current_result: Union[RenderValueResult, None] = None # type: ignore - - self._data_preview = DataViewPane(id=f"{self._base_id}.preview") - self._preview_control = DataViewControl(id=f"{self._base_id}.control") - self._preview_control.set_reserved_keys(RESERVED_KEYS) - - self._num_rows: int = 0 - self._control_height = 0 - - super().__init__(*args, **kwargs) - - def compose(self) -> ComposeResult: - """Create child widgets for the app.""" - yield Header() - yield self._data_preview - yield self._preview_control - yield Footer() - - def on_mount(self) -> None: - - self._current_value = self._init_value - self.update_scene() - - def action_redraw_preview(self) -> None: - - self.update_scene() - - def recalculate_num_rows(self) -> None: - - if self._control_height == 0: - ch = 0 - else: - ch = self._control_height + 2 - - self._num_rows = (get_console().size.height - 8) - ch - - def on_key(self, event: events.Key) -> None: - - if event.key == "r": - self.action_redraw_preview() - return - - scene = self._preview_control.get_scene_for_key(event.key) - if scene: - self._current_render_config = scene.render_config - self.update_scene() - - def update_scene(self): - - if not self._current_value: - self._control_height = self._preview_control.compute_related_scenes(None) - self._preview_control.commit() - self._data_preview.value_view = "-- no value --" - return - - self.recalculate_num_rows() - if self._current_render_config is None: - self._current_render_config = {} - self._current_render_config["number_of_rows"] = self._num_rows - self._current_render_config["display_width"] = get_console().size.width - 4 - - current_result = self._api.render_value( - value=self._current_value, - target_format="terminal_renderable", - render_config=self._current_render_config, - ) - control_height = self._preview_control.compute_related_scenes( - current_result.related_scenes - ) - - if control_height != self._control_height: - self._control_height = control_height - self.update_scene() - else: - self._current_result = current_result - self._data_preview.value_view = current_result.rendered - self._preview_control.commit() - - -if __name__ == "__main__": - app = PagerApp(value="journals_node.table") - app.run() diff --git a/src/kiara/interfaces/tui/widgets/__init__.py b/src/kiara/interfaces/tui/widgets/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/kiara/interfaces/tui/widgets/pager.py b/src/kiara/interfaces/tui/widgets/pager.py deleted file mode 100644 index 34807eb40..000000000 --- a/src/kiara/interfaces/tui/widgets/pager.py +++ /dev/null @@ -1,364 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Dict, Iterable, List, Mapping, Tuple, Union - -from rich import box -from rich.console import RenderableType -from rich.table import Table -from rich.text import Text -from rich.tree import Tree -from textual.reactive import reactive -from textual.widgets import Static - -from kiara.models.rendering import RenderScene - - -class DataViewPane(Static): - - """A widget that displays a data preview.""" - - value_view = reactive(None) - # is_scrollable = True - - def __init__(self, *args, **kwargs) -> None: - - super().__init__(*args, **kwargs) - - def watch_value_view(self, value_view: Union[RenderableType, None]) -> None: - """Update the data preview.""" - if not value_view: - self.update("-- no value --") - else: - self.update(value_view) - - -class DataViewControl(Static): - - current_value: Union[str, None] = None - related_scenes: Union[None, Mapping[str, Union[None, RenderScene]]] = None - scene_keys: Union[None, Dict[str, Union[None, RenderScene]]] = None - max_level: int = 0 - control_table: Union[None, RenderableType] = None - reserved_keys: Tuple[str, ...] = () - - def on_mount(self): - - self.compute_related_scenes(None) - self.commit() - - def set_reserved_keys(self, keys: Iterable[str]): - self.reserved_keys = tuple(keys) - - def get_title( - self, - key: str, - scene: Union[None, RenderScene], - scene_keys: Dict[str, Union[None, RenderScene]], - ) -> RenderableType: - - last_token = key.split(".")[-1] - - title = None - found_key = None - for idx, command_key in enumerate(last_token): - if ( - command_key.lower() not in self.reserved_keys - and command_key.lower() not in (x.lower() for x in scene_keys.keys()) - ): - replaced = last_token.replace(command_key, f"\[{command_key}]", 1) - if scene is None or scene.disabled: - title = Text.from_markup(f"[grey46]{replaced}[/grey46]") - else: - title = Text.from_markup(replaced) - found_key = command_key - break - - if title is None: - raise NotImplementedError("Could not find free command key.") - if found_key is None: - raise NotImplementedError("Invalid key.") - - scene_keys[found_key] = scene # tpye: ignore - return title - - def render_sub_command_table( - self, - scene: Union[None, RenderScene], - scene_keys: Dict[str, Union[None, RenderScene]], - forced_titles: Dict[str, RenderableType], - row_list: Union[None, List[List[RenderableType]]] = None, - level: int = 0, - ) -> Tuple[List[List[RenderableType]], int]: - - if row_list is None: - row_list = [] - - if not scene: - return (row_list, level) - - if not scene.related_scenes: - return (row_list, level) - - level = level + 1 - sub_list: List[RenderableType] = [] - - for scene_key, sub_scene in scene.related_scenes.items(): - - if scene_key in forced_titles.keys(): - new_scene_key: RenderableType = forced_titles[scene_key] - else: - new_scene_key = self.get_title( - key=scene_key, scene=sub_scene, scene_keys=scene_keys - ) - - sub_list.append(new_scene_key) - - row_list.append(sub_list) - - for scene_key, sub_scene in scene.related_scenes.items(): - _, level = self.render_sub_command_table( - scene=sub_scene, - scene_keys=scene_keys, - forced_titles=forced_titles, - row_list=row_list, - level=level, - ) - - return (row_list, level) - - def render_sub_command_tree( - self, - key: str, - scene: Union[RenderScene, None], - scene_keys: Dict[str, Union[None, RenderScene]], - forced_titles: Dict[str, RenderableType], - node: Union[Tree, None] = None, - level: int = 0, - ): - - if key in forced_titles.keys(): - title: RenderableType = forced_titles[key] - else: - title = self.get_title(key=key, scene=scene, scene_keys=scene_keys) - - if node is None: - node = Tree(title) - else: - node = node.add(title) - - if scene: - for scene_key, sub_scene in scene.related_scenes.items(): - _, level = self.render_sub_command_tree( - key=f"{key}.{scene_key}", - scene=sub_scene, - scene_keys=scene_keys, - forced_titles=forced_titles, - node=node, - level=level + 1, - ) - - return (node, level) - - def render_command_table(self) -> Tuple[RenderableType, int]: - - max_level = 0 - - if self.related_scenes is None: - return "", max_level - - scene_keys: Dict[str, Union[None, RenderScene]] = {} - - forced_titles = {} - for key, scene in self.related_scenes.items(): - - title = self.get_title(key=key, scene=scene, scene_keys=scene_keys) - forced_titles[key] = title - - render_as_tree = False - if render_as_tree: - row = [] - table = Table(show_header=False, box=box.SIMPLE) - for key, scene in self.related_scenes.items(): - - table.add_column(f"category: {key}") - node, level = self.render_sub_command_tree( - key=key, - scene=scene, - scene_keys=scene_keys, - forced_titles=forced_titles, - ) - row.append(node) - - control_height = ((level * 2) - 1) + 2 - if control_height > max_level: - max_level = control_height - - table.add_row(*row) - - else: - main_scene = RenderScene( - title=f"Value: {self.current_value}", - disabled=False, - description=f"Preview of value: {self.current_value}", - manifest_hash="", - render_config={}, - related_scenes=self.related_scenes, - ) - row_list, level = self.render_sub_command_table( - scene=main_scene, - scene_keys=scene_keys, - forced_titles=forced_titles, - ) - - table = Table(show_header=False, box=box.SIMPLE, show_lines=True) - if not row_list: - max_level = 0 - else: - max_len = max([len(x) for x in row_list]) - for i in range(0, max_len): - table.add_column(f"col_{i}") - - for row in row_list: - table.add_row(*row) - - if level > max_level: - max_level = level - - self.scene_keys = scene_keys - return table, max_level - - def compute_related_scenes( - self, related_scenes: Union[None, Mapping[str, Union[None, RenderScene]]] - ) -> int: - - self.related_scenes = related_scenes - self.control_table, self.max_level = self.render_command_table() - - return self.max_level - - def commit(self): - - self.update(self.control_table) - - def get_scene_for_key(self, key: str) -> Union[None, RenderScene]: - - if self.scene_keys is None: - return None - - if key in self.scene_keys.keys(): - return self.scene_keys[key] - - for x in self.scene_keys.keys(): - if x.lower() == key.lower(): - return self.scene_keys[x] - - return None - - -# class DataPreview(Static): -# -# CSS_PATH = os.path.join(KIARA_RESOURCES_FOLDER, "tui", "pager_app.css") -# -# # BINDINGS = [("v", "send_command('next')", "Next page"), ("p", "send_command('previous')", "Previous page")] -# can_focus = True -# is_scrollable = True -# -# class Command(Message): -# """Color selected message.""" -# -# def __init__(self, sender: MessageTarget, command: str) -> None: -# self.command = command -# super().__init__(sender) -# -# def __init__( -# self, -# api: KiaraAPI, -# base_id: str, -# value: Union[None, str] = None, -# *args, -# **kwargs, -# ): -# -# self._api: KiaraAPI = api -# self._current_value: Union[None, str] = value -# -# self._current_render_config: Union[Mapping[str, Any], None] = None # type: ignore -# self._current_result: Union[RenderValueResult, None] = None # type: ignore -# self._value_preview = DataViewPane() -# -# self._base_id = base_id -# self._preview = DataViewPane(id=f"{base_id}.preview") -# self._control = DataViewControl(id=f"{base_id}.control") -# -# super().__init__(*args, id=self._base_id, **kwargs) -# -# @property -# def control_height(self) -> int: -# return self._control_height -# -# def set_num_rows(self, num_rows: int): -# -# if self._num_rows == num_rows: -# return -# -# self._num_rows = num_rows -# # self._preview.styles.height = self._num_rows + 4 -# # self.styles.height = (self._num_rows + 4) + self._control_height + 2 -# self.update_scene(self._current_render_config) -# -# def on_mount(self): -# -# self._control.compute_related_scenes(None) -# self._control.commit() -# self._preview.value_view = "initializing..." -# -# def set_value(self, value: Union[None, str]) -> None: -# -# self._current_value = value -# self.update_scene({}) -# -# def on_key(self, event: events.Key) -> None: -# -# scene = self._control.get_scene_for_key(event.key) -# if scene: -# self.update_scene(scene.render_config) -# -# def compose(self) -> ComposeResult: -# -# yield self._preview -# yield self._control -# -# def update_scene(self, render_config: Union[None, Mapping[str, Any]]): -# -# if not self._current_value: -# self._preview.renderable = "-- no value --" -# self._control.compute_related_scenes(None) -# self._control.commit() -# return -# -# if render_config is None: -# return -# -# if self._num_rows <= 0: -# self._num_rows = 4 -# -# rc = dict(render_config) -# rc["number_of_rows"] = self._num_rows -# -# render_result = self._api.render_value( -# value=self._current_value, -# target_format="terminal_renderable", -# render_config=rc, -# ) -# -# max_level = self._control.compute_related_scenes(render_result.related_scenes) -# -# if max_level != self._control_height: -# self._control_height = max_level -# self.set_num_rows(self._num_rows - (self._control_height)) -# self.update_scene(render_config=rc) -# else: -# -# self._current_render_config = rc -# self._current_result = render_result -# self._preview.value_view = render_result.rendered -# self._control.commit()