dataframe-textual 1.2.0__py3-none-any.whl → 1.3.9__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.
@@ -10,7 +10,7 @@ from textual.app import App, ComposeResult
10
10
  from textual.css.query import NoMatches
11
11
  from textual.theme import BUILTIN_THEMES
12
12
  from textual.widgets import TabbedContent, TabPane
13
- from textual.widgets.tabbed_content import ContentTab, ContentTabs
13
+ from textual.widgets.tabbed_content import ContentTabs
14
14
 
15
15
  from .common import get_next_item, load_file
16
16
  from .data_frame_help_panel import DataFrameHelpPanel
@@ -26,7 +26,7 @@ class DataFrameViewer(App):
26
26
 
27
27
  ## 🎯 File & Tab Management
28
28
  - **Ctrl+O** - 📁 Add a new tab
29
- - **Ctrl+Shift+S** - 💾 Save all tabs
29
+ - **Ctrl+A** - 💾 Save all tabs
30
30
  - **Ctrl+W** - ❌ Close current tab
31
31
  - **>** or **b** - ▶️ Next tab
32
32
  - **<** - ◀️ Previous tab
@@ -34,7 +34,7 @@ class DataFrameViewer(App):
34
34
  - **q** - 🚪 Quit application
35
35
 
36
36
  ## 🎨 View & Settings
37
- - **Ctrl+H** - ❓ Toggle this help panel
37
+ - **F1** - ❓ Toggle this help panel
38
38
  - **k** - 🌙 Cycle through themes
39
39
 
40
40
  ## ⭐ Features
@@ -51,30 +51,25 @@ class DataFrameViewer(App):
51
51
 
52
52
  BINDINGS = [
53
53
  ("q", "quit", "Quit"),
54
- ("ctrl+h", "toggle_help_panel", "Help"),
54
+ ("f1", "toggle_help_panel", "Help"),
55
55
  ("B", "toggle_tab_bar", "Toggle Tab Bar"),
56
56
  ("ctrl+o", "add_tab", "Add Tab"),
57
- ("ctrl+shift+s", "save_all_tabs", "Save All Tabs"),
57
+ ("ctrl+a", "save_all_tabs", "Save All Tabs"),
58
58
  ("ctrl+w", "close_tab", "Close Tab"),
59
59
  ("greater_than_sign,b", "next_tab(1)", "Next Tab"),
60
60
  ("less_than_sign", "next_tab(-1)", "Prev Tab"),
61
61
  ]
62
62
 
63
63
  CSS = """
64
- TabbedContent {
65
- height: 100%; /* Or a specific value, e.g., 20; */
66
- }
67
64
  TabbedContent > ContentTabs {
68
65
  dock: bottom;
69
66
  }
70
67
  TabbedContent > ContentSwitcher {
71
68
  overflow: auto;
72
- height: 1fr; /* Takes the remaining space below tabs */
69
+ height: 1fr;
73
70
  }
74
-
75
- TabbedContent ContentTab.active {
76
- background: $primary;
77
- color: $text;
71
+ ContentTab.-active {
72
+ background: $block-cursor-background; /* Same as underline */
78
73
  }
79
74
  """
80
75
 
@@ -178,12 +173,6 @@ class DataFrameViewer(App):
178
173
  if table.loaded_rows == 0:
179
174
  table._setup_table()
180
175
 
181
- # Apply background color to active tab
182
- event.tab.add_class("active")
183
- for tab in self.tabbed.query(ContentTab):
184
- if tab != event.tab:
185
- tab.remove_class("active")
186
-
187
176
  def action_toggle_help_panel(self) -> None:
188
177
  """Toggle the help panel on or off.
189
178
 
@@ -305,15 +294,15 @@ class DataFrameViewer(App):
305
294
  try:
306
295
  n_tab = 0
307
296
  for lf, filename, tabname in load_file(filename, prefix_sheet=True):
308
- self._add_tab(lf.collect(), filename, tabname)
297
+ self._add_tab(lf, filename, tabname)
309
298
  n_tab += 1
310
- self.notify(f"Added [$accent]{n_tab}[/] tab(s) for [$success]{filename}[/]", title="Open")
299
+ # self.notify(f"Added [$accent]{n_tab}[/] tab(s) for [$success]{filename}[/]", title="Open")
311
300
  except Exception as e:
312
- self.notify(f"Error: {e}", title="Open", severity="error")
301
+ self.notify(f"Error loading [$error]{filename}[/]: {str(e)}", title="Open", severity="error")
313
302
  else:
314
303
  self.notify(f"File does not exist: [$warning]{filename}[/]", title="Open", severity="warning")
315
304
 
316
- def _add_tab(self, df: pl.DataFrame, filename: str, tabname: str) -> None:
305
+ def _add_tab(self, df: pl.DataFrame | pl.LazyFrame, filename: str, tabname: str) -> None:
317
306
  """Add new tab for the given DataFrame.
318
307
 
319
308
  Creates and adds a new tab with the provided DataFrame and configuration.
@@ -321,15 +310,18 @@ class DataFrameViewer(App):
321
310
  if this is no longer the only tab.
322
311
 
323
312
  Args:
324
- df: The Polars DataFrame to display in the new tab.
313
+ lf: The Polars DataFrame to display in the new tab.
325
314
  filename: The source filename for this data (used in table metadata).
326
315
  tabname: The display name for the tab.
327
316
 
328
317
  Returns:
329
318
  None
330
319
  """
331
- if any(tab.name == tabname for tab in self.tabs):
332
- tabname = f"{tabname}_{len(self.tabs) + 1}"
320
+ # Ensure unique tab names
321
+ counter = 1
322
+ while any(tab.name == tabname for tab in self.tabs):
323
+ tabname = f"{tabname}_{counter}"
324
+ counter += 1
333
325
 
334
326
  # Find an available tab index
335
327
  tab_idx = f"tab_{len(self.tabs) + 1}"
@@ -369,6 +361,6 @@ class DataFrameViewer(App):
369
361
  if active_pane := self.tabbed.active_pane:
370
362
  self.tabbed.remove_pane(active_pane.id)
371
363
  self.tabs.pop(active_pane)
372
- self.notify(f"Closed tab [$success]{active_pane.name}[/]", title="Close")
364
+ # self.notify(f"Closed tab [$success]{active_pane.name}[/]", title="Close")
373
365
  except NoMatches:
374
366
  pass
@@ -0,0 +1,202 @@
1
+ """Modal screens for Polars sql manipulation"""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from .data_frame_table import DataFrameTable
7
+
8
+ import polars as pl
9
+ from textual.app import ComposeResult
10
+ from textual.containers import Container, Horizontal
11
+ from textual.screen import ModalScreen
12
+ from textual.widgets import Button, Input, Label, SelectionList, TextArea
13
+ from textual.widgets.selection_list import Selection
14
+
15
+
16
+ class SqlScreen(ModalScreen):
17
+ """Base class for modal screens handling SQL query."""
18
+
19
+ DEFAULT_CSS = """
20
+ SqlScreen {
21
+ align: center middle;
22
+ }
23
+
24
+ SqlScreen > Container {
25
+ width: auto;
26
+ height: auto;
27
+ border: heavy $accent;
28
+ border-title-color: $accent;
29
+ border-title-background: $panel;
30
+ border-title-style: bold;
31
+ background: $background;
32
+ padding: 1 2;
33
+ overflow: auto;
34
+ }
35
+
36
+ #button-container {
37
+ width: auto;
38
+ margin: 1 0 0 0;
39
+ height: 3;
40
+ align: center middle;
41
+ }
42
+
43
+ Button {
44
+ margin: 0 2;
45
+ }
46
+
47
+ """
48
+
49
+ def __init__(self, dftable: "DataFrameTable", on_yes_callback=None) -> None:
50
+ """Initialize the SQL screen."""
51
+ super().__init__()
52
+ self.dftable = dftable # DataFrameTable
53
+ self.df: pl.DataFrame = dftable.df # Polars DataFrame
54
+ self.on_yes_callback = on_yes_callback
55
+
56
+ def compose(self) -> ComposeResult:
57
+ """Compose the SQL screen widget structure."""
58
+ # Shared by subclasses
59
+ with Horizontal(id="button-container"):
60
+ yield Button("Apply", id="yes", variant="success")
61
+ yield Button("Cancel", id="no", variant="error")
62
+
63
+ def on_key(self, event) -> None:
64
+ """Handle key press events in the SQL screen"""
65
+ if event.key in ("q", "escape"):
66
+ self.app.pop_screen()
67
+ event.stop()
68
+ elif event.key == "enter":
69
+ self._handle_yes()
70
+ event.stop()
71
+ elif event.key == "escape":
72
+ self.dismiss(None)
73
+ event.stop()
74
+
75
+ def on_button_pressed(self, event: Button.Pressed) -> None:
76
+ """Handle button press events in the SQL screen."""
77
+ if event.button.id == "yes":
78
+ self._handle_yes()
79
+ elif event.button.id == "no":
80
+ self.dismiss(None)
81
+
82
+ def _handle_yes(self) -> None:
83
+ """Handle Yes button/Enter key press."""
84
+ if self.on_yes_callback:
85
+ result = self.on_yes_callback()
86
+ self.dismiss(result)
87
+ else:
88
+ self.dismiss(True)
89
+
90
+
91
+ class SimpleSqlScreen(SqlScreen):
92
+ """Simple SQL query screen."""
93
+
94
+ DEFAULT_CSS = SqlScreen.DEFAULT_CSS.replace("SqlScreen", "SimpleSqlScreen")
95
+
96
+ CSS = """
97
+ SimpleSqlScreen SelectionList {
98
+ width: auto;
99
+ min-width: 40;
100
+ margin: 1 0;
101
+ }
102
+
103
+ SimpleSqlScreen SelectionList:blur {
104
+ border: solid $secondary;
105
+ }
106
+
107
+ SimpleSqlScreen Label {
108
+ width: auto;
109
+ }
110
+
111
+ SimpleSqlScreen Input {
112
+ width: auto;
113
+ }
114
+
115
+ SimpleSqlScreen Input:blur {
116
+ border: solid $secondary;
117
+ }
118
+
119
+ #button-container {
120
+ min-width: 40;
121
+ }
122
+ """
123
+
124
+ def __init__(self, dftable: "DataFrameTable") -> None:
125
+ """Initialize the simple SQL screen.
126
+
127
+ Sets up the modal screen with reference to the main DataFrameTable widget
128
+ and stores the DataFrame for display.
129
+
130
+ Args:
131
+ dftable: Reference to the parent DataFrameTable widget.
132
+
133
+ Returns:
134
+ None
135
+ """
136
+ super().__init__(dftable, on_yes_callback=self._handle_simple)
137
+
138
+ def compose(self) -> ComposeResult:
139
+ """Compose the simple SQL screen widget structure."""
140
+ with Container(id="sql-container") as container:
141
+ container.border_title = "SQL Query"
142
+ yield Label("Select columns (default to all):", id="select-label")
143
+ yield SelectionList(*[Selection(col, col) for col in self.df.columns], id="column-selection")
144
+ yield Label("Where condition (optional)", id="where-label")
145
+ yield Input(placeholder="e.g., age > 30 and height < 180", id="where-input")
146
+ yield from super().compose()
147
+
148
+ def _handle_simple(self) -> None:
149
+ """Handle Yes button/Enter key press."""
150
+ selections = self.query_one(SelectionList).selected
151
+ columns = ", ".join(f"`{s}`" for s in selections) if selections else "*"
152
+ where = self.query_one(Input).value.strip()
153
+
154
+ return columns, where
155
+
156
+
157
+ class AdvancedSqlScreen(SqlScreen):
158
+ """Advanced SQL query screen."""
159
+
160
+ DEFAULT_CSS = SqlScreen.DEFAULT_CSS.replace("SqlScreen", "AdvancedSqlScreen")
161
+
162
+ CSS = """
163
+ AdvancedSqlScreen TextArea {
164
+ width: auto;
165
+ min-width: 60;
166
+ height: auto;
167
+ min-height: 10;
168
+ }
169
+
170
+ #button-container {
171
+ min-width: 60;
172
+ }
173
+ """
174
+
175
+ def __init__(self, dftable: "DataFrameTable") -> None:
176
+ """Initialize the simple SQL screen.
177
+
178
+ Sets up the modal screen with reference to the main DataFrameTable widget
179
+ and stores the DataFrame for display.
180
+
181
+ Args:
182
+ dftable: Reference to the parent DataFrameTable widget.
183
+
184
+ Returns:
185
+ None
186
+ """
187
+ super().__init__(dftable, on_yes_callback=self._handle_advanced)
188
+
189
+ def compose(self) -> ComposeResult:
190
+ """Compose the advanced SQL screen widget structure."""
191
+ with Container(id="sql-container") as container:
192
+ container.border_title = "Advanced SQL Query"
193
+ yield TextArea.code_editor(
194
+ placeholder="Enter SQL query (use `self` as the table name), e.g., \n\nSELECT * \nFROM self \nWHERE age > 30",
195
+ id="sql-textarea",
196
+ language="sql",
197
+ )
198
+ yield from super().compose()
199
+
200
+ def _handle_advanced(self) -> None:
201
+ """Handle Yes button/Enter key press."""
202
+ return self.query_one(TextArea).text.strip()
@@ -37,7 +37,7 @@ class TableScreen(ModalScreen):
37
37
  }
38
38
  """
39
39
 
40
- def __init__(self, dftable: DataFrameTable) -> None:
40
+ def __init__(self, dftable: "DataFrameTable") -> None:
41
41
  """Initialize the table screen.
42
42
 
43
43
  Sets up the base modal screen with reference to the main DataFrameTable widget
@@ -50,8 +50,8 @@ class TableScreen(ModalScreen):
50
50
  None
51
51
  """
52
52
  super().__init__()
53
- self.df: pl.DataFrame = dftable.df # Polars DataFrame
54
53
  self.dftable = dftable # DataFrameTable
54
+ self.df: pl.DataFrame = dftable.df # Polars DataFrame
55
55
  self.thousand_separator = False # Whether to use thousand separators in numbers
56
56
 
57
57
  def compose(self) -> ComposeResult:
@@ -225,7 +225,7 @@ class StatisticsScreen(TableScreen):
225
225
 
226
226
  CSS = TableScreen.DEFAULT_CSS.replace("TableScreen", "StatisticsScreen")
227
227
 
228
- def __init__(self, dftable: DataFrameTable, col_idx: int | None = None):
228
+ def __init__(self, dftable: "DataFrameTable", col_idx: int | None = None):
229
229
  super().__init__(dftable)
230
230
  self.col_idx = col_idx # None for dataframe statistics, otherwise column index
231
231
 
@@ -344,15 +344,16 @@ class FrequencyScreen(TableScreen):
344
344
 
345
345
  CSS = TableScreen.DEFAULT_CSS.replace("TableScreen", "FrequencyScreen")
346
346
 
347
- def __init__(self, col_idx: int, dftable: DataFrameTable):
347
+ def __init__(self, col_idx: int, dftable: "DataFrameTable") -> None:
348
348
  super().__init__(dftable)
349
349
  self.col_idx = col_idx
350
350
  self.sorted_columns = {
351
351
  1: True, # Count
352
352
  }
353
- self.df: pl.DataFrame = (
354
- dftable.df[dftable.df.columns[self.col_idx]].value_counts(sort=True).sort("count", descending=True)
355
- )
353
+
354
+ df = dftable.df.filter(dftable.visible_rows) if False in dftable.visible_rows else dftable.df
355
+ self.total_count = len(df)
356
+ self.df: pl.DataFrame = df[df.columns[self.col_idx]].value_counts(sort=True).sort("count", descending=True)
356
357
 
357
358
  def on_mount(self) -> None:
358
359
  """Create the frequency table."""
@@ -385,9 +386,6 @@ class FrequencyScreen(TableScreen):
385
386
  dtype = self.dftable.df.dtypes[self.col_idx]
386
387
  dc = DtypeConfig(dtype)
387
388
 
388
- # Calculate frequencies using Polars
389
- total_count = len(self.dftable.df)
390
-
391
389
  # Add column headers with sort indicators
392
390
  columns = [
393
391
  (column, "Value", 0),
@@ -415,7 +413,7 @@ class FrequencyScreen(TableScreen):
415
413
  # Add rows to the frequency table
416
414
  for row_idx, row in enumerate(self.df.rows()):
417
415
  column, count = row
418
- percentage = (count / total_count) * 100
416
+ percentage = (count / self.total_count) * 100
419
417
 
420
418
  if column is None:
421
419
  value = NULL_DISPLAY
@@ -446,7 +444,7 @@ class FrequencyScreen(TableScreen):
446
444
  # Add a total row
447
445
  self.table.add_row(
448
446
  Text("Total", style="bold", justify=dc.justify),
449
- Text(f"{total_count:,}", style="bold", justify="right"),
447
+ Text(f"{self.total_count:,}", style="bold", justify="right"),
450
448
  Text("100.00", style="bold", justify="right"),
451
449
  Bar(
452
450
  highlight_range=(0.0, 10),
@@ -460,12 +458,10 @@ class FrequencyScreen(TableScreen):
460
458
  row_idx, col_idx = self.table.cursor_coordinate
461
459
  col_sort = col_idx if col_idx == 0 else 1
462
460
 
463
- sort_dir = self.sorted_columns.get(col_sort)
464
- if sort_dir is not None:
461
+ if self.sorted_columns.get(col_sort) == descending:
465
462
  # If already sorted in the same direction, do nothing
466
- if sort_dir == descending:
467
- self.notify("Already sorted in that order", title="Sort", severity="warning")
468
- return
463
+ # self.notify("Already sorted in that order", title="Sort", severity="warning")
464
+ return
469
465
 
470
466
  self.sorted_columns.clear()
471
467
  self.sorted_columns[col_sort] = descending
@@ -33,9 +33,11 @@ class YesNoScreen(ModalScreen):
33
33
  min-width: 40;
34
34
  max-width: 60;
35
35
  height: auto;
36
- border: heavy $primary;
37
- border-title-color: $primary-lighten-3;
38
- background: $surface;
36
+ border: heavy $accent;
37
+ border-title-color: $accent;
38
+ border-title-background: $panel;
39
+ border-title-style: bold;
40
+ background: $background;
39
41
  padding: 1 2;
40
42
  }
41
43
 
@@ -241,6 +243,7 @@ class YesNoScreen(ModalScreen):
241
243
  yield self.no
242
244
 
243
245
  def on_button_pressed(self, event: Button.Pressed) -> None:
246
+ """Handle button press events in the Yes/No screen."""
244
247
  if event.button.id == "yes":
245
248
  self._handle_yes()
246
249
  elif event.button.id == "maybe":
@@ -249,6 +252,7 @@ class YesNoScreen(ModalScreen):
249
252
  self.dismiss(None)
250
253
 
251
254
  def on_key(self, event) -> None:
255
+ """Handle key press events in the table screen."""
252
256
  if event.key == "enter":
253
257
  self._handle_yes()
254
258
  event.stop()
@@ -609,7 +613,7 @@ class AddColumnScreen(YesNoScreen):
609
613
  return self.cidx, col_name, pl.lit(None)
610
614
  elif tentative_expr(term):
611
615
  try:
612
- expr = validate_expr(term, self.df, self.cidx)
616
+ expr = validate_expr(term, self.df.columns, self.cidx)
613
617
  return self.cidx, col_name, expr
614
618
  except ValueError as e:
615
619
  self.notify(f"Invalid expression [$error]{term}[/]: {str(e)}", title="Add Column", severity="error")
@@ -634,7 +638,7 @@ class FindReplaceScreen(YesNoScreen):
634
638
 
635
639
  CSS = YesNoScreen.DEFAULT_CSS.replace("YesNoScreen", "ReplaceScreen")
636
640
 
637
- def __init__(self, dftable: DataFrameTable):
641
+ def __init__(self, dftable: "DataFrameTable"):
638
642
  term_find = str(dftable.cursor_value)
639
643
  super().__init__(
640
644
  title="Find and Replace",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframe-textual
3
- Version: 1.2.0
3
+ Version: 1.3.9
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
@@ -29,7 +29,7 @@ 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: textual>=6.5.0
32
+ Requires-Dist: textual[syntax]>=6.5.0
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: textual-dev>=1.8.0; extra == 'dev'
35
35
  Provides-Extra: excel
@@ -161,7 +161,7 @@ When multiple files are opened:
161
161
  |-----|--------|
162
162
  | `Ctrl+O` | Open file in a new tab |
163
163
  | `Ctrl+W` | Close current tab |
164
- | `Ctrl+Shift+S` | Save all open tabs to Excel file |
164
+ | `Ctrl+A` | Save all open tabs to Excel file |
165
165
  | `>` or `b` | Move to next tab |
166
166
  | `<` | Move to previous tab |
167
167
  | `B` | Toggle tab bar visibility |
@@ -171,7 +171,7 @@ When multiple files are opened:
171
171
 
172
172
  | Key | Action |
173
173
  |-----|--------|
174
- | `Ctrl+H` | Toggle help panel |
174
+ | `F1` | Toggle help panel |
175
175
  | `k` | Cycle through themes |
176
176
 
177
177
  ---
@@ -213,7 +213,7 @@ When multiple files are opened:
213
213
  | `A` | Add column with name and value/expression |
214
214
  | `-` (minus) | Delete current column |
215
215
  | `_` (underscore) | Delete current column and all columns after |
216
- | `Ctrl+-` | Delete current column and all columns before |
216
+ | `Ctrl+_` | Delete current column and all columns before |
217
217
  | `x` | Delete current row |
218
218
  | `X` | Delete current row and all rows below |
219
219
  | `Ctrl+X` | Delete current row and all rows above |
@@ -241,6 +241,13 @@ When multiple files are opened:
241
241
  | `v` | View only rows by selected rows and/or matches or cursor value |
242
242
  | `V` | View only rows by expression |
243
243
 
244
+ #### SQL Interface
245
+
246
+ | Key | Action |
247
+ |-----|--------|
248
+ | `l` | Simple SQL interface (select columns & WHERE clause) |
249
+ | `L` | Advanced SQL interface (full SQL queries) |
250
+
244
251
  #### Find & Replace
245
252
 
246
253
  | Key | Action |
@@ -703,7 +710,40 @@ Press `@` to make URLs in the current column clickable:
703
710
  - **Scans** all cells in the current column for URLs starting with `http://` or `https://`
704
711
  - **Applies** link styling to make them clickable and dataframe remains unchanged
705
712
 
706
- ### 19. Clipboard Operations
713
+ ### 19. SQL Interface
714
+
715
+ The SQL interface provides two modes for querying your dataframe:
716
+
717
+ #### Simple SQL Interface (`l`)
718
+ Select specific columns and apply WHERE conditions without writing full SQL:
719
+ - Choose which columns to include in results
720
+ - Specify WHERE clause for filtering
721
+ - Ideal for quick filtering and column selection
722
+
723
+ #### Advanced SQL Interface (`L`)
724
+ Execute complete SQL queries for advanced data manipulation:
725
+ - Write full SQL queries with standard [SQL syntax](https://docs.pola.rs/api/python/stable/reference/sql/index.html)
726
+ - Support for JOINs, GROUP BY, aggregations, and more
727
+ - Access to all SQL capabilities for complex transformations
728
+ - Always use `self` as the table name
729
+
730
+ **Examples:**
731
+ ```sql
732
+ -- Filter and select specific rows and/or columns
733
+ SELECT name, age FROM self WHERE age > 30
734
+
735
+ -- Aggregate with GROUP BY
736
+ SELECT department, COUNT(*) as count, AVG(salary) as avg_salary
737
+ FROM self
738
+ GROUP BY department
739
+
740
+ -- Complex filtering with multiple conditions
741
+ SELECT *
742
+ FROM self
743
+ WHERE (age > 25 AND salary > 50000) OR department = 'Management'
744
+ ```
745
+
746
+ ### 20. Clipboard Operations
707
747
 
708
748
  Copies value to system clipboard with `pbcopy` on macOS and `xclip` on Linux
709
749
 
@@ -0,0 +1,14 @@
1
+ dataframe_textual/__init__.py,sha256=IFPb8RMUgghw0eRomehkkC684Iny_gs1VkiZMQ5ZpFk,813
2
+ dataframe_textual/__main__.py,sha256=hgjKLT3ggGxVVeRBunkArTy7PTqOHVJUf3fsH4P5dfU,2146
3
+ dataframe_textual/common.py,sha256=YeXikgy7Mvgfs4vvoloG_8i2rBBnAsHDPnjaYQfIHUM,16748
4
+ dataframe_textual/data_frame_help_panel.py,sha256=iEKaur-aH1N_oqHu-vMwEEjfkjQiThK24UO5izsOiW0,3416
5
+ dataframe_textual/data_frame_table.py,sha256=FIxhKvVEzm4wrejFu7iX_zoZFDAxO0xs0o80aeF0Km0,114086
6
+ dataframe_textual/data_frame_viewer.py,sha256=CFZv4UdD2JU_uTmXccVznb_K7NmxmFMsDkYoZBZlkc0,12716
7
+ dataframe_textual/sql_screen.py,sha256=F-4C4AwjdyiXzLl6fheGrkVMfk-Yvz5EXaXPFfZq5xs,6165
8
+ dataframe_textual/table_screen.py,sha256=o5gLWiF01oa2beTjCCqTG87qMs7XDZgkvqjfsWRU9K4,17755
9
+ dataframe_textual/yes_no_screen.py,sha256=gI4yj1T5rek10A9Va11KsuO_1XwVgvVPOUDrcknPrQM,23127
10
+ dataframe_textual-1.3.9.dist-info/METADATA,sha256=Q1a1gyNiWwMftSIPkw3XUWoVigM0KSfIpdIra0j7Wec,27924
11
+ dataframe_textual-1.3.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ dataframe_textual-1.3.9.dist-info/entry_points.txt,sha256=R_GoooOxcq6ab4RaHiVoZ4zrZJ-phMcGmlL2rwqncW8,107
13
+ dataframe_textual-1.3.9.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
14
+ dataframe_textual-1.3.9.dist-info/RECORD,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
+ dataframe-textual = dataframe_textual.__main__:main
2
3
  dv = dataframe_textual.__main__:main
@@ -1,13 +0,0 @@
1
- dataframe_textual/__init__.py,sha256=IFPb8RMUgghw0eRomehkkC684Iny_gs1VkiZMQ5ZpFk,813
2
- dataframe_textual/__main__.py,sha256=hgjKLT3ggGxVVeRBunkArTy7PTqOHVJUf3fsH4P5dfU,2146
3
- dataframe_textual/common.py,sha256=q4bXS7oiJAsdcMEfHkPm5-e8SlfcwCBNhFN9TinQqV0,16171
4
- dataframe_textual/data_frame_help_panel.py,sha256=XgKGEPJr2hnDWpZ5mavLRcBSPa9cvrXdzVUGFQavXm4,3353
5
- dataframe_textual/data_frame_table.py,sha256=UbUlPwrcLbN_yNz9ECMgKHypTgVV5qDLvSQUJsDby8Q,109452
6
- dataframe_textual/data_frame_viewer.py,sha256=4mV3k7MNTf9TKBmGJ8fDx7itA1vo4qSmaWpvZozwfjs,12987
7
- dataframe_textual/table_screen.py,sha256=KCmvKAdHexIFQKGob6WMPrcAITHO6CMVpRJzzs0pbrE,17793
8
- dataframe_textual/yes_no_screen.py,sha256=vyUKMBbbwgt5At1U430eLg3WbJvqUNoz2GpvdnMd7q0,22921
9
- dataframe_textual-1.2.0.dist-info/METADATA,sha256=zLQtFr7h6-7fZ5dJoNYjGX5cghyKpGDKX0c7-wSbUy0,26688
10
- dataframe_textual-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- dataframe_textual-1.2.0.dist-info/entry_points.txt,sha256=Z0XKp1ACvmbJymekkxt-C81I0OoInksr5Ib0w2OT_a4,55
12
- dataframe_textual-1.2.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
13
- dataframe_textual-1.2.0.dist-info/RECORD,,