dataframe-textual 2.2.2__tar.gz → 2.3.0__tar.gz
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.
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/PKG-INFO +3 -2
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/README.md +2 -1
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/pyproject.toml +1 -1
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/data_frame_table.py +116 -54
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/data_frame_viewer.py +13 -8
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/uv.lock +1 -1
- dataframe_textual-2.2.2/x +0 -40532
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/.gitignore +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/1811.csv.gz +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/LICENSE +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/large_malformed.tsv.gz +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/main.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/__init__.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/__main__.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/common.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/data_frame_help_panel.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/sql_screen.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/table_screen.py +0 -0
- {dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/yes_no_screen.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframe-textual
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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
|
|
@@ -302,6 +302,7 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
302
302
|
| `_` (underscore) | Toggle column full width |
|
|
303
303
|
| `z` | Freeze rows and columns |
|
|
304
304
|
| `,` | Toggle thousand separator for numeric display |
|
|
305
|
+
| `&` | Set current row as the new header row |
|
|
305
306
|
| `h` | Hide current column |
|
|
306
307
|
| `H` | Show all hidden rows/columns |
|
|
307
308
|
|
|
@@ -696,7 +697,7 @@ Press `,` to toggle thousand separator formatting for numeric data:
|
|
|
696
697
|
|
|
697
698
|
### 15. Save File
|
|
698
699
|
|
|
699
|
-
Press `Ctrl+S` to save filtered, edited, or sorted data back to file
|
|
700
|
+
Press `Ctrl+S` to save filtered, edited, or sorted data back to file. The output format is automatically determined by the file extension, making it easy to convert between different formats (e.g., CSV to TSV).
|
|
700
701
|
|
|
701
702
|
### 16. Undo/Redo/Reset
|
|
702
703
|
|
|
@@ -263,6 +263,7 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
263
263
|
| `_` (underscore) | Toggle column full width |
|
|
264
264
|
| `z` | Freeze rows and columns |
|
|
265
265
|
| `,` | Toggle thousand separator for numeric display |
|
|
266
|
+
| `&` | Set current row as the new header row |
|
|
266
267
|
| `h` | Hide current column |
|
|
267
268
|
| `H` | Show all hidden rows/columns |
|
|
268
269
|
|
|
@@ -657,7 +658,7 @@ Press `,` to toggle thousand separator formatting for numeric data:
|
|
|
657
658
|
|
|
658
659
|
### 15. Save File
|
|
659
660
|
|
|
660
|
-
Press `Ctrl+S` to save filtered, edited, or sorted data back to file
|
|
661
|
+
Press `Ctrl+S` to save filtered, edited, or sorted data back to file. The output format is automatically determined by the file extension, making it easy to convert between different formats (e.g., CSV to TSV).
|
|
661
662
|
|
|
662
663
|
### 16. Undo/Redo/Reset
|
|
663
664
|
|
{dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/data_frame_table.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""DataFrameTable widget for displaying and interacting with Polars DataFrames."""
|
|
2
2
|
|
|
3
|
+
import io
|
|
3
4
|
import sys
|
|
4
5
|
from collections import defaultdict, deque
|
|
5
6
|
from dataclasses import dataclass
|
|
@@ -140,6 +141,7 @@ class DataFrameTable(DataTable):
|
|
|
140
141
|
- **Ctrl+F** - 📜 Page down
|
|
141
142
|
- **Ctrl+B** - 📜 Page up
|
|
142
143
|
- **PgUp/PgDn** - 📜 Page up/down
|
|
144
|
+
- **&** - 📌 Mark current row as header
|
|
143
145
|
|
|
144
146
|
## ♻️ Undo/Redo/Reset
|
|
145
147
|
- **u** - ↩️ Undo last action
|
|
@@ -245,9 +247,10 @@ class DataFrameTable(DataTable):
|
|
|
245
247
|
("tilde", "toggle_row_labels", "Toggle row labels"), # `~`
|
|
246
248
|
("K", "cycle_cursor_type", "Cycle cursor mode"), # `K`
|
|
247
249
|
("z", "freeze_row_column", "Freeze rows/columns"),
|
|
248
|
-
("comma", "
|
|
250
|
+
("comma", "toggle_thousand_separator", "Toggle thousand separator"), # `,`
|
|
249
251
|
("underscore", "expand_column", "Expand column to full width"), # `_`
|
|
250
252
|
("circumflex_accent", "toggle_rid", "Toggle internal row index"), # `^`
|
|
253
|
+
("ampersand", "set_cursor_row_as_header", "Set cursor row as the new header row"), # `&`
|
|
251
254
|
# Copy
|
|
252
255
|
("c", "copy_cell", "Copy cell to clipboard"),
|
|
253
256
|
("ctrl+c", "copy_column", "Copy column to clipboard"),
|
|
@@ -725,6 +728,10 @@ class DataFrameTable(DataTable):
|
|
|
725
728
|
"""Toggle the internal row index column visibility."""
|
|
726
729
|
self.do_toggle_rid()
|
|
727
730
|
|
|
731
|
+
def action_set_cursor_row_as_header(self) -> None:
|
|
732
|
+
"""Set cursor row as the new header row."""
|
|
733
|
+
self.do_set_cursor_row_as_header()
|
|
734
|
+
|
|
728
735
|
def action_show_hidden_rows_columns(self) -> None:
|
|
729
736
|
"""Show all hidden rows/columns."""
|
|
730
737
|
self.do_show_hidden_rows_columns()
|
|
@@ -905,7 +912,7 @@ class DataFrameTable(DataTable):
|
|
|
905
912
|
"""Toggle row labels visibility."""
|
|
906
913
|
self.show_row_labels = not self.show_row_labels
|
|
907
914
|
# status = "shown" if self.show_row_labels else "hidden"
|
|
908
|
-
# self.notify(f"Row labels {status}", title="Labels")
|
|
915
|
+
# self.notify(f"Row labels {status}", title="Toggle Row Labels")
|
|
909
916
|
|
|
910
917
|
def action_cast_column_dtype(self, dtype: str | pl.DataType) -> None:
|
|
911
918
|
"""Cast the current column to a different data type."""
|
|
@@ -921,8 +928,8 @@ class DataFrameTable(DataTable):
|
|
|
921
928
|
self.do_copy_to_clipboard(cell_str, f"Copied: [$success]{cell_str[:50]}[/]")
|
|
922
929
|
except IndexError:
|
|
923
930
|
self.notify(
|
|
924
|
-
f"Error copying cell ([$error]{ridx}[/], [$accent]{cidx}[/])",
|
|
925
|
-
title="
|
|
931
|
+
f"Error copying cell ([$error]{ridx}[/], [$accent]{cidx}[/]) to clipboard",
|
|
932
|
+
title="Copy Cell",
|
|
926
933
|
severity="error",
|
|
927
934
|
timeout=10,
|
|
928
935
|
)
|
|
@@ -941,7 +948,12 @@ class DataFrameTable(DataTable):
|
|
|
941
948
|
f"Copied [$accent]{len(col_values)}[/] values from column [$success]{col_name}[/]",
|
|
942
949
|
)
|
|
943
950
|
except (FileNotFoundError, IndexError):
|
|
944
|
-
self.notify(
|
|
951
|
+
self.notify(
|
|
952
|
+
f"Error copying column [$error]{col_name}[/] to clipboard",
|
|
953
|
+
title="Copy Column",
|
|
954
|
+
severity="error",
|
|
955
|
+
timeout=10,
|
|
956
|
+
)
|
|
945
957
|
|
|
946
958
|
def action_copy_row(self) -> None:
|
|
947
959
|
"""Copy the current row to clipboard (values separated by tabs)."""
|
|
@@ -957,14 +969,16 @@ class DataFrameTable(DataTable):
|
|
|
957
969
|
f"Copied row [$accent]{ridx + 1}[/] with [$success]{len(row_values)}[/] values",
|
|
958
970
|
)
|
|
959
971
|
except (FileNotFoundError, IndexError):
|
|
960
|
-
self.notify(
|
|
972
|
+
self.notify(
|
|
973
|
+
f"Error copying row [$error]{ridx}[/] to clipboard", title="Copy Row", severity="error", timeout=10
|
|
974
|
+
)
|
|
961
975
|
|
|
962
|
-
def
|
|
976
|
+
def action_toggle_thousand_separator(self) -> None:
|
|
963
977
|
"""Toggle thousand separator for numeric display."""
|
|
964
978
|
self.thousand_separator = not self.thousand_separator
|
|
965
979
|
self.setup_table()
|
|
966
980
|
# status = "enabled" if self.thousand_separator else "disabled"
|
|
967
|
-
# self.notify(f"Thousand separator {status}", title="
|
|
981
|
+
# self.notify(f"Thousand separator {status}", title="Toggle Thousand Separator")
|
|
968
982
|
|
|
969
983
|
def action_next_match(self) -> None:
|
|
970
984
|
"""Go to the next matched cell."""
|
|
@@ -1344,7 +1358,7 @@ class DataFrameTable(DataTable):
|
|
|
1344
1358
|
return range_count
|
|
1345
1359
|
|
|
1346
1360
|
except Exception as e:
|
|
1347
|
-
self.notify("Error loading rows", title="Load", severity="error", timeout=10)
|
|
1361
|
+
self.notify("Error loading rows", title="Load Rows", severity="error", timeout=10)
|
|
1348
1362
|
self.log(f"Error loading rows: {str(e)}")
|
|
1349
1363
|
return 0
|
|
1350
1364
|
|
|
@@ -1593,7 +1607,7 @@ class DataFrameTable(DataTable):
|
|
|
1593
1607
|
def do_undo(self) -> None:
|
|
1594
1608
|
"""Undo the last action."""
|
|
1595
1609
|
if not self.histories_undo:
|
|
1596
|
-
self.notify("No actions to undo", title="Undo", severity="warning")
|
|
1610
|
+
# self.notify("No actions to undo", title="Undo", severity="warning")
|
|
1597
1611
|
return
|
|
1598
1612
|
|
|
1599
1613
|
# Pop the last history state for undo and save to redo stack
|
|
@@ -1608,7 +1622,7 @@ class DataFrameTable(DataTable):
|
|
|
1608
1622
|
def do_redo(self) -> None:
|
|
1609
1623
|
"""Redo the last undone action."""
|
|
1610
1624
|
if not self.histories_redo:
|
|
1611
|
-
self.notify("No actions to redo", title="Redo", severity="warning")
|
|
1625
|
+
# self.notify("No actions to redo", title="Redo", severity="warning")
|
|
1612
1626
|
return
|
|
1613
1627
|
|
|
1614
1628
|
# Pop the last undone state from redo stack
|
|
@@ -1635,7 +1649,7 @@ class DataFrameTable(DataTable):
|
|
|
1635
1649
|
next_type = get_next_item(CURSOR_TYPES, self.cursor_type)
|
|
1636
1650
|
self.cursor_type = next_type
|
|
1637
1651
|
|
|
1638
|
-
# self.notify(f"Changed cursor type to [$success]{next_type}[/]", title="Cursor")
|
|
1652
|
+
# self.notify(f"Changed cursor type to [$success]{next_type}[/]", title="Cycle Cursor Type")
|
|
1639
1653
|
|
|
1640
1654
|
def do_view_row_detail(self) -> None:
|
|
1641
1655
|
"""Open a modal screen to view the selected row's details."""
|
|
@@ -1697,7 +1711,7 @@ class DataFrameTable(DataTable):
|
|
|
1697
1711
|
if fixed_columns >= 0:
|
|
1698
1712
|
self.fixed_columns = fixed_columns
|
|
1699
1713
|
|
|
1700
|
-
# self.notify(f"Pinned [$success]{fixed_rows}[/] rows and [$accent]{fixed_columns}[/] columns", title="Pin")
|
|
1714
|
+
# self.notify(f"Pinned [$success]{fixed_rows}[/] rows and [$accent]{fixed_columns}[/] columns", title="Pin Row/Column")
|
|
1701
1715
|
|
|
1702
1716
|
def do_hide_column(self) -> None:
|
|
1703
1717
|
"""Hide the currently selected column from the table display."""
|
|
@@ -1718,7 +1732,9 @@ class DataFrameTable(DataTable):
|
|
|
1718
1732
|
if col_idx >= len(self.columns):
|
|
1719
1733
|
self.move_cursor(column=len(self.columns) - 1)
|
|
1720
1734
|
|
|
1721
|
-
# self.notify(
|
|
1735
|
+
# self.notify(
|
|
1736
|
+
# f"Hid column [$success]{col_name}[/]. Press [$accent]H[/] to show hidden columns", title="Hide Column"
|
|
1737
|
+
# )
|
|
1722
1738
|
|
|
1723
1739
|
def do_expand_column(self) -> None:
|
|
1724
1740
|
"""Expand the current column to show the widest cell in the loaded data."""
|
|
@@ -1773,7 +1789,7 @@ class DataFrameTable(DataTable):
|
|
|
1773
1789
|
self._require_update_dimensions = True
|
|
1774
1790
|
self.refresh(layout=True)
|
|
1775
1791
|
|
|
1776
|
-
# self.notify(f"Expanded column [$success]{col_name}[/] to width [$accent]{max_width}[/]", title="Expand")
|
|
1792
|
+
# self.notify(f"Expanded column [$success]{col_name}[/] to width [$accent]{max_width}[/]", title="Expand Column")
|
|
1777
1793
|
|
|
1778
1794
|
def do_toggle_rid(self) -> None:
|
|
1779
1795
|
"""Toggle display of the internal RID column."""
|
|
@@ -1782,10 +1798,52 @@ class DataFrameTable(DataTable):
|
|
|
1782
1798
|
# Recreate table for display
|
|
1783
1799
|
self.setup_table()
|
|
1784
1800
|
|
|
1801
|
+
def do_set_cursor_row_as_header(self) -> None:
|
|
1802
|
+
"""Set cursor row as the new header row."""
|
|
1803
|
+
ridx = self.cursor_row_idx
|
|
1804
|
+
|
|
1805
|
+
# Get the new header values
|
|
1806
|
+
new_header = list(self.df.row(ridx))
|
|
1807
|
+
new_header[-1] = RID # Ensure last column remains RID
|
|
1808
|
+
|
|
1809
|
+
# Handle duplicate column names by appending suffixes
|
|
1810
|
+
seen = {}
|
|
1811
|
+
for i, col in enumerate(new_header):
|
|
1812
|
+
if col in seen:
|
|
1813
|
+
seen[col] += 1
|
|
1814
|
+
new_header[i] = f"{col}_{seen[col]}"
|
|
1815
|
+
else:
|
|
1816
|
+
seen[col] = 0
|
|
1817
|
+
|
|
1818
|
+
# Create a mapping of old column names to new column names
|
|
1819
|
+
col_rename_map = {old_col: str(new_col) for old_col, new_col in zip(self.df.columns, new_header)}
|
|
1820
|
+
|
|
1821
|
+
# Add to history
|
|
1822
|
+
self.add_history(f"Set row [$success]{ridx + 1}[/] as header", dirty=False)
|
|
1823
|
+
|
|
1824
|
+
# Rename columns in the dataframe
|
|
1825
|
+
self.df = self.df.slice(ridx + 1).rename(col_rename_map)
|
|
1826
|
+
|
|
1827
|
+
# Write to string buffer
|
|
1828
|
+
buffer = io.StringIO()
|
|
1829
|
+
self.df.write_csv(buffer)
|
|
1830
|
+
|
|
1831
|
+
# Re-read with inferred schema to reset dtypes
|
|
1832
|
+
buffer.seek(0)
|
|
1833
|
+
self.df = pl.read_csv(buffer)
|
|
1834
|
+
|
|
1835
|
+
# Recreate table for display
|
|
1836
|
+
self.setup_table()
|
|
1837
|
+
|
|
1838
|
+
# Move cursor to first column
|
|
1839
|
+
self.move_cursor(row=ridx, column=0)
|
|
1840
|
+
|
|
1841
|
+
# self.notify(f"Set row [$success]{ridx + 1}[/] as header", title="Set Row as Header")
|
|
1842
|
+
|
|
1785
1843
|
def do_show_hidden_rows_columns(self) -> None:
|
|
1786
1844
|
"""Show all hidden rows/columns by recreating the table."""
|
|
1787
1845
|
if not self.hidden_columns and self.df_view is None:
|
|
1788
|
-
self.notify("No hidden rows or columns to show", title="Show", severity="warning")
|
|
1846
|
+
# self.notify("No hidden rows or columns to show", title="Show Hidden Rows/Columns", severity="warning")
|
|
1789
1847
|
return
|
|
1790
1848
|
|
|
1791
1849
|
# Add to history
|
|
@@ -1802,7 +1860,7 @@ class DataFrameTable(DataTable):
|
|
|
1802
1860
|
# Recreate table for display
|
|
1803
1861
|
self.setup_table()
|
|
1804
1862
|
|
|
1805
|
-
self.notify("Showed hidden row(s) and/or hidden column(s)", title="Show")
|
|
1863
|
+
# self.notify("Showed hidden row(s) and/or hidden column(s)", title="Show Hidden Rows/Columns")
|
|
1806
1864
|
|
|
1807
1865
|
# Sort
|
|
1808
1866
|
def do_sort_by_column(self, descending: bool = False) -> None:
|
|
@@ -1979,7 +2037,7 @@ class DataFrameTable(DataTable):
|
|
|
1979
2037
|
except Exception:
|
|
1980
2038
|
self.notify(
|
|
1981
2039
|
f"Error converting [$error]{term}[/] to [$accent]{dtype}[/]. Cast to string.",
|
|
1982
|
-
title="Edit",
|
|
2040
|
+
title="Edit Column",
|
|
1983
2041
|
severity="error",
|
|
1984
2042
|
)
|
|
1985
2043
|
expr = pl.lit(str(term))
|
|
@@ -2084,7 +2142,7 @@ class DataFrameTable(DataTable):
|
|
|
2084
2142
|
# Move cursor to the renamed column
|
|
2085
2143
|
self.move_cursor(column=col_idx)
|
|
2086
2144
|
|
|
2087
|
-
# self.notify(f"Renamed column [$success]{col_name}[/] to [$success]{new_name}[/]", title="Column")
|
|
2145
|
+
# self.notify(f"Renamed column [$success]{col_name}[/] to [$success]{new_name}[/]", title="Rename Column")
|
|
2088
2146
|
|
|
2089
2147
|
def do_clear_cell(self) -> None:
|
|
2090
2148
|
"""Clear the current cell by setting its value to None."""
|
|
@@ -2296,7 +2354,7 @@ class DataFrameTable(DataTable):
|
|
|
2296
2354
|
# Move cursor to the new column
|
|
2297
2355
|
self.move_cursor(column=cidx + 1)
|
|
2298
2356
|
|
|
2299
|
-
self.notify(f"Added link column [$success]{new_col_name}[/]. Use Ctrl/Cmd click to open.", title="Add Link")
|
|
2357
|
+
# self.notify(f"Added link column [$success]{new_col_name}[/]. Use Ctrl/Cmd click to open.", title="Add Link")
|
|
2300
2358
|
|
|
2301
2359
|
except Exception as e:
|
|
2302
2360
|
self.notify(
|
|
@@ -2311,7 +2369,7 @@ class DataFrameTable(DataTable):
|
|
|
2311
2369
|
try:
|
|
2312
2370
|
col_name = self.cursor_col_name
|
|
2313
2371
|
except CellDoesNotExist:
|
|
2314
|
-
self.notify("No column to delete at the current cursor position", title="Delete Column", severity="warning")
|
|
2372
|
+
# self.notify("No column to delete at the current cursor position", title="Delete Column", severity="warning")
|
|
2315
2373
|
return
|
|
2316
2374
|
|
|
2317
2375
|
col_key = self.cursor_col_key
|
|
@@ -2378,7 +2436,7 @@ class DataFrameTable(DataTable):
|
|
|
2378
2436
|
if self.df_view is not None:
|
|
2379
2437
|
self.df_view = self.df_view.drop(col_names_to_remove)
|
|
2380
2438
|
|
|
2381
|
-
self.notify(message, title="Delete Column")
|
|
2439
|
+
# self.notify(message, title="Delete Column")
|
|
2382
2440
|
|
|
2383
2441
|
def do_duplicate_column(self) -> None:
|
|
2384
2442
|
"""Duplicate the currently selected column, inserting it right after the current column."""
|
|
@@ -2417,7 +2475,7 @@ class DataFrameTable(DataTable):
|
|
|
2417
2475
|
# Move cursor to the new duplicated column
|
|
2418
2476
|
self.move_cursor(column=col_idx + 1)
|
|
2419
2477
|
|
|
2420
|
-
# self.notify(f"Duplicated column [$success]{col_name}[/] as [$accent]{new_col_name}[/]", title="Duplicate")
|
|
2478
|
+
# self.notify(f"Duplicated column [$success]{col_name}[/] as [$accent]{new_col_name}[/]", title="Duplicate Column")
|
|
2421
2479
|
|
|
2422
2480
|
def do_delete_row(self, more: str = None) -> None:
|
|
2423
2481
|
"""Delete rows from the table and dataframe.
|
|
@@ -2459,7 +2517,7 @@ class DataFrameTable(DataTable):
|
|
|
2459
2517
|
try:
|
|
2460
2518
|
df_filtered = self.df.lazy().filter(~pl.col(RID).is_in(rids_to_delete)).collect()
|
|
2461
2519
|
except Exception as e:
|
|
2462
|
-
self.notify(f"Error deleting row(s): {e}", title="Delete", severity="error", timeout=10)
|
|
2520
|
+
self.notify(f"Error deleting row(s): {e}", title="Delete Row(s)", severity="error", timeout=10)
|
|
2463
2521
|
self.histories_undo.pop() # Remove last history entry
|
|
2464
2522
|
return
|
|
2465
2523
|
|
|
@@ -2486,7 +2544,7 @@ class DataFrameTable(DataTable):
|
|
|
2486
2544
|
|
|
2487
2545
|
deleted_count = old_count - len(self.df)
|
|
2488
2546
|
if deleted_count > 0:
|
|
2489
|
-
self.notify(f"Deleted [$success]{deleted_count}[/] row(s)", title="Delete")
|
|
2547
|
+
self.notify(f"Deleted [$success]{deleted_count}[/] row(s)", title="Delete Row(s)")
|
|
2490
2548
|
|
|
2491
2549
|
def do_duplicate_row(self) -> None:
|
|
2492
2550
|
"""Duplicate the currently selected row, inserting it right after the current row."""
|
|
@@ -2521,7 +2579,7 @@ class DataFrameTable(DataTable):
|
|
|
2521
2579
|
# Move cursor to the new duplicated row
|
|
2522
2580
|
self.move_cursor(row=ridx + 1)
|
|
2523
2581
|
|
|
2524
|
-
# self.notify(f"Duplicated row [$success]{ridx + 1}[/]", title="Row")
|
|
2582
|
+
# self.notify(f"Duplicated row [$success]{ridx + 1}[/]", title="Duplicate Row")
|
|
2525
2583
|
|
|
2526
2584
|
def do_move_column(self, direction: str) -> None:
|
|
2527
2585
|
"""Move the current column left or right.
|
|
@@ -2537,12 +2595,12 @@ class DataFrameTable(DataTable):
|
|
|
2537
2595
|
# Validate move is possible
|
|
2538
2596
|
if direction == "left":
|
|
2539
2597
|
if col_idx <= 0:
|
|
2540
|
-
self.notify("Cannot move column left", title="Move", severity="warning")
|
|
2598
|
+
# self.notify("Cannot move column left", title="Move Column", severity="warning")
|
|
2541
2599
|
return
|
|
2542
2600
|
swap_idx = col_idx - 1
|
|
2543
2601
|
elif direction == "right":
|
|
2544
2602
|
if col_idx >= len(self.columns) - 1:
|
|
2545
|
-
self.notify("Cannot move column right", title="Move", severity="warning")
|
|
2603
|
+
# self.notify("Cannot move column right", title="Move Column", severity="warning")
|
|
2546
2604
|
return
|
|
2547
2605
|
swap_idx = col_idx + 1
|
|
2548
2606
|
|
|
@@ -2583,7 +2641,7 @@ class DataFrameTable(DataTable):
|
|
|
2583
2641
|
if self.df_view is not None:
|
|
2584
2642
|
self.df_view = self.df_view.select(cols)
|
|
2585
2643
|
|
|
2586
|
-
# self.notify(f"Moved column [$success]{col_name}[/] {direction}", title="Move")
|
|
2644
|
+
# self.notify(f"Moved column [$success]{col_name}[/] {direction}", title="Move Column")
|
|
2587
2645
|
|
|
2588
2646
|
def do_move_row(self, direction: str) -> None:
|
|
2589
2647
|
"""Move the current row up or down.
|
|
@@ -2596,12 +2654,12 @@ class DataFrameTable(DataTable):
|
|
|
2596
2654
|
# Validate move is possible
|
|
2597
2655
|
if direction == "up":
|
|
2598
2656
|
if curr_row_idx <= 0:
|
|
2599
|
-
self.notify("Cannot move row up", title="Move", severity="warning")
|
|
2657
|
+
# self.notify("Cannot move row up", title="Move Row", severity="warning")
|
|
2600
2658
|
return
|
|
2601
2659
|
swap_row_idx = curr_row_idx - 1
|
|
2602
2660
|
elif direction == "down":
|
|
2603
2661
|
if curr_row_idx >= len(self.rows) - 1:
|
|
2604
|
-
self.notify("Cannot move row down", title="Move", severity="warning")
|
|
2662
|
+
# self.notify("Cannot move row down", title="Move Row", severity="warning")
|
|
2605
2663
|
return
|
|
2606
2664
|
swap_row_idx = curr_row_idx + 1
|
|
2607
2665
|
else:
|
|
@@ -2688,15 +2746,17 @@ class DataFrameTable(DataTable):
|
|
|
2688
2746
|
try:
|
|
2689
2747
|
target_dtype = eval(dtype)
|
|
2690
2748
|
except Exception:
|
|
2691
|
-
self.notify(
|
|
2749
|
+
self.notify(
|
|
2750
|
+
f"Invalid target data type: [$error]{dtype}[/]", title="Cast Column", severity="error", timeout=10
|
|
2751
|
+
)
|
|
2692
2752
|
return
|
|
2693
2753
|
|
|
2694
2754
|
if current_dtype == target_dtype:
|
|
2695
|
-
self.notify(
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
)
|
|
2755
|
+
# self.notify(
|
|
2756
|
+
# f"Column [$warning]{col_name}[/] is already of type [$accent]{target_dtype}[/]",
|
|
2757
|
+
# title="Cast Column",
|
|
2758
|
+
# severity="warning",
|
|
2759
|
+
# )
|
|
2700
2760
|
return # No change needed
|
|
2701
2761
|
|
|
2702
2762
|
# Add to history
|
|
@@ -2716,11 +2776,11 @@ class DataFrameTable(DataTable):
|
|
|
2716
2776
|
# Recreate table for display
|
|
2717
2777
|
self.setup_table()
|
|
2718
2778
|
|
|
2719
|
-
self.notify(f"Cast column [$success]{col_name}[/] to [$accent]{target_dtype}[/]", title="Cast")
|
|
2779
|
+
# self.notify(f"Cast column [$success]{col_name}[/] to [$accent]{target_dtype}[/]", title="Cast")
|
|
2720
2780
|
except Exception as e:
|
|
2721
2781
|
self.notify(
|
|
2722
2782
|
f"Error casting column [$error]{col_name}[/] to [$accent]{target_dtype}[/]",
|
|
2723
|
-
title="Cast",
|
|
2783
|
+
title="Cast Column",
|
|
2724
2784
|
severity="error",
|
|
2725
2785
|
timeout=10,
|
|
2726
2786
|
)
|
|
@@ -2865,7 +2925,7 @@ class DataFrameTable(DataTable):
|
|
|
2865
2925
|
|
|
2866
2926
|
# Check if we're highlighting or un-highlighting
|
|
2867
2927
|
if selected_count := len(self.selected_rows):
|
|
2868
|
-
self.notify(f"Toggled selection for [$success]{selected_count}[/] rows", title="Toggle")
|
|
2928
|
+
self.notify(f"Toggled selection for [$success]{selected_count}[/] rows", title="Toggle Selection(s)")
|
|
2869
2929
|
|
|
2870
2930
|
# Recreate table for display
|
|
2871
2931
|
self.setup_table()
|
|
@@ -2907,7 +2967,7 @@ class DataFrameTable(DataTable):
|
|
|
2907
2967
|
"""Clear all selected rows and matches without removing them from the dataframe."""
|
|
2908
2968
|
# Check if any selected rows or matches
|
|
2909
2969
|
if not self.selected_rows and not self.matches:
|
|
2910
|
-
self.notify("No selections to clear", title="Clear", severity="warning")
|
|
2970
|
+
# self.notify("No selections to clear", title="Clear Selections and Matches", severity="warning")
|
|
2911
2971
|
return
|
|
2912
2972
|
|
|
2913
2973
|
row_count = len(self.selected_rows | set(self.matches.keys()))
|
|
@@ -2922,7 +2982,7 @@ class DataFrameTable(DataTable):
|
|
|
2922
2982
|
# Recreate table for display
|
|
2923
2983
|
self.setup_table()
|
|
2924
2984
|
|
|
2925
|
-
self.notify(f"Cleared selections for [$success]{row_count}[/] rows", title="Clear")
|
|
2985
|
+
self.notify(f"Cleared selections for [$success]{row_count}[/] rows", title="Clear Selections and Matches")
|
|
2926
2986
|
|
|
2927
2987
|
# Find & Replace
|
|
2928
2988
|
def find_matches(
|
|
@@ -3096,7 +3156,7 @@ class DataFrameTable(DataTable):
|
|
|
3096
3156
|
def do_next_match(self) -> None:
|
|
3097
3157
|
"""Move cursor to the next match."""
|
|
3098
3158
|
if not self.matches:
|
|
3099
|
-
self.notify("No matches to navigate", title="Next Match", severity="warning")
|
|
3159
|
+
# self.notify("No matches to navigate", title="Next Match", severity="warning")
|
|
3100
3160
|
return
|
|
3101
3161
|
|
|
3102
3162
|
# Get sorted list of matched coordinates
|
|
@@ -3118,7 +3178,7 @@ class DataFrameTable(DataTable):
|
|
|
3118
3178
|
def do_previous_match(self) -> None:
|
|
3119
3179
|
"""Move cursor to the previous match."""
|
|
3120
3180
|
if not self.matches:
|
|
3121
|
-
self.notify("No matches to navigate", title="Previous Match", severity="warning")
|
|
3181
|
+
# self.notify("No matches to navigate", title="Previous Match", severity="warning")
|
|
3122
3182
|
return
|
|
3123
3183
|
|
|
3124
3184
|
# Get sorted list of matched coordinates
|
|
@@ -3146,7 +3206,7 @@ class DataFrameTable(DataTable):
|
|
|
3146
3206
|
def do_next_selected_row(self) -> None:
|
|
3147
3207
|
"""Move cursor to the next selected row."""
|
|
3148
3208
|
if not self.selected_rows:
|
|
3149
|
-
self.notify("No selected rows to navigate", title="Next Selected Row", severity="warning")
|
|
3209
|
+
# self.notify("No selected rows to navigate", title="Next Selected Row", severity="warning")
|
|
3150
3210
|
return
|
|
3151
3211
|
|
|
3152
3212
|
# Get list of selected row indices in order
|
|
@@ -3168,7 +3228,7 @@ class DataFrameTable(DataTable):
|
|
|
3168
3228
|
def do_previous_selected_row(self) -> None:
|
|
3169
3229
|
"""Move cursor to the previous selected row."""
|
|
3170
3230
|
if not self.selected_rows:
|
|
3171
|
-
self.notify("No selected rows to navigate", title="Previous Selected Row", severity="warning")
|
|
3231
|
+
# self.notify("No selected rows to navigate", title="Previous Selected Row", severity="warning")
|
|
3172
3232
|
return
|
|
3173
3233
|
|
|
3174
3234
|
# Get list of selected row indices in order
|
|
@@ -3567,7 +3627,7 @@ class DataFrameTable(DataTable):
|
|
|
3567
3627
|
expr = validate_expr(term, self.df.columns, cidx)
|
|
3568
3628
|
except Exception as e:
|
|
3569
3629
|
self.notify(
|
|
3570
|
-
f"Error validating expression [$error]{term}[/]", title="Filter", severity="error", timeout=10
|
|
3630
|
+
f"Error validating expression [$error]{term}[/]", title="Filter Rows", severity="error", timeout=10
|
|
3571
3631
|
)
|
|
3572
3632
|
self.log(f"Error validating expression `{term}`: {str(e)}")
|
|
3573
3633
|
return
|
|
@@ -3592,7 +3652,9 @@ class DataFrameTable(DataTable):
|
|
|
3592
3652
|
term = f"(?i){term}"
|
|
3593
3653
|
expr = pl.col(col_name).cast(pl.String).str.contains(term)
|
|
3594
3654
|
self.notify(
|
|
3595
|
-
f"Unknown column type [$warning]{dtype}[/]. Cast to string.",
|
|
3655
|
+
f"Unknown column type [$warning]{dtype}[/]. Cast to string.",
|
|
3656
|
+
title="View Rows",
|
|
3657
|
+
severity="warning",
|
|
3596
3658
|
)
|
|
3597
3659
|
|
|
3598
3660
|
# Lazyframe with row indices
|
|
@@ -3605,13 +3667,13 @@ class DataFrameTable(DataTable):
|
|
|
3605
3667
|
df_filtered = lf.filter(expr).collect()
|
|
3606
3668
|
except Exception as e:
|
|
3607
3669
|
self.histories_undo.pop() # Remove last history entry
|
|
3608
|
-
self.notify(f"Error applying filter [$error]{expr_str}[/]", title="
|
|
3670
|
+
self.notify(f"Error applying filter [$error]{expr_str}[/]", title="View Rows", severity="error", timeout=10)
|
|
3609
3671
|
self.log(f"Error applying filter `{expr_str}`: {str(e)}")
|
|
3610
3672
|
return
|
|
3611
3673
|
|
|
3612
3674
|
matched_count = len(df_filtered)
|
|
3613
3675
|
if not matched_count:
|
|
3614
|
-
self.notify(f"No rows match the expression: [$success]{expr}[/]", title="
|
|
3676
|
+
self.notify(f"No rows match the expression: [$success]{expr}[/]", title="View Rows", severity="warning")
|
|
3615
3677
|
return
|
|
3616
3678
|
|
|
3617
3679
|
# Add to history
|
|
@@ -3637,7 +3699,7 @@ class DataFrameTable(DataTable):
|
|
|
3637
3699
|
# Recreate table for display
|
|
3638
3700
|
self.setup_table()
|
|
3639
3701
|
|
|
3640
|
-
self.notify(f"Filtered to [$success]{matched_count}[/] matching row(s)", title="
|
|
3702
|
+
self.notify(f"Filtered to [$success]{matched_count}[/] matching row(s)", title="View Rows")
|
|
3641
3703
|
|
|
3642
3704
|
def do_filter_rows(self) -> None:
|
|
3643
3705
|
"""Filter rows.
|
|
@@ -3691,7 +3753,7 @@ class DataFrameTable(DataTable):
|
|
|
3691
3753
|
# Recreate table for display
|
|
3692
3754
|
self.setup_table()
|
|
3693
3755
|
|
|
3694
|
-
self.notify(f"{message}. Now showing [$success]{len(self.df)}[/] rows.", title="Filter")
|
|
3756
|
+
self.notify(f"{message}. Now showing [$success]{len(self.df)}[/] rows.", title="Filter Rows")
|
|
3695
3757
|
|
|
3696
3758
|
# Copy & Save
|
|
3697
3759
|
def do_copy_to_clipboard(self, content: str, message: str) -> None:
|
|
@@ -3713,9 +3775,9 @@ class DataFrameTable(DataTable):
|
|
|
3713
3775
|
input=content,
|
|
3714
3776
|
text=True,
|
|
3715
3777
|
)
|
|
3716
|
-
self.notify(message, title="Clipboard")
|
|
3778
|
+
self.notify(message, title="Copy to Clipboard")
|
|
3717
3779
|
except FileNotFoundError:
|
|
3718
|
-
self.notify("Error copying to clipboard", title="Clipboard", severity="error", timeout=10)
|
|
3780
|
+
self.notify("Error copying to clipboard", title="Copy to Clipboard", severity="error", timeout=10)
|
|
3719
3781
|
|
|
3720
3782
|
def do_save_to_file(self, all_tabs: bool | None = None, task_after_save: str | None = None) -> None:
|
|
3721
3783
|
"""Open screen to save file."""
|
{dataframe_textual-2.2.2 → dataframe_textual-2.3.0}/src/dataframe_textual/data_frame_viewer.py
RENAMED
|
@@ -137,7 +137,9 @@ class DataFrameViewer(App):
|
|
|
137
137
|
except Exception as e:
|
|
138
138
|
self.notify(
|
|
139
139
|
f"Error loading [$error]{filename}[/]: Try [$accent]-I[/] to disable schema inference",
|
|
140
|
+
title="Load File",
|
|
140
141
|
severity="error",
|
|
142
|
+
timeout=10,
|
|
141
143
|
)
|
|
142
144
|
self.log(f"Error loading `{filename}`: {str(e)}")
|
|
143
145
|
|
|
@@ -166,7 +168,7 @@ class DataFrameViewer(App):
|
|
|
166
168
|
"""
|
|
167
169
|
if event.key == "k":
|
|
168
170
|
self.theme = get_next_item(list(BUILTIN_THEMES.keys()), self.theme)
|
|
169
|
-
self.notify(f"Switched to theme: [$success]{self.theme}[/]", title="
|
|
171
|
+
self.notify(f"Switched to theme: [$success]{self.theme}[/]", title="SwitchTheme")
|
|
170
172
|
|
|
171
173
|
def on_click(self, event: Click) -> None:
|
|
172
174
|
"""Handle mouse click events on tabs.
|
|
@@ -360,7 +362,7 @@ class DataFrameViewer(App):
|
|
|
360
362
|
tabs = self.query_one(ContentTabs)
|
|
361
363
|
tabs.display = not tabs.display
|
|
362
364
|
# status = "shown" if tabs.display else "hidden"
|
|
363
|
-
# self.notify(f"Tab bar [$success]{status}[/]", title="Toggle")
|
|
365
|
+
# self.notify(f"Tab bar [$success]{status}[/]", title="Toggle Tab Bar")
|
|
364
366
|
|
|
365
367
|
def get_active_table(self) -> DataFrameTable | None:
|
|
366
368
|
"""Get the currently active DataFrameTable widget.
|
|
@@ -376,7 +378,8 @@ class DataFrameViewer(App):
|
|
|
376
378
|
if active_pane := tabbed.active_pane:
|
|
377
379
|
return active_pane.query_one(DataFrameTable)
|
|
378
380
|
except (NoMatches, AttributeError):
|
|
379
|
-
self.notify("No active table found", title="Locate", severity="error")
|
|
381
|
+
self.notify("No active table found", title="Locate Table", severity="error", timeout=10)
|
|
382
|
+
|
|
380
383
|
return None
|
|
381
384
|
|
|
382
385
|
def get_unique_tabname(self, tab_name: str) -> str:
|
|
@@ -414,11 +417,13 @@ class DataFrameViewer(App):
|
|
|
414
417
|
for source in load_file(filename, prefix_sheet=True):
|
|
415
418
|
self.add_tab(source.frame, filename, source.tabname, after=self.tabbed.active_pane)
|
|
416
419
|
n_tab += 1
|
|
417
|
-
# self.notify(f"Added [$accent]{n_tab}[/] tab(s) for [$success]{filename}[/]", title="Open")
|
|
420
|
+
# self.notify(f"Added [$accent]{n_tab}[/] tab(s) for [$success]{filename}[/]", title="Open File")
|
|
418
421
|
except Exception as e:
|
|
419
|
-
self.notify(
|
|
422
|
+
self.notify(
|
|
423
|
+
f"Error loading [$error]{filename}[/]: {str(e)}", title="Open File", severity="error", timeout=10
|
|
424
|
+
)
|
|
420
425
|
else:
|
|
421
|
-
self.notify(f"File does not exist: [$warning]{filename}[/]", title="Open", severity="warning")
|
|
426
|
+
self.notify(f"File does not exist: [$warning]{filename}[/]", title="Open File", severity="warning")
|
|
422
427
|
|
|
423
428
|
def add_tab(
|
|
424
429
|
self,
|
|
@@ -610,7 +615,7 @@ class DataFrameViewer(App):
|
|
|
610
615
|
content_tab, new_name = result
|
|
611
616
|
|
|
612
617
|
# Update the tab name
|
|
613
|
-
old_name = content_tab.label_text
|
|
618
|
+
# old_name = content_tab.label_text
|
|
614
619
|
content_tab.label = new_name
|
|
615
620
|
|
|
616
621
|
# Mark tab as dirty to indicate name change
|
|
@@ -622,4 +627,4 @@ class DataFrameViewer(App):
|
|
|
622
627
|
table.focus()
|
|
623
628
|
break
|
|
624
629
|
|
|
625
|
-
self.notify(f"Renamed tab [$accent]{old_name}[/] to [$success]{new_name}[/]", title="Rename")
|
|
630
|
+
# self.notify(f"Renamed tab [$accent]{old_name}[/] to [$success]{new_name}[/]", title="Rename Tab")
|