dataframe-textual 2.0.0__py3-none-any.whl → 2.1.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.
- dataframe_textual/data_frame_table.py +61 -31
- dataframe_textual/data_frame_viewer.py +1 -1
- dataframe_textual/table_screen.py +1 -1
- {dataframe_textual-2.0.0.dist-info → dataframe_textual-2.1.0.dist-info}/METADATA +19 -18
- {dataframe_textual-2.0.0.dist-info → dataframe_textual-2.1.0.dist-info}/RECORD +8 -8
- {dataframe_textual-2.0.0.dist-info → dataframe_textual-2.1.0.dist-info}/WHEEL +0 -0
- {dataframe_textual-2.0.0.dist-info → dataframe_textual-2.1.0.dist-info}/entry_points.txt +0 -0
- {dataframe_textual-2.0.0.dist-info → dataframe_textual-2.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -20,6 +20,7 @@ from textual.widgets._data_table import (
|
|
|
20
20
|
CellDoesNotExist,
|
|
21
21
|
CellKey,
|
|
22
22
|
CellType,
|
|
23
|
+
Column,
|
|
23
24
|
ColumnKey,
|
|
24
25
|
CursorType,
|
|
25
26
|
DuplicateKey,
|
|
@@ -79,14 +80,13 @@ class History:
|
|
|
79
80
|
df: pl.DataFrame
|
|
80
81
|
df_view: pl.DataFrame | None
|
|
81
82
|
filename: str
|
|
82
|
-
loaded_rows: int
|
|
83
83
|
hidden_columns: set[str]
|
|
84
84
|
selected_rows: set[int]
|
|
85
85
|
sorted_columns: dict[str, bool] # col_name -> descending
|
|
86
|
+
matches: dict[int, set[str]] # RID -> set of col names
|
|
86
87
|
fixed_rows: int
|
|
87
88
|
fixed_columns: int
|
|
88
89
|
cursor_coordinate: Coordinate
|
|
89
|
-
matches: dict[int, set[str]] # RID -> set of col names
|
|
90
90
|
dirty: bool = False # Whether this history state has unsaved changes
|
|
91
91
|
|
|
92
92
|
|
|
@@ -141,7 +141,7 @@ class DataFrameTable(DataTable):
|
|
|
141
141
|
- **M** - 📋 Show column metadata (ID, name, type)
|
|
142
142
|
- **h** - 👁️ Hide current column
|
|
143
143
|
- **H** - 👀 Show all hidden rows/columns
|
|
144
|
-
- **_** - 📏
|
|
144
|
+
- **_** - 📏 Toggle column full width
|
|
145
145
|
- **z** - 📌 Freeze rows and columns
|
|
146
146
|
- **~** - 🏷️ Toggle row labels
|
|
147
147
|
- **,** - 🔢 Toggle thousand separator for numeric display
|
|
@@ -352,6 +352,9 @@ class DataFrameTable(DataTable):
|
|
|
352
352
|
# Whether to use thousand separator for numeric display
|
|
353
353
|
self.thousand_separator = False
|
|
354
354
|
|
|
355
|
+
# Set of columns expanded to full width
|
|
356
|
+
self.expanded_columns: set[str] = set()
|
|
357
|
+
|
|
355
358
|
# Whether to show internal row index column
|
|
356
359
|
self.show_rid = False
|
|
357
360
|
|
|
@@ -1036,7 +1039,7 @@ class DataFrameTable(DataTable):
|
|
|
1036
1039
|
Returns:
|
|
1037
1040
|
dict[str, int]: Mapping of column name to width (None for auto-sizing columns).
|
|
1038
1041
|
"""
|
|
1039
|
-
|
|
1042
|
+
col_widths, col_label_widths = {}, {}
|
|
1040
1043
|
|
|
1041
1044
|
# Get available width for the table (with some padding for borders/scrollbar)
|
|
1042
1045
|
available_width = self.scrollable_content_region.width
|
|
@@ -1046,7 +1049,7 @@ class DataFrameTable(DataTable):
|
|
|
1046
1049
|
|
|
1047
1050
|
# No string columns, let TextualDataTable auto-size all columns
|
|
1048
1051
|
if not string_cols:
|
|
1049
|
-
return
|
|
1052
|
+
return col_widths
|
|
1050
1053
|
|
|
1051
1054
|
# Sample a reasonable number of rows to calculate widths (don't scan entire dataframe)
|
|
1052
1055
|
sample_size = min(self.BATCH_SIZE, len(self.df))
|
|
@@ -1060,7 +1063,10 @@ class DataFrameTable(DataTable):
|
|
|
1060
1063
|
# Get column label width
|
|
1061
1064
|
# Add padding for sort indicators if any
|
|
1062
1065
|
label_width = measure(self.app.console, col, 1) + 2
|
|
1063
|
-
|
|
1066
|
+
col_label_widths[col] = label_width
|
|
1067
|
+
|
|
1068
|
+
# Let Textual auto-size for non-string columns and already expanded columns
|
|
1069
|
+
if dtype != pl.String or col in self.expanded_columns:
|
|
1064
1070
|
available_width -= label_width
|
|
1065
1071
|
continue
|
|
1066
1072
|
|
|
@@ -1083,16 +1089,16 @@ class DataFrameTable(DataTable):
|
|
|
1083
1089
|
max_width = label_width
|
|
1084
1090
|
self.log(f"Error determining width for column '{col}': {e}")
|
|
1085
1091
|
|
|
1086
|
-
|
|
1092
|
+
col_widths[col] = max_width
|
|
1087
1093
|
available_width -= max_width
|
|
1088
1094
|
|
|
1089
1095
|
# If there's no more available width, auto-size remaining columns
|
|
1090
1096
|
if available_width < 0:
|
|
1091
|
-
for col in
|
|
1092
|
-
if
|
|
1093
|
-
|
|
1097
|
+
for col in col_widths:
|
|
1098
|
+
if col_widths[col] > STRING_WIDTH_CAP and col_label_widths[col] < STRING_WIDTH_CAP:
|
|
1099
|
+
col_widths[col] = STRING_WIDTH_CAP # Cap string columns
|
|
1094
1100
|
|
|
1095
|
-
return
|
|
1101
|
+
return col_widths
|
|
1096
1102
|
|
|
1097
1103
|
def setup_columns(self) -> None:
|
|
1098
1104
|
"""Clear table and setup columns.
|
|
@@ -1487,6 +1493,9 @@ class DataFrameTable(DataTable):
|
|
|
1487
1493
|
if start % self.BATCH_SIZE != 0:
|
|
1488
1494
|
start = (start // self.BATCH_SIZE + 1) * self.BATCH_SIZE
|
|
1489
1495
|
|
|
1496
|
+
if stop - start < self.BATCH_SIZE:
|
|
1497
|
+
start -= self.BATCH_SIZE
|
|
1498
|
+
|
|
1490
1499
|
self.load_rows_range(start, stop)
|
|
1491
1500
|
self.move_cursor(row=self.row_count - 1)
|
|
1492
1501
|
|
|
@@ -1519,14 +1528,13 @@ class DataFrameTable(DataTable):
|
|
|
1519
1528
|
df=self.df,
|
|
1520
1529
|
df_view=self.df_view,
|
|
1521
1530
|
filename=self.filename,
|
|
1522
|
-
loaded_rows=self.loaded_rows,
|
|
1523
1531
|
hidden_columns=self.hidden_columns.copy(),
|
|
1524
1532
|
selected_rows=self.selected_rows.copy(),
|
|
1525
1533
|
sorted_columns=self.sorted_columns.copy(),
|
|
1534
|
+
matches={k: v.copy() for k, v in self.matches.items()},
|
|
1526
1535
|
fixed_rows=self.fixed_rows,
|
|
1527
1536
|
fixed_columns=self.fixed_columns,
|
|
1528
1537
|
cursor_coordinate=self.cursor_coordinate,
|
|
1529
|
-
matches={k: v.copy() for k, v in self.matches.items()},
|
|
1530
1538
|
dirty=self.dirty,
|
|
1531
1539
|
)
|
|
1532
1540
|
|
|
@@ -1539,14 +1547,13 @@ class DataFrameTable(DataTable):
|
|
|
1539
1547
|
self.df = history.df
|
|
1540
1548
|
self.df_view = history.df_view
|
|
1541
1549
|
self.filename = history.filename
|
|
1542
|
-
self.loaded_rows = history.loaded_rows
|
|
1543
1550
|
self.hidden_columns = history.hidden_columns.copy()
|
|
1544
1551
|
self.selected_rows = history.selected_rows.copy()
|
|
1545
1552
|
self.sorted_columns = history.sorted_columns.copy()
|
|
1553
|
+
self.matches = {k: v.copy() for k, v in history.matches.items()} if history.matches else defaultdict(set)
|
|
1546
1554
|
self.fixed_rows = history.fixed_rows
|
|
1547
1555
|
self.fixed_columns = history.fixed_columns
|
|
1548
1556
|
self.cursor_coordinate = history.cursor_coordinate
|
|
1549
|
-
self.matches = {k: v.copy() for k, v in history.matches.items()} if history.matches else defaultdict(set)
|
|
1550
1557
|
self.dirty = history.dirty
|
|
1551
1558
|
|
|
1552
1559
|
# Recreate table for display
|
|
@@ -1710,11 +1717,15 @@ class DataFrameTable(DataTable):
|
|
|
1710
1717
|
if dtype != pl.String:
|
|
1711
1718
|
return
|
|
1712
1719
|
|
|
1720
|
+
# The column to expand/shrink
|
|
1721
|
+
col: Column = self.columns[col_key]
|
|
1722
|
+
|
|
1713
1723
|
# Calculate the maximum width across all loaded rows
|
|
1714
|
-
|
|
1724
|
+
label_width = len(col_name) + 2 # Start with column name width + padding
|
|
1715
1725
|
|
|
1716
1726
|
try:
|
|
1717
1727
|
need_expand = False
|
|
1728
|
+
max_width = label_width
|
|
1718
1729
|
|
|
1719
1730
|
# Scan through all loaded rows that are visible to find max width
|
|
1720
1731
|
for row_idx in range(self.loaded_rows):
|
|
@@ -1728,22 +1739,28 @@ class DataFrameTable(DataTable):
|
|
|
1728
1739
|
if not need_expand:
|
|
1729
1740
|
return
|
|
1730
1741
|
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1742
|
+
if col_name in self.expanded_columns:
|
|
1743
|
+
col.width = max(label_width, STRING_WIDTH_CAP)
|
|
1744
|
+
self.expanded_columns.remove(col_name)
|
|
1745
|
+
else:
|
|
1746
|
+
self.expanded_columns.add(col_name)
|
|
1734
1747
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
self._require_update_dimensions = True
|
|
1738
|
-
self.refresh(layout=True)
|
|
1748
|
+
# Update the column width
|
|
1749
|
+
col.width = max_width
|
|
1739
1750
|
|
|
1740
|
-
# self.notify(f"Expanded column [$success]{col_name}[/] to width [$accent]{max_width}[/]", title="Expand")
|
|
1741
1751
|
except Exception as e:
|
|
1742
1752
|
self.notify(
|
|
1743
1753
|
f"Error expanding column [$error]{col_name}[/]", title="Expand Column", severity="error", timeout=10
|
|
1744
1754
|
)
|
|
1745
1755
|
self.log(f"Error expanding column `{col_name}`: {str(e)}")
|
|
1746
1756
|
|
|
1757
|
+
# Force a refresh
|
|
1758
|
+
self._update_count += 1
|
|
1759
|
+
self._require_update_dimensions = True
|
|
1760
|
+
self.refresh(layout=True)
|
|
1761
|
+
|
|
1762
|
+
# self.notify(f"Expanded column [$success]{col_name}[/] to width [$accent]{max_width}[/]", title="Expand")
|
|
1763
|
+
|
|
1747
1764
|
def do_toggle_rid(self) -> None:
|
|
1748
1765
|
"""Toggle display of the internal RID column."""
|
|
1749
1766
|
self.show_rid = not self.show_rid
|
|
@@ -1965,13 +1982,21 @@ class DataFrameTable(DataTable):
|
|
|
1965
1982
|
if self.df_view is not None:
|
|
1966
1983
|
# Get updated column from df for rows that exist in df_view
|
|
1967
1984
|
col_updated = f"^_{col_name}_^"
|
|
1968
|
-
|
|
1969
|
-
|
|
1985
|
+
col_exists = "^_exists_^"
|
|
1986
|
+
lf_updated = self.df.lazy().select(
|
|
1987
|
+
RID, pl.col(col_name).alias(col_updated), pl.lit(True).alias(col_exists)
|
|
1988
|
+
)
|
|
1989
|
+
# Join and use when/then/otherwise to handle all updates including NULLs
|
|
1970
1990
|
self.df_view = (
|
|
1971
1991
|
self.df_view.lazy()
|
|
1972
1992
|
.join(lf_updated, on=RID, how="left")
|
|
1973
|
-
.with_columns(
|
|
1974
|
-
|
|
1993
|
+
.with_columns(
|
|
1994
|
+
pl.when(pl.col(col_exists))
|
|
1995
|
+
.then(pl.col(col_updated))
|
|
1996
|
+
.otherwise(pl.col(col_name))
|
|
1997
|
+
.alias(col_name)
|
|
1998
|
+
)
|
|
1999
|
+
.drop(col_updated, col_exists)
|
|
1975
2000
|
.collect()
|
|
1976
2001
|
)
|
|
1977
2002
|
except Exception as e:
|
|
@@ -2269,7 +2294,12 @@ class DataFrameTable(DataTable):
|
|
|
2269
2294
|
"""Remove the currently selected column from the table."""
|
|
2270
2295
|
# Get the column to remove
|
|
2271
2296
|
col_idx = self.cursor_column
|
|
2272
|
-
|
|
2297
|
+
try:
|
|
2298
|
+
col_name = self.cursor_col_name
|
|
2299
|
+
except CellDoesNotExist:
|
|
2300
|
+
self.notify("No column to delete at the current cursor position", title="Delete Column", severity="warning")
|
|
2301
|
+
return
|
|
2302
|
+
|
|
2273
2303
|
col_key = self.cursor_col_key
|
|
2274
2304
|
|
|
2275
2305
|
col_names_to_remove = []
|
|
@@ -2334,7 +2364,7 @@ class DataFrameTable(DataTable):
|
|
|
2334
2364
|
if self.df_view is not None:
|
|
2335
2365
|
self.df_view = self.df_view.drop(col_names_to_remove)
|
|
2336
2366
|
|
|
2337
|
-
self.notify(message, title="Delete")
|
|
2367
|
+
self.notify(message, title="Delete Column")
|
|
2338
2368
|
|
|
2339
2369
|
def do_duplicate_column(self) -> None:
|
|
2340
2370
|
"""Duplicate the currently selected column, inserting it right after the current column."""
|
|
@@ -2386,7 +2416,7 @@ class DataFrameTable(DataTable):
|
|
|
2386
2416
|
# Delete all selected rows
|
|
2387
2417
|
if selected_count := len(self.selected_rows):
|
|
2388
2418
|
history_desc = f"Deleted {selected_count} selected row(s)"
|
|
2389
|
-
rids_to_delete
|
|
2419
|
+
rids_to_delete.update(self.selected_rows)
|
|
2390
2420
|
|
|
2391
2421
|
# Delete current row and those above
|
|
2392
2422
|
elif more == "above":
|
|
@@ -163,7 +163,7 @@ class RowDetailScreen(TableScreen):
|
|
|
163
163
|
|
|
164
164
|
# Get all columns and values from the dataframe row
|
|
165
165
|
for col, val, dtype in zip(self.df.columns, self.df.row(self.ridx), self.df.dtypes):
|
|
166
|
-
if col == RID:
|
|
166
|
+
if col in self.dftable.hidden_columns or col == RID:
|
|
167
167
|
continue # Skip RID column
|
|
168
168
|
formatted_row = []
|
|
169
169
|
formatted_row.append(col)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframe-textual
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.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
|
|
@@ -247,8 +247,8 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
247
247
|
| `Q` | Close all tabs and app (prompts to save unsaved changes) |
|
|
248
248
|
| `Ctrl+Q` | Force to quit app (regardless of unsaved changes) |
|
|
249
249
|
| `Ctrl+T` | Save current tab to file |
|
|
250
|
+
| `Ctrl+A` | Save all tabs to a Excel file |
|
|
250
251
|
| `w` | Save current tab to file (overwrite without prompt) |
|
|
251
|
-
| `Ctrl+A` | Save all tabs to file |
|
|
252
252
|
| `W` | Save all tabs to file (overwrite without prompt) |
|
|
253
253
|
| `Ctrl+D` | Duplicate current tab |
|
|
254
254
|
| `Ctrl+O` | Open file in a new tab |
|
|
@@ -271,7 +271,7 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
271
271
|
| Key | Action |
|
|
272
272
|
|-----|--------|
|
|
273
273
|
| `g` | Jump to first row |
|
|
274
|
-
| `G` | Jump to last row
|
|
274
|
+
| `G` | Jump to last row |
|
|
275
275
|
| `↑` / `↓` | Move up/down one row |
|
|
276
276
|
| `←` / `→` | Move left/right one column |
|
|
277
277
|
| `Home` / `End` | Jump to first/last column |
|
|
@@ -295,9 +295,11 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
295
295
|
| `F` | Show frequency distribution for current column |
|
|
296
296
|
| `s` | Show statistics for current column |
|
|
297
297
|
| `S` | Show statistics for entire dataframe |
|
|
298
|
+
| `m` | Show metadata for row count and column count |
|
|
299
|
+
| `M` | Show metadata for current column |
|
|
298
300
|
| `K` | Cycle cursor types: cell → row → column → cell |
|
|
299
301
|
| `~` | Toggle row labels |
|
|
300
|
-
| `_` (underscore) |
|
|
302
|
+
| `_` (underscore) | Toggle column full width |
|
|
301
303
|
| `z` | Freeze rows and columns |
|
|
302
304
|
| `,` | Toggle thousand separator for numeric display |
|
|
303
305
|
| `h` | Hide current column |
|
|
@@ -318,7 +320,7 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
318
320
|
| `x` | Delete current row |
|
|
319
321
|
| `X` | Delete current row and all those below |
|
|
320
322
|
| `Ctrl+X` | Delete current row and all those above |
|
|
321
|
-
| `d` | Duplicate current column
|
|
323
|
+
| `d` | Duplicate current column |
|
|
322
324
|
| `D` | Duplicate current row |
|
|
323
325
|
|
|
324
326
|
#### Row Selection
|
|
@@ -392,7 +394,7 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
392
394
|
| `c` | Copy current cell to clipboard |
|
|
393
395
|
| `Ctrl+C` | Copy column to clipboard |
|
|
394
396
|
| `Ctrl+R` | Copy row to clipboard (tab-separated) |
|
|
395
|
-
| `Ctrl+S` | Save
|
|
397
|
+
| `Ctrl+S` | Save to file |
|
|
396
398
|
|
|
397
399
|
## Features in Detail
|
|
398
400
|
|
|
@@ -413,8 +415,8 @@ Useful for examining wide datasets where columns don't fit well on screen.
|
|
|
413
415
|
**In the Row Detail Modal**:
|
|
414
416
|
- Press `v` to **view** all rows containing the selected column value (others hidden but preserved)
|
|
415
417
|
- Press `"` to **filter** all rows containing the selected column value (others removed)
|
|
416
|
-
- Press `{` to move to the
|
|
417
|
-
- Press `}` to move to the
|
|
418
|
+
- Press `{` to move to the previous row
|
|
419
|
+
- Press `}` to move to the next row
|
|
418
420
|
- Press `q` or `Escape` to close the modal
|
|
419
421
|
|
|
420
422
|
### 3. Row Selection
|
|
@@ -429,7 +431,7 @@ The application provides multiple modes for selecting rows (marks it for filteri
|
|
|
429
431
|
- `{` - Go to previous selected row
|
|
430
432
|
- `}` - Go to next selected row
|
|
431
433
|
|
|
432
|
-
**Advanced
|
|
434
|
+
**Advanced Options**:
|
|
433
435
|
|
|
434
436
|
When searching or finding, you can use checkboxes in the dialog to enable:
|
|
435
437
|
- **Match Nocase**: Ignore case differences
|
|
@@ -445,7 +447,7 @@ These options work with plain text searches. Use Polars regex patterns in expres
|
|
|
445
447
|
- Use `u` to undo any search or filter
|
|
446
448
|
|
|
447
449
|
### 4. Find & Replace
|
|
448
|
-
|
|
450
|
+
Find by value/expression and highlight matching cells:
|
|
449
451
|
- `/` - Find cursor value within current column (respects data type)
|
|
450
452
|
- `?` - Open dialog to search current column with expression
|
|
451
453
|
- `;` - Find cursor value across all columns
|
|
@@ -469,10 +471,10 @@ When you press `r` or `R`, enter:
|
|
|
469
471
|
|
|
470
472
|
**Replace Interactive**:
|
|
471
473
|
- Review each match one at a time (confirm, skip, or cancel)
|
|
472
|
-
- Shows progress
|
|
474
|
+
- Shows progress
|
|
473
475
|
|
|
474
476
|
**Tips:**
|
|
475
|
-
- Search are done by string value (i.e
|
|
477
|
+
- Search are done by string value (i.e., ignoring data type)
|
|
476
478
|
- Type `NULL` to replace null/missing values
|
|
477
479
|
- Use `Match Nocase` for case-insensitive matching
|
|
478
480
|
- Use `Match Whole` to avoid partial replacements
|
|
@@ -480,7 +482,7 @@ When you press `r` or `R`, enter:
|
|
|
480
482
|
|
|
481
483
|
### 5. Filter vs. View
|
|
482
484
|
|
|
483
|
-
Both operations show
|
|
485
|
+
Both operations show selected rows but with fundamentally different effects:
|
|
484
486
|
|
|
485
487
|
| Operation | Keyboard | Effect | Data Preserved |
|
|
486
488
|
|-----------|----------|--------|-----------------|
|
|
@@ -571,8 +573,8 @@ View quick metadata about your dataframe and columns to understand their structu
|
|
|
571
573
|
|
|
572
574
|
**Dataframe Metadata** (`m`):
|
|
573
575
|
- Press `m` to open a modal displaying:
|
|
574
|
-
- **
|
|
575
|
-
- **
|
|
576
|
+
- **Row** - Total number of rows in the dataframe
|
|
577
|
+
- **Column** - Total number of columns in the dataframe
|
|
576
578
|
|
|
577
579
|
**Column Metadata** (`M`):
|
|
578
580
|
- Press `M` to open a modal displaying details for all columns:
|
|
@@ -751,7 +753,6 @@ SELECT specific columns and apply WHERE conditions without writing full SQL:
|
|
|
751
753
|
#### Advanced SQL Interface (`L`)
|
|
752
754
|
Execute complete SQL queries for advanced data manipulation:
|
|
753
755
|
- Write full SQL queries with standard [SQL syntax](https://docs.pola.rs/api/python/stable/reference/sql/index.html)
|
|
754
|
-
- Support for JOINs, GROUP BY, aggregations, and more
|
|
755
756
|
- Access to all SQL capabilities for complex transformations
|
|
756
757
|
- Always use `self` as the table name
|
|
757
758
|
- Syntax highlighted
|
|
@@ -773,7 +774,7 @@ WHERE `product id` = 7
|
|
|
773
774
|
|
|
774
775
|
Copies value to system clipboard with `pbcopy` on macOS and `xclip` on Linux.
|
|
775
776
|
|
|
776
|
-
**Note
|
|
777
|
+
**Note**: may require a X server to work.
|
|
777
778
|
|
|
778
779
|
- Press `c` to copy cursor value
|
|
779
780
|
- Press `Ctrl+C` to copy column values
|
|
@@ -815,8 +816,8 @@ Manage multiple files and dataframes simultaneously with tabs.
|
|
|
815
816
|
- **`Double-click`** - Rename the tab
|
|
816
817
|
- **`Ctrl+D`** - Duplicate current tab (creates a copy with same data and state)
|
|
817
818
|
- **`Ctrl+T`** - Save current tab to file
|
|
818
|
-
- **`w`** - Save current tab to file (overwrite without prompt)
|
|
819
819
|
- **`Ctrl+A`** - Save all tabs in a single Excel file
|
|
820
|
+
- **`w`** - Save current tab to file (overwrite without prompt)
|
|
820
821
|
- **`W`** - Save all tabs to file (overwrite without prompt)
|
|
821
822
|
- **`q`** - Close current tab (closes tab, prompts to save if unsaved changes)
|
|
822
823
|
- **`Q`** - Close all tabs and exit app (prompts to save tabs with unsaved changes)
|
|
@@ -2,13 +2,13 @@ dataframe_textual/__init__.py,sha256=E53fW1spQRA4jW9grxSqPEmoe9zofzr6twdveMbt_W8
|
|
|
2
2
|
dataframe_textual/__main__.py,sha256=vgHjpSsHBF34UN46iMgu_EiebZUzxWwJZ_ngOU3nQvI,3412
|
|
3
3
|
dataframe_textual/common.py,sha256=gpNNY5ZePJkJPf-YoLSCHKaBpdXQ1yW2iSKYV6zZYUo,27908
|
|
4
4
|
dataframe_textual/data_frame_help_panel.py,sha256=UEtj64XsVRdtLzuwOaITfoEQUkAfwFuvpr5Npip5WHs,3381
|
|
5
|
-
dataframe_textual/data_frame_table.py,sha256=
|
|
6
|
-
dataframe_textual/data_frame_viewer.py,sha256=
|
|
5
|
+
dataframe_textual/data_frame_table.py,sha256=pPddPOa3hV3voCf3_9botg0jeNcPdWsN7Oiw5vKdMdU,148701
|
|
6
|
+
dataframe_textual/data_frame_viewer.py,sha256=pX1xU3REQnMMEHapYKmr_FH1hFGExqTrOjjFWBx7dtg,22406
|
|
7
7
|
dataframe_textual/sql_screen.py,sha256=P3j1Fv45NIKEYo9adb7NPod54FaU-djFIvCUMMHbvjY,7534
|
|
8
|
-
dataframe_textual/table_screen.py,sha256=
|
|
8
|
+
dataframe_textual/table_screen.py,sha256=XPzJI6FXjwnxtQSMTmluygwkYM-0-Lx3v9o-MuL6bMg,19071
|
|
9
9
|
dataframe_textual/yes_no_screen.py,sha256=NI7Zt3rETDWYiT5CH_FDy7sIWkZ7d7LquaZZbX79b2g,26400
|
|
10
|
-
dataframe_textual-2.
|
|
11
|
-
dataframe_textual-2.
|
|
12
|
-
dataframe_textual-2.
|
|
13
|
-
dataframe_textual-2.
|
|
14
|
-
dataframe_textual-2.
|
|
10
|
+
dataframe_textual-2.1.0.dist-info/METADATA,sha256=6RZnWlkm6mWKnQNMUTUGBeklOFYXQgWtd1lOJmz5oX0,29160
|
|
11
|
+
dataframe_textual-2.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
dataframe_textual-2.1.0.dist-info/entry_points.txt,sha256=R_GoooOxcq6ab4RaHiVoZ4zrZJ-phMcGmlL2rwqncW8,107
|
|
13
|
+
dataframe_textual-2.1.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
|
|
14
|
+
dataframe_textual-2.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|