dataframe-textual 1.9.0__py3-none-any.whl → 2.2.2__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/__init__.py +27 -1
- dataframe_textual/__main__.py +13 -2
- dataframe_textual/common.py +139 -52
- dataframe_textual/data_frame_help_panel.py +0 -3
- dataframe_textual/data_frame_table.py +1336 -764
- dataframe_textual/data_frame_viewer.py +48 -10
- dataframe_textual/sql_screen.py +17 -20
- dataframe_textual/table_screen.py +164 -144
- dataframe_textual/yes_no_screen.py +34 -39
- {dataframe_textual-1.9.0.dist-info → dataframe_textual-2.2.2.dist-info}/METADATA +213 -215
- dataframe_textual-2.2.2.dist-info/RECORD +14 -0
- {dataframe_textual-1.9.0.dist-info → dataframe_textual-2.2.2.dist-info}/WHEEL +1 -1
- dataframe_textual-1.9.0.dist-info/RECORD +0 -14
- {dataframe_textual-1.9.0.dist-info → dataframe_textual-2.2.2.dist-info}/entry_points.txt +0 -0
- {dataframe_textual-1.9.0.dist-info → dataframe_textual-2.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -119,9 +119,6 @@ class YesNoScreen(ModalScreen):
|
|
|
119
119
|
maybe: Optional Maybe button text/dict. Defaults to None.
|
|
120
120
|
no: Text or dict for the No button. If None, hides the No button. Defaults to "No".
|
|
121
121
|
on_yes_callback: Optional callable that takes no args and returns the value to dismiss with when Yes is pressed. Defaults to None.
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
None
|
|
125
122
|
"""
|
|
126
123
|
super().__init__()
|
|
127
124
|
self.title = title
|
|
@@ -295,32 +292,26 @@ class SaveFileScreen(YesNoScreen):
|
|
|
295
292
|
|
|
296
293
|
CSS = YesNoScreen.DEFAULT_CSS.replace("YesNoScreen", "SaveFileScreen")
|
|
297
294
|
|
|
298
|
-
def __init__(
|
|
299
|
-
self
|
|
300
|
-
):
|
|
301
|
-
self.all_tabs = all_tabs or (all_tabs is None and multi_tab)
|
|
295
|
+
def __init__(self, filename: str, save_all: bool = False, tab_count: int = 1):
|
|
296
|
+
self.save_all = save_all
|
|
302
297
|
super().__init__(
|
|
303
|
-
title=
|
|
304
|
-
label="
|
|
298
|
+
title="Save to File",
|
|
299
|
+
label="Filename",
|
|
305
300
|
input=filename,
|
|
306
|
-
yes="Save",
|
|
307
|
-
maybe="Save All Tabs" if self.all_tabs else None,
|
|
301
|
+
yes=f"Save {tab_count} Tabs" if self.save_all else "Save Current Tab" if tab_count > 1 else "Save",
|
|
308
302
|
no="Cancel",
|
|
309
303
|
on_yes_callback=self.handle_save,
|
|
310
|
-
on_maybe_callback=self.handle_save,
|
|
311
304
|
)
|
|
312
305
|
|
|
313
306
|
def handle_save(self):
|
|
314
307
|
if self.input:
|
|
315
308
|
input_filename = self.input.value.strip()
|
|
316
309
|
if input_filename:
|
|
317
|
-
return input_filename, self.
|
|
310
|
+
return input_filename, self.save_all, True # Overwrite prompt
|
|
318
311
|
else:
|
|
319
312
|
self.notify("Filename cannot be empty", title="Save", severity="error")
|
|
320
313
|
return None
|
|
321
314
|
|
|
322
|
-
return None
|
|
323
|
-
|
|
324
315
|
|
|
325
316
|
class ConfirmScreen(YesNoScreen):
|
|
326
317
|
"""Modal screen to ask for confirmation."""
|
|
@@ -359,7 +350,7 @@ class EditCellScreen(YesNoScreen):
|
|
|
359
350
|
|
|
360
351
|
# Input
|
|
361
352
|
df_value = df.item(ridx, cidx)
|
|
362
|
-
self.input_value =
|
|
353
|
+
self.input_value = NULL if df_value is None else str(df_value)
|
|
363
354
|
|
|
364
355
|
super().__init__(
|
|
365
356
|
title="Edit Cell",
|
|
@@ -373,20 +364,20 @@ class EditCellScreen(YesNoScreen):
|
|
|
373
364
|
|
|
374
365
|
def _validate_input(self) -> None:
|
|
375
366
|
"""Validate and save the edited value."""
|
|
376
|
-
new_value_str = self.input.value
|
|
367
|
+
new_value_str = self.input.value # Do not strip to preserve spaces
|
|
377
368
|
|
|
378
369
|
# Handle empty input
|
|
379
370
|
if not new_value_str:
|
|
380
|
-
new_value =
|
|
371
|
+
new_value = ""
|
|
381
372
|
self.notify(
|
|
382
|
-
"Empty value provided. If you want to clear the cell, press [$accent]
|
|
383
|
-
title="Edit",
|
|
373
|
+
"Empty value provided. If you want to clear the cell, press [$accent]Delete[/].",
|
|
374
|
+
title="Edit Cell",
|
|
384
375
|
severity="warning",
|
|
385
376
|
)
|
|
386
377
|
# Check if value changed
|
|
387
378
|
elif new_value_str == self.input_value:
|
|
388
379
|
new_value = None
|
|
389
|
-
self.notify("No changes made", title="Edit", severity="warning")
|
|
380
|
+
self.notify("No changes made", title="Edit Cell", severity="warning")
|
|
390
381
|
else:
|
|
391
382
|
# Parse and validate based on column dtype
|
|
392
383
|
try:
|
|
@@ -394,7 +385,7 @@ class EditCellScreen(YesNoScreen):
|
|
|
394
385
|
except Exception as e:
|
|
395
386
|
self.notify(
|
|
396
387
|
f"Failed to convert [$accent]{new_value_str}[/] to [$error]{self.dtype}[/]: {str(e)}",
|
|
397
|
-
title="Edit",
|
|
388
|
+
title="Edit Cell",
|
|
398
389
|
severity="error",
|
|
399
390
|
)
|
|
400
391
|
return None
|
|
@@ -477,7 +468,7 @@ class SearchScreen(YesNoScreen):
|
|
|
477
468
|
|
|
478
469
|
def _validate_input(self) -> tuple[str, int, bool, bool]:
|
|
479
470
|
"""Validate the input and return it."""
|
|
480
|
-
term = self.input.value
|
|
471
|
+
term = self.input.value # Do not strip to preserve spaces
|
|
481
472
|
|
|
482
473
|
if not term:
|
|
483
474
|
self.notify("Term cannot be empty", title=self.title, severity="error")
|
|
@@ -494,13 +485,13 @@ class FilterScreen(YesNoScreen):
|
|
|
494
485
|
|
|
495
486
|
CSS = YesNoScreen.DEFAULT_CSS.replace("YesNoScreen", "FilterScreen")
|
|
496
487
|
|
|
497
|
-
def __init__(self, df: pl.DataFrame, cidx: int,
|
|
488
|
+
def __init__(self, df: pl.DataFrame, cidx: int, term: str | None = None):
|
|
498
489
|
self.df = df
|
|
499
490
|
self.cidx = cidx
|
|
500
491
|
super().__init__(
|
|
501
492
|
title="Filter by Expression",
|
|
502
493
|
label="e.g., NULL, $1 > 50, $name == 'text', $_ > 100, $a < $b, $_.str.contains('sub')",
|
|
503
|
-
input=
|
|
494
|
+
input=term,
|
|
504
495
|
checkbox="Match Nocase",
|
|
505
496
|
checkbox2="Match Whole",
|
|
506
497
|
on_yes_callback=self._get_input,
|
|
@@ -508,7 +499,7 @@ class FilterScreen(YesNoScreen):
|
|
|
508
499
|
|
|
509
500
|
def _get_input(self) -> tuple[str, int, bool, bool]:
|
|
510
501
|
"""Get input."""
|
|
511
|
-
term = self.input.value
|
|
502
|
+
term = self.input.value # Do not strip to preserve spaces
|
|
512
503
|
match_nocase = self.checkbox.value
|
|
513
504
|
match_whole = self.checkbox2.value
|
|
514
505
|
|
|
@@ -583,14 +574,14 @@ class EditColumnScreen(YesNoScreen):
|
|
|
583
574
|
self.df = df
|
|
584
575
|
super().__init__(
|
|
585
576
|
title="Edit Column",
|
|
586
|
-
label=f"
|
|
577
|
+
label=f"By value or Polars expression, e.g., abc, pl.lit(7), {NULL}, $_ * 2, $1 + $2, $_.str.to_uppercase(), pl.arange(0, pl.len())",
|
|
587
578
|
input="$_",
|
|
588
579
|
on_yes_callback=self._get_input,
|
|
589
580
|
)
|
|
590
581
|
|
|
591
582
|
def _get_input(self) -> tuple[str, int]:
|
|
592
583
|
"""Get input."""
|
|
593
|
-
term = self.input.value
|
|
584
|
+
term = self.input.value # Do not strip to preserve spaces
|
|
594
585
|
return term, self.cidx
|
|
595
586
|
|
|
596
587
|
|
|
@@ -606,19 +597,19 @@ class AddColumnScreen(YesNoScreen):
|
|
|
606
597
|
self.existing_columns = set(df.columns)
|
|
607
598
|
super().__init__(
|
|
608
599
|
title="Add Column",
|
|
609
|
-
label="
|
|
610
|
-
input="Link" if link else "
|
|
611
|
-
label2="
|
|
600
|
+
label="Column name",
|
|
601
|
+
input="Link" if link else "New column",
|
|
602
|
+
label2="Link template, e.g., https://example.com/$1/id/$_, PC/compound/$cid"
|
|
612
603
|
if link
|
|
613
|
-
else "
|
|
614
|
-
input2="Link template" if link else "
|
|
604
|
+
else "Value or Polars expression, e.g., abc, pl.lit(123), NULL, $_ * 2, $1 + $total, $_ + '_suffix', $_.str.to_uppercase()",
|
|
605
|
+
input2="Link template" if link else "Value or expression",
|
|
615
606
|
on_yes_callback=self._get_input,
|
|
616
607
|
)
|
|
617
608
|
|
|
618
609
|
def _get_input(self) -> tuple[int, str, str] | None:
|
|
619
610
|
"""Validate and return the new column configuration."""
|
|
620
611
|
col_name = self.input.value.strip()
|
|
621
|
-
term = self.input2.value
|
|
612
|
+
term = self.input2.value # Do not strip to preserve spaces
|
|
622
613
|
|
|
623
614
|
# Validate column name
|
|
624
615
|
if not col_name:
|
|
@@ -680,7 +671,11 @@ class FindReplaceScreen(YesNoScreen):
|
|
|
680
671
|
CSS = YesNoScreen.DEFAULT_CSS.replace("YesNoScreen", "ReplaceScreen")
|
|
681
672
|
|
|
682
673
|
def __init__(self, dftable: "DataFrameTable", title: str = "Find and Replace"):
|
|
683
|
-
|
|
674
|
+
if (cursor_value := dftable.cursor_value) is None:
|
|
675
|
+
term_find = NULL
|
|
676
|
+
else:
|
|
677
|
+
term_find = str(cursor_value)
|
|
678
|
+
|
|
684
679
|
super().__init__(
|
|
685
680
|
title=title,
|
|
686
681
|
label="Find",
|
|
@@ -698,8 +693,8 @@ class FindReplaceScreen(YesNoScreen):
|
|
|
698
693
|
|
|
699
694
|
def _get_input(self) -> tuple[str, str, bool, bool, bool]:
|
|
700
695
|
"""Get input."""
|
|
701
|
-
term_find = self.input.value
|
|
702
|
-
term_replace = self.input2.value
|
|
696
|
+
term_find = self.input.value # Do not strip to preserve spaces
|
|
697
|
+
term_replace = self.input2.value # Do not strip to preserve spaces
|
|
703
698
|
match_nocase = self.checkbox.value
|
|
704
699
|
match_whole = self.checkbox2.value
|
|
705
700
|
replace_all = False
|
|
@@ -708,8 +703,8 @@ class FindReplaceScreen(YesNoScreen):
|
|
|
708
703
|
|
|
709
704
|
def _get_input_replace_all(self) -> tuple[str, str, bool, bool, bool]:
|
|
710
705
|
"""Get input for 'Replace All'."""
|
|
711
|
-
term_find = self.input.value
|
|
712
|
-
term_replace = self.input2.value
|
|
706
|
+
term_find = self.input.value # Do not strip to preserve spaces
|
|
707
|
+
term_replace = self.input2.value # Do not strip to preserve spaces
|
|
713
708
|
match_nocase = self.checkbox.value
|
|
714
709
|
match_whole = self.checkbox2.value
|
|
715
710
|
replace_all = True
|