dataframe-textual 0.2.1__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -549,7 +549,7 @@ class DataFrameTable(DataTable):
549
549
  return
550
550
 
551
551
  # Push the modal screen
552
- self.app.push_screen(RowDetailScreen(row_idx, self.df))
552
+ self.app.push_screen(RowDetailScreen(row_idx, self))
553
553
 
554
554
  def _show_frequency(self) -> None:
555
555
  """Show frequency distribution for the current column."""
@@ -558,9 +558,7 @@ class DataFrameTable(DataTable):
558
558
  return
559
559
 
560
560
  # Push the frequency modal screen
561
- self.app.push_screen(
562
- FrequencyScreen(col_idx, self.df.filter(self.visible_rows))
563
- )
561
+ self.app.push_screen(FrequencyScreen(col_idx, self))
564
562
 
565
563
  def _open_freeze_screen(self) -> None:
566
564
  """Open the freeze screen to set fixed rows and columns."""
@@ -1,11 +1,15 @@
1
1
  """Modal screens for displaying data in tables (row details and frequency)."""
2
2
 
3
- from typing import Any
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ if TYPE_CHECKING:
6
+ from .data_frame_table import DataFrameTable
4
7
 
5
8
  import polars as pl
6
9
  from rich.text import Text
7
10
  from textual.app import ComposeResult
8
11
  from textual.coordinate import Coordinate
12
+ from textual.renderables.bar import Bar
9
13
  from textual.screen import ModalScreen
10
14
  from textual.widgets import DataTable
11
15
 
@@ -32,14 +36,14 @@ class TableScreen(ModalScreen):
32
36
  }
33
37
  """
34
38
 
35
- def __init__(self, df: pl.DataFrame, id: str | None = None):
39
+ def __init__(self, dftable: DataFrameTable):
36
40
  super().__init__()
37
- self.df = df
38
- self.id = id
41
+ self.df: pl.DataFrame = dftable.df # Polars DataFrame
42
+ self.dftable = dftable # DataFrameTable
39
43
 
40
44
  def compose(self) -> ComposeResult:
41
45
  """Create the table. Must be overridden by subclasses."""
42
- self.table = DataTable(zebra_stripes=True, id=self.id)
46
+ self.table = DataTable(zebra_stripes=True)
43
47
  yield self.table
44
48
 
45
49
  def on_key(self, event):
@@ -86,27 +90,26 @@ class TableScreen(ModalScreen):
86
90
  expr = pl.col(col_name) == col_value
87
91
  value_display = f"[on $primary]{col_value}[/]"
88
92
 
89
- app = self.app
90
93
  matched_indices = set(
91
- app.df.with_row_index("__rid__").filter(expr)["__rid__"].to_list()
94
+ self.dftable.df.with_row_index("__rid__").filter(expr)["__rid__"].to_list()
92
95
  )
93
96
 
94
97
  # Apply the action
95
98
  if action == "filter":
96
99
  # Update visible_rows to reflect the filter
97
- for i in range(len(app.visible_rows)):
98
- app.visible_rows[i] = i in matched_indices
100
+ for i in range(len(self.dftable.visible_rows)):
101
+ self.dftable.visible_rows[i] = i in matched_indices
99
102
  title = "Filter"
100
103
  message = f"Filtered by [on $primary]{col_name}[/] = {value_display}"
101
104
  else: # action == "highlight"
102
105
  # Update selected_rows to reflect the highlights
103
- for i in range(len(app.selected_rows)):
104
- app.selected_rows[i] = i in matched_indices
106
+ for i in range(len(self.dftable.selected_rows)):
107
+ self.dftable.selected_rows[i] = i in matched_indices
105
108
  title = "Highlight"
106
109
  message = f"Highlighted [on $primary]{col_name}[/] = {value_display}"
107
110
 
108
111
  # Recreate the table display with updated data in the main app
109
- app._setup_table()
112
+ self.dftable._setup_table()
110
113
 
111
114
  # Dismiss the frequency screen
112
115
  self.app.pop_screen()
@@ -119,8 +122,8 @@ class RowDetailScreen(TableScreen):
119
122
 
120
123
  CSS = TableScreen.DEFAULT_CSS.replace("TableScreen", "RowDetailScreen")
121
124
 
122
- def __init__(self, row_idx: int, df: pl.DataFrame):
123
- super().__init__(df, id="row-detail-table")
125
+ def __init__(self, row_idx: int, dftable):
126
+ super().__init__(dftable)
124
127
  self.row_idx = row_idx
125
128
 
126
129
  def on_mount(self) -> None:
@@ -136,6 +139,8 @@ class RowDetailScreen(TableScreen):
136
139
  *_format_row([col, val], [None, dtype], apply_justify=False)
137
140
  )
138
141
 
142
+ self.table.cursor_type = "row"
143
+
139
144
  def on_key(self, event):
140
145
  if event.key == "v":
141
146
  # Filter the main table by the selected value
@@ -166,8 +171,8 @@ class FrequencyScreen(TableScreen):
166
171
 
167
172
  CSS = TableScreen.DEFAULT_CSS.replace("TableScreen", "FrequencyScreen")
168
173
 
169
- def __init__(self, col_idx: int, df: pl.DataFrame):
170
- super().__init__(df, id="frequency-table")
174
+ def __init__(self, col_idx: int, dftable):
175
+ super().__init__(dftable)
171
176
  self.col_idx = col_idx
172
177
  self.sorted_columns = {
173
178
  1: True, # Count
@@ -188,6 +193,7 @@ class FrequencyScreen(TableScreen):
188
193
  self.table.add_column(Text(column, justify=dc.justify), key=column)
189
194
  self.table.add_column(Text("Count", justify="right"), key="Count")
190
195
  self.table.add_column(Text("%", justify="right"), key="%")
196
+ self.table.add_column(Text("Histogram", justify="left"), key="Histogram")
191
197
 
192
198
  # Get style config for Int64 and Float64
193
199
  ds_int = DtypeConfig("Int64")
@@ -204,16 +210,16 @@ class FrequencyScreen(TableScreen):
204
210
  style=dc.style,
205
211
  justify=dc.justify,
206
212
  ),
207
- Text(
208
- str(count),
209
- style=ds_int.style,
210
- justify=ds_int.justify,
211
- ),
213
+ Text(str(count), style=ds_int.style, justify=ds_int.justify),
212
214
  Text(
213
215
  f"{percentage:.2f}",
214
216
  style=ds_float.style,
215
217
  justify=ds_float.justify,
216
218
  ),
219
+ Bar(
220
+ highlight_range=(0.0, percentage / 100 * 10),
221
+ width=10,
222
+ ),
217
223
  key=str(row_idx + 1),
218
224
  )
219
225
 
@@ -279,14 +285,17 @@ class FrequencyScreen(TableScreen):
279
285
  def key_fun(freq_col):
280
286
  col_value = freq_col.plain
281
287
 
282
- if col_dtype == "Int64":
283
- return int(col_value)
284
- elif col_dtype == "Float64":
285
- return float(col_value)
286
- elif col_dtype == "Boolean":
287
- return BOOLS[col_value]
288
- else:
289
- return col_value
288
+ try:
289
+ if col_dtype == "Int64":
290
+ return int(col_value)
291
+ elif col_dtype == "Float64":
292
+ return float(col_value)
293
+ elif col_dtype == "Boolean":
294
+ return BOOLS[col_value]
295
+ else:
296
+ return col_value
297
+ except ValueError:
298
+ return 0
290
299
 
291
300
  # Sort the table
292
301
  freq_table.sort(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframe-textual
3
- Version: 0.2.1
3
+ Version: 0.3.1
4
4
  Summary: Interactive CSV/Excel viewer for the terminal (Textual TUI)
5
5
  Project-URL: Homepage, https://github.com/need47/dataframe-textual
6
6
  Project-URL: Repository, https://github.com/need47/dataframe-textual.git
@@ -9,7 +9,7 @@ Project-URL: Bug Tracker, https://github.com/need47/dataframe-textual/issues
9
9
  Author-email: Tiejun Cheng <need47@gmail.com>
10
10
  License: MIT
11
11
  License-File: LICENSE
12
- Keywords: csv,data-analysis,interactive,polars,terminal,textual,tui,viewer
12
+ Keywords: csv,data-analysis,excel,interactive,polars,terminal,textual,tui,viewer
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Intended Audience :: Developers
@@ -29,7 +29,6 @@ Classifier: Topic :: Utilities
29
29
  Classifier: Typing :: Typed
30
30
  Requires-Python: >=3.11
31
31
  Requires-Dist: polars>=1.34.0
32
- Requires-Dist: rich>=14.2.0
33
32
  Requires-Dist: textual>=1.0.0
34
33
  Provides-Extra: dev
35
34
  Requires-Dist: textual-dev>=1.8.0; extra == 'dev'
@@ -2,12 +2,12 @@ dataframe_textual/__init__.py,sha256=uzB3bjlbm8JbsjxEgwqvPcYERktm3F9d9Op_6cWJ1sk
2
2
  dataframe_textual/__main__.py,sha256=LPLyZcv4hAFfF3hUt1S2dtZOqaAZnlguicgwiDrtXgk,1349
3
3
  dataframe_textual/common.py,sha256=3zzhI__F_hoOFDRe-wt-oTfMDFik1ohraIa6rXcVit8,6357
4
4
  dataframe_textual/data_frame_help_panel.py,sha256=SQ2lulb1SPxItR9tMvIgOzzeCcW9SB1rRojAcwZ7Vis,2730
5
- dataframe_textual/data_frame_table.py,sha256=u_gMc-U57efgIRoCfOR65clieK7M7FUg45jjUb06V_w,50079
5
+ dataframe_textual/data_frame_table.py,sha256=8CCTwhi0cYPr9yEuMlv76rzPT48PzxyjsCIP2P3g3_c,50025
6
6
  dataframe_textual/data_frame_viewer.py,sha256=BP-pCYIG5bEDFkUmUyA3sxWc3z1zI_viClDZlT-s_uE,11715
7
- dataframe_textual/table_screen.py,sha256=Pq6aM-vntDxLj1r5Uzke6_pzbuhO-VrX_V12ezwm3uM,10588
7
+ dataframe_textual/table_screen.py,sha256=elY0gBdc0PQlh82N7lu3FHeVoFprqd-8iUBVvfSdah0,11015
8
8
  dataframe_textual/yes_no_screen.py,sha256=z7MEVTMepFuGWFIthhQlAT3m69D6lgIl4tb2_oJAWWQ,13207
9
- dataframe_textual-0.2.1.dist-info/METADATA,sha256=jACe80kpq-4nzJZ0Ra5MwSnezCOMbJb4hTe9obRUWaI,17131
10
- dataframe_textual-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- dataframe_textual-0.2.1.dist-info/entry_points.txt,sha256=FkXDHVYYtGud6F2Jm2X9OMFAuFrSflNfgcNP5c2469M,70
12
- dataframe_textual-0.2.1.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
13
- dataframe_textual-0.2.1.dist-info/RECORD,,
9
+ dataframe_textual-0.3.1.dist-info/METADATA,sha256=nmpsgfqdd5E9RAe3rMwI_qkX3V_n_k_bVFP-DX9bAzY,17109
10
+ dataframe_textual-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ dataframe_textual-0.3.1.dist-info/entry_points.txt,sha256=FkXDHVYYtGud6F2Jm2X9OMFAuFrSflNfgcNP5c2469M,70
12
+ dataframe_textual-0.3.1.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
13
+ dataframe_textual-0.3.1.dist-info/RECORD,,