dataframe-textual 2.10.1__py3-none-any.whl → 2.12.0__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.
@@ -4,6 +4,8 @@ import argparse
4
4
  import sys
5
5
  from pathlib import Path
6
6
 
7
+ from textual.theme import BUILTIN_THEMES
8
+
7
9
  from . import __version__
8
10
  from .common import SUPPORTED_FORMATS, load_dataframe
9
11
  from .data_frame_viewer import DataFrameViewer
@@ -81,7 +83,23 @@ def cli() -> argparse.Namespace:
81
83
  parser.add_argument("-N", "--n-rows", metavar="N", type=int, help="Stop after reading N rows from CSV/TSV")
82
84
  parser.add_argument("-n", "--null", nargs="+", help="Values to interpret as null values when reading CSV/TSV")
83
85
 
86
+ parser.add_argument(
87
+ "--theme",
88
+ nargs="?",
89
+ const="list",
90
+ help="Set the theme for the application (use 'list' to see available themes)",
91
+ )
92
+
84
93
  args = parser.parse_args()
94
+
95
+ # List available themes and exit
96
+ if args.theme == "list":
97
+ print("Available themes:")
98
+ for theme in BUILTIN_THEMES:
99
+ print(f" - {theme}")
100
+ sys.exit(0)
101
+
102
+ # Handle files
85
103
  if args.files is None:
86
104
  args.files = []
87
105
 
@@ -119,7 +137,7 @@ def main() -> None:
119
137
  truncate_ragged_lines=args.truncate_ragged_lines,
120
138
  n_rows=args.n_rows,
121
139
  )
122
- app = DataFrameViewer(*sources)
140
+ app = DataFrameViewer(*sources, theme=args.theme)
123
141
  app.run()
124
142
 
125
143
 
@@ -242,7 +242,7 @@ class DataFrameTable(DataTable):
242
242
  ("ctrl+u", "reset", "Reset to initial state"),
243
243
  # Display
244
244
  ("h", "hide_column", "Hide column"),
245
- ("H", "show_hidden_rows_columns", "Show hidden rows/columns"),
245
+ ("H", "show_hidden_columns", "Show hidden column(s)"),
246
246
  ("tilde", "toggle_row_labels", "Toggle row labels"), # `~`
247
247
  ("K", "cycle_cursor_type", "Cycle cursor mode"), # `K`
248
248
  ("z", "freeze_row_column", "Freeze rows/columns"),
@@ -731,9 +731,9 @@ class DataFrameTable(DataTable):
731
731
  """Set cursor row as the new header row."""
732
732
  self.do_set_cursor_row_as_header()
733
733
 
734
- def action_show_hidden_rows_columns(self) -> None:
735
- """Show all hidden rows/columns."""
736
- self.do_show_hidden_rows_columns()
734
+ def action_show_hidden_columns(self) -> None:
735
+ """Show all hidden columns."""
736
+ self.do_show_hidden_columns()
737
737
 
738
738
  def action_sort_ascending(self) -> None:
739
739
  """Sort by current column in ascending order."""
@@ -1847,27 +1847,22 @@ class DataFrameTable(DataTable):
1847
1847
 
1848
1848
  # self.notify(f"Set row [$success]{ridx + 1}[/] as header", title="Set Row as Header")
1849
1849
 
1850
- def do_show_hidden_rows_columns(self) -> None:
1851
- """Show all hidden rows/columns by recreating the table."""
1852
- if not self.hidden_columns and self.df_view is None:
1853
- # self.notify("No hidden rows or columns to show", title="Show Hidden Rows/Columns", severity="warning")
1850
+ def do_show_hidden_columns(self) -> None:
1851
+ """Show all hidden columns by recreating the table."""
1852
+ if not self.hidden_columns:
1853
+ # self.notify("No hidden columns to show", title="Show Hidden Column(s)", severity="warning")
1854
1854
  return
1855
1855
 
1856
1856
  # Add to history
1857
- self.add_history("Showed hidden rows/columns")
1857
+ self.add_history("Showed hidden column(s)")
1858
1858
 
1859
- # If in a filtered view, restore the full dataframe
1860
- if self.df_view is not None:
1861
- self.df = self.df_view
1862
- self.df_view = None
1863
-
1864
- # Clear hidden rows/columns tracking
1859
+ # Clear hidden columns tracking
1865
1860
  self.hidden_columns.clear()
1866
1861
 
1867
1862
  # Recreate table for display
1868
1863
  self.setup_table()
1869
1864
 
1870
- # self.notify("Showed hidden row(s) and/or hidden column(s)", title="Show Hidden Rows/Columns")
1865
+ # self.notify("Showed hidden column(s)", title="Show Hidden Column(s)")
1871
1866
 
1872
1867
  # Sort
1873
1868
  def do_sort_by_column(self, descending: bool = False) -> None:
@@ -58,8 +58,8 @@ class DataFrameViewer(App):
58
58
  """).strip()
59
59
 
60
60
  BINDINGS = [
61
- ("q", "close_tab", "Close current tab"),
62
- ("Q", "close_all_tabs", "Close all tabs and quit app"),
61
+ ("q", "quit", "Quit from view or close current tab"),
62
+ ("Q", "quit_all", "Quit app after closing all tabs"),
63
63
  ("B", "toggle_tab_bar", "Toggle Tab Bar"),
64
64
  ("f1", "toggle_help_panel", "Help"),
65
65
  ("ctrl+o", "open_file", "Open File"),
@@ -88,7 +88,7 @@ class DataFrameViewer(App):
88
88
  }
89
89
  """
90
90
 
91
- def __init__(self, *sources: Source) -> None:
91
+ def __init__(self, *sources: Source, theme: str | None = None) -> None:
92
92
  """Initialize the DataFrame Viewer application.
93
93
 
94
94
  Loads data from provided sources and prepares the tabbed interface.
@@ -96,9 +96,11 @@ class DataFrameViewer(App):
96
96
  Args:
97
97
  sources: sources to load dataframes from, each as a tuple of
98
98
  (DataFrame, filename, tabname).
99
+ theme: Optional; The theme to use for the application.
99
100
  """
100
101
  super().__init__()
101
102
  self.sources = sources
103
+ self.theme = theme
102
104
  self.tabs: dict[TabPane, DataFrameTable] = {}
103
105
  self.help_panel = None
104
106
 
@@ -242,21 +244,21 @@ class DataFrameViewer(App):
242
244
  """
243
245
  self.push_screen(OpenFileScreen(), self.do_open_file)
244
246
 
245
- def action_close_tab(self) -> None:
246
- """Close the current tab.
247
+ def action_quit(self) -> None:
248
+ """Quit from the view or the current tab.
247
249
 
248
250
  Checks for unsaved changes and prompts the user to save if needed.
249
251
  If this is the last tab, exits the app.
250
252
  """
251
- self.do_close_tab()
253
+ self.do_quit()
252
254
 
253
- def action_close_all_tabs(self) -> None:
254
- """Close all tabs and exit the app.
255
+ def action_quit_all(self) -> None:
256
+ """Quit app after closing all tabs.
255
257
 
256
258
  Checks if any tabs have unsaved changes. If yes, opens a confirmation dialog.
257
259
  Otherwise, quits immediately.
258
260
  """
259
- self.do_close_all_tabs()
261
+ self.do_quit_all()
260
262
 
261
263
  def action_save_current_tab(self) -> None:
262
264
  """Open a save dialog to save current tab to file."""
@@ -486,16 +488,33 @@ class DataFrameViewer(App):
486
488
  self.tabbed.active = tab.id
487
489
  table.focus()
488
490
 
489
- def do_close_tab(self) -> None:
490
- """Close the currently active tab.
491
+ def do_quit(self) -> None:
492
+ """Quit from the view or the current tab.
491
493
 
492
- Removes the active tab from the interface. If only one tab remains and no more
493
- can be closed, the application exits instead.
494
+ When in a view, return to main table. Otherwise, close the active tab.
495
+ If only one tab remains and no more tabs can be closed, exits the application.
494
496
  """
495
497
  try:
496
498
  if not (table := self.active_table):
497
499
  return
498
500
 
501
+ # In a view - return to main table
502
+ if table.df_view is not None:
503
+ # Remove from history
504
+ while table.histories_undo:
505
+ h = table.histories_undo[-1]
506
+ if h.description.startswith("Viewed rows by expression"):
507
+ table.histories_undo.pop()
508
+ else:
509
+ break
510
+
511
+ table.add_history("Return to main table")
512
+ table.df = table.df_view
513
+ table.df_view = None
514
+ table.setup_table()
515
+
516
+ return
517
+
499
518
  def _on_save_confirm(result: bool) -> None:
500
519
  """Handle the "save before closing?" confirmation."""
501
520
  if result:
@@ -540,7 +559,7 @@ class DataFrameViewer(App):
540
559
  except Exception:
541
560
  pass
542
561
 
543
- def do_close_all_tabs(self) -> None:
562
+ def do_quit_all(self) -> None:
544
563
  """Close all tabs and quit the app.
545
564
 
546
565
  Checks if any tabs have unsaved changes. If yes, opens a confirmation dialog.
@@ -134,8 +134,10 @@ class TableScreen(ModalScreen):
134
134
  else:
135
135
  self.dftable.view_rows((expr, cidx, False, True))
136
136
 
137
- # Dismiss the current modal screen
138
- self.app.pop_screen()
137
+ # Dismiss modal screen(s) to return to main table
138
+ while len(self.app._screen_stack) > 1:
139
+ self.app.pop_screen()
140
+ break
139
141
 
140
142
  def show_frequency(self, cidx_name_value: tuple[int, str, Any] | None) -> None:
141
143
  """Show frequency by the selected value.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframe-textual
3
- Version: 2.10.1
3
+ Version: 2.12.0
4
4
  Summary: Interactive terminal viewer/editor for tabular data
5
5
  Project-URL: Homepage, https://github.com/need47/dataframe-textual
6
6
  Project-URL: Repository, https://github.com/need47/dataframe-textual.git
@@ -195,6 +195,7 @@ options:
195
195
  -N, --n-rows N Stop after reading N rows from CSV/TSV
196
196
  -n, --null NULL [NULL ...]
197
197
  Values to interpret as null values when reading CSV/TSV
198
+ --theme [THEME] Set the theme for the application (use 'list' to see available themes)
198
199
  ```
199
200
 
200
201
  ### CLI Examples
@@ -233,6 +234,9 @@ dv -l 3 -a 1 -I messy_scientific_data.csv
233
234
  # Process compressed data
234
235
  dv data.csv.gz
235
236
  zcat compressed_data.csv.gz | dv -f csv
237
+
238
+ # Choose the `monokai` theme
239
+ dv --theme monokai data.csv
236
240
  ```
237
241
 
238
242
  ## Keyboard Shortcuts
@@ -247,8 +251,8 @@ zcat compressed_data.csv.gz | dv -f csv
247
251
  | `<` | Move to previous tab |
248
252
  | `b` | Cycle through tabs |
249
253
  | `B` | Toggle tab bar visibility |
250
- | `q` | Close current tab (prompts to save unsaved changes) |
251
- | `Q` | Close all tabs and app (prompts to save unsaved changes) |
254
+ | `q` | Quit current tab (prompts to save unsaved changes) |
255
+ | `Q` | Quit all tabs and app (prompts to save unsaved changes) |
252
256
  | `Ctrl+Q` | Force to quit app (regardless of unsaved changes) |
253
257
  | `Ctrl+T` | Save current tab to file |
254
258
  | `Ctrl+S` | Save all tabs to file |
@@ -306,9 +310,9 @@ zcat compressed_data.csv.gz | dv -f csv
306
310
  | `_` (underscore) | Toggle column full width |
307
311
  | `z` | Freeze rows and columns |
308
312
  | `,` | Toggle thousand separator for numeric display |
309
- | `&` | Set current row as the new header row |
313
+ | `&` | Set current row as the new header row |
310
314
  | `h` | Hide current column |
311
- | `H` | Show all hidden rows/columns |
315
+ | `H` | Show all hidden columns |
312
316
 
313
317
  #### Editing
314
318
 
@@ -499,7 +503,7 @@ Both operations show selected rows but with fundamentally different effects:
499
503
  **When to use View** (`v` or `V`):
500
504
  - Exploring or analyzing data safely
501
505
  - Switching between different perspectives
502
- - Press `H` to restore hidden rows (and hidden columns)
506
+ - Press `q` to return to main table
503
507
 
504
508
  **When to use Filter** (`"`):
505
509
  - Cleaning data (removing unwanted rows)
@@ -678,7 +682,7 @@ This is useful for:
678
682
 
679
683
  **Hide/Show Columns** (`h` / `H`):
680
684
  - `h` - Temporarily hide current column (data preserved)
681
- - `H` - Restore all hidden columns and rows
685
+ - `H` - Restore all hidden columns
682
686
 
683
687
  ### 12. Column & Row Reordering
684
688
 
@@ -0,0 +1,14 @@
1
+ dataframe_textual/__init__.py,sha256=E53fW1spQRA4jW9grxSqPEmoe9zofzr6twdveMbt_W8,1310
2
+ dataframe_textual/__main__.py,sha256=OXnvZkLEK5SPMJWtchQRyXwHFofgvdvZp7M9XjzN3AQ,4451
3
+ dataframe_textual/common.py,sha256=CNRdHP3N1li2dy9OsTiW-zfpzf8zcrt2fW8mmYY-YVA,29073
4
+ dataframe_textual/data_frame_help_panel.py,sha256=UEtj64XsVRdtLzuwOaITfoEQUkAfwFuvpr5Npip5WHs,3381
5
+ dataframe_textual/data_frame_table.py,sha256=Vi02ombWWUV8lehj6vTNWakXpkRWWPJU4eUl6aCiG2o,148263
6
+ dataframe_textual/data_frame_viewer.py,sha256=1NvP6Jf5Pcw-yT7jUzOMJzQlmEiufQ0LZQwwcoqBC3Q,29472
7
+ dataframe_textual/sql_screen.py,sha256=P3j1Fv45NIKEYo9adb7NPod54FaU-djFIvCUMMHbvjY,7534
8
+ dataframe_textual/table_screen.py,sha256=kk5dpVu-ExkMh7BunQdCNvHC1RmLl6nHLHHulWucloY,22046
9
+ dataframe_textual/yes_no_screen.py,sha256=LC42DeJRIWb-PdpR3FDNvwxhnfZ6OXfU9Kxiu340BNE,26132
10
+ dataframe_textual-2.12.0.dist-info/METADATA,sha256=96eysYGwTTebGPrzIiQ9Fijyb9FkeSq8QfFAKUQ8XG0,29941
11
+ dataframe_textual-2.12.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
+ dataframe_textual-2.12.0.dist-info/entry_points.txt,sha256=R_GoooOxcq6ab4RaHiVoZ4zrZJ-phMcGmlL2rwqncW8,107
13
+ dataframe_textual-2.12.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
14
+ dataframe_textual-2.12.0.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- dataframe_textual/__init__.py,sha256=E53fW1spQRA4jW9grxSqPEmoe9zofzr6twdveMbt_W8,1310
2
- dataframe_textual/__main__.py,sha256=tJ6FjjV25ZQzaMdqD5XcDVRZfj8l6kgGvXyrn975rjo,3999
3
- dataframe_textual/common.py,sha256=CNRdHP3N1li2dy9OsTiW-zfpzf8zcrt2fW8mmYY-YVA,29073
4
- dataframe_textual/data_frame_help_panel.py,sha256=UEtj64XsVRdtLzuwOaITfoEQUkAfwFuvpr5Npip5WHs,3381
5
- dataframe_textual/data_frame_table.py,sha256=EAtUrzygpppdgEOk1FhSZWwDAwdKLpF-1s2iAE6EI3E,148529
6
- dataframe_textual/data_frame_viewer.py,sha256=_VwbCcRBgdTcrZmgS2mRwIJ-cFxOeJ55twDFvQUHMfk,28723
7
- dataframe_textual/sql_screen.py,sha256=P3j1Fv45NIKEYo9adb7NPod54FaU-djFIvCUMMHbvjY,7534
8
- dataframe_textual/table_screen.py,sha256=aiMzI_kKiR2_3U1bqWHvaEkg4rqm62pXoMm_s1-19QU,21962
9
- dataframe_textual/yes_no_screen.py,sha256=LC42DeJRIWb-PdpR3FDNvwxhnfZ6OXfU9Kxiu340BNE,26132
10
- dataframe_textual-2.10.1.dist-info/METADATA,sha256=0-PLV-wlVMGd9a1rbM0SV5PCaHJ5L9D07wFpfNp4z6A,29825
11
- dataframe_textual-2.10.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
- dataframe_textual-2.10.1.dist-info/entry_points.txt,sha256=R_GoooOxcq6ab4RaHiVoZ4zrZJ-phMcGmlL2rwqncW8,107
13
- dataframe_textual-2.10.1.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
14
- dataframe_textual-2.10.1.dist-info/RECORD,,