|
5 | 5 | from rich.table import Table |
6 | 6 | from rich.text import Text |
7 | 7 | from textual.app import ComposeResult |
8 | | -from textual.widgets import Static |
| 8 | +from textual.containers import HorizontalScroll, Vertical |
| 9 | +from textual.widgets import DataTable, Static |
9 | 10 |
|
10 | 11 | from datanomy.reader.parquet import ParquetReader |
11 | 12 | from datanomy.tui.common import create_column_grid |
@@ -736,6 +737,7 @@ def __init__(self, reader: ParquetReader, num_rows: int = 50) -> None: |
736 | 737 | """ |
737 | 738 | super().__init__(reader) |
738 | 739 | self.num_rows = num_rows |
| 740 | + self.id = "data-content" |
739 | 741 |
|
740 | 742 | @staticmethod |
741 | 743 | def _format_value(value: Any, max_length: int = 50) -> str: |
@@ -782,80 +784,91 @@ def _read_data(self) -> tuple[Any, int, int]: |
782 | 784 |
|
783 | 785 | return table, num_rows_display, total_rows |
784 | 786 |
|
785 | | - def _create_data_table(self, table: Any, num_rows_display: int) -> Panel: |
| 787 | + def _create_data_table(self, table: Any, num_rows_display: int) -> DataTable: |
786 | 788 | """ |
787 | | - Create Rich table with data rows. |
| 789 | + Create a Textual ``DataTable`` widget populated with preview rows. |
788 | 790 |
|
789 | 791 | Parameters |
790 | 792 | ---------- |
791 | | - table: PyArrow table |
792 | | - num_rows_display: Number of rows to display |
| 793 | + table: PyArrow table slice for preview rendering |
| 794 | + num_rows_display: Number of rows to include in the table |
793 | 795 |
|
794 | 796 | Returns |
795 | 797 | ------- |
796 | | - Panel: Rich Panel containing data table |
797 | | - """ |
798 | | - # Create Rich table |
799 | | - data_table = Table( |
800 | | - show_header=True, |
801 | | - header_style="bold cyan", |
802 | | - border_style="dim", |
803 | | - expand=False, |
804 | | - box=None, |
805 | | - ) |
| 798 | + DataTable: Configured widget ready for display |
| 799 | + """ |
| 800 | + |
| 801 | + data_table: DataTable = DataTable(id="data-preview-table", zebra_stripes=True) |
| 802 | + data_table.border_title = "Data Preview" |
| 803 | + data_table.styles.border = ("round", "cyan") |
| 804 | + data_table.styles.width = "auto" |
| 805 | + |
| 806 | + columns = list(table.schema.names) |
| 807 | + if not columns: |
| 808 | + return data_table |
| 809 | + |
| 810 | + min_width = max(80, sum(max(12, len(name) + 2) for name in columns)) |
| 811 | + data_table.styles.min_width = min_width |
806 | 812 |
|
807 | | - # Add columns |
808 | | - for col_name in table.schema.names: |
809 | | - data_table.add_column(col_name, style="white", no_wrap=False) |
| 813 | + data_table.add_columns(*columns) |
810 | 814 |
|
811 | | - # Add rows |
812 | | - for i in range(num_rows_display): |
| 815 | + for row_idx in range(num_rows_display): |
813 | 816 | row_values: list[str | Text] = [] |
814 | | - for col_name in table.schema.names: |
815 | | - value = table[col_name][i].as_py() |
| 817 | + for name in columns: |
| 818 | + value = table[name][row_idx].as_py() |
816 | 819 | formatted_value = self._format_value(value) |
817 | | - |
818 | | - # Style NULL values differently |
819 | 820 | if value is None: |
820 | 821 | row_values.append(Text(formatted_value, style="dim yellow")) |
821 | 822 | else: |
822 | 823 | row_values.append(formatted_value) |
823 | | - |
824 | 824 | data_table.add_row(*row_values) |
825 | 825 |
|
826 | | - # Wrap table in panel |
827 | | - return Panel( |
828 | | - data_table, |
829 | | - title="[cyan]Data Preview[/cyan]", |
830 | | - border_style="cyan", |
831 | | - ) |
| 826 | + return data_table |
832 | 827 |
|
833 | | - def render_tab_content(self) -> Group: |
| 828 | + def compose(self) -> ComposeResult: |
834 | 829 | """ |
835 | | - Render data preview table. |
| 830 | + Compose widgets for the data preview tab. |
836 | 831 |
|
837 | | - Returns |
838 | | - ------- |
839 | | - Group: Rich renderable showing data rows |
| 832 | + Yields |
| 833 | + ------ |
| 834 | + ComposeResult: Child widgets making up the tab content |
840 | 835 | """ |
841 | | - # Read data from the Parquet file |
| 836 | + |
842 | 837 | try: |
843 | 838 | table, num_rows_display, total_rows = self._read_data() |
844 | 839 | except Exception as e: |
845 | 840 | error_text = Text() |
846 | 841 | error_text.append(f"Error reading data: {e}", style="red") |
847 | | - return Group(Panel(error_text, title="[red]Error[/red]")) |
| 842 | + yield Static(Panel(error_text, title="[red]Error[/red]"), id="data-content") |
| 843 | + return |
| 844 | + |
| 845 | + columns = list(table.schema.names) |
| 846 | + data_widget: DataTable | None = None |
| 847 | + empty_panel: Panel | None = None |
| 848 | + |
| 849 | + if columns: |
| 850 | + data_widget = self._create_data_table(table, num_rows_display) |
| 851 | + else: |
| 852 | + empty_text = Text("Parquet table has no columns", style="yellow") |
| 853 | + empty_panel = Panel( |
| 854 | + empty_text, |
| 855 | + title="[cyan]Data Preview[/cyan]", |
| 856 | + border_style="cyan", |
| 857 | + ) |
848 | 858 |
|
849 | | - # Create header info |
850 | 859 | header_text = Text() |
851 | 860 | header_text.append( |
852 | 861 | f"Showing {num_rows_display:,} of {total_rows:,} rows", style="cyan bold" |
853 | 862 | ) |
854 | 863 |
|
855 | | - # Create data table |
856 | | - table_panel = self._create_data_table(table, num_rows_display) |
| 864 | + with Vertical(id="data-content"): |
| 865 | + yield Static(header_text) |
857 | 866 |
|
858 | | - return Group(header_text, Text(), table_panel) |
| 867 | + if data_widget is not None: |
| 868 | + with HorizontalScroll(): |
| 869 | + yield data_widget |
| 870 | + elif empty_panel is not None: |
| 871 | + yield Static(empty_panel) |
859 | 872 |
|
860 | 873 |
|
861 | 874 | class MetadataTab(BaseParquetTab): |
|
0 commit comments