dataframe-textual 0.3.0__tar.gz → 0.3.2__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-0.3.0 → dataframe_textual-0.3.2}/PKG-INFO +1 -1
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/pyproject.toml +1 -1
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/data_frame_table.py +9 -11
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/data_frame_viewer.py +18 -3
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/table_screen.py +73 -77
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/yes_no_screen.py +12 -14
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/uv.lock +1 -1
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/.gitignore +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/.python-version +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/LICENSE +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/README.md +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/main.py +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/screenshot.png +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/__init__.py +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/__main__.py +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/common.py +0 -0
- {dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/data_frame_help_panel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframe-textual
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Interactive CSV/Excel viewer for the terminal (Textual TUI)
|
|
5
5
|
Project-URL: Homepage, https://github.com/need47/dataframe-textual
|
|
6
6
|
Project-URL: Repository, https://github.com/need47/dataframe-textual.git
|
{dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/data_frame_table.py
RENAMED
|
@@ -549,7 +549,7 @@ class DataFrameTable(DataTable):
|
|
|
549
549
|
return
|
|
550
550
|
|
|
551
551
|
# Push the modal screen
|
|
552
|
-
self.app.push_screen(RowDetailScreen(row_idx, self
|
|
552
|
+
self.app.push_screen(RowDetailScreen(row_idx, self))
|
|
553
553
|
|
|
554
554
|
def _show_frequency(self) -> None:
|
|
555
555
|
"""Show frequency distribution for the current column."""
|
|
@@ -558,9 +558,7 @@ class DataFrameTable(DataTable):
|
|
|
558
558
|
return
|
|
559
559
|
|
|
560
560
|
# Push the frequency modal screen
|
|
561
|
-
self.app.push_screen(
|
|
562
|
-
FrequencyScreen(col_idx, self.df.filter(self.visible_rows))
|
|
563
|
-
)
|
|
561
|
+
self.app.push_screen(FrequencyScreen(col_idx, self))
|
|
564
562
|
|
|
565
563
|
def _open_freeze_screen(self) -> None:
|
|
566
564
|
"""Open the freeze screen to set fixed rows and columns."""
|
|
@@ -579,7 +577,7 @@ class DataFrameTable(DataTable):
|
|
|
579
577
|
|
|
580
578
|
# Add to history
|
|
581
579
|
self._add_history(
|
|
582
|
-
f"Pinned [
|
|
580
|
+
f"Pinned [$accent]{fixed_rows}[/] rows and [$accent]{fixed_columns}[/] columns"
|
|
583
581
|
)
|
|
584
582
|
|
|
585
583
|
# Apply the pin settings to the table
|
|
@@ -589,7 +587,7 @@ class DataFrameTable(DataTable):
|
|
|
589
587
|
self.fixed_columns = fixed_columns
|
|
590
588
|
|
|
591
589
|
self.app.notify(
|
|
592
|
-
f"Pinned [
|
|
590
|
+
f"Pinned [$accent]{fixed_rows}[/] rows and [$accent]{fixed_columns}[/] columns",
|
|
593
591
|
title="Pin",
|
|
594
592
|
)
|
|
595
593
|
|
|
@@ -1109,7 +1107,7 @@ class DataFrameTable(DataTable):
|
|
|
1109
1107
|
self.update_cell(row_key, col_key, cell_text)
|
|
1110
1108
|
|
|
1111
1109
|
self.app.notify(
|
|
1112
|
-
f"Found [
|
|
1110
|
+
f"Found [$accent]{match_count}[/] matches for [on $primary]{term}[/] across all columns",
|
|
1113
1111
|
title="Global Search",
|
|
1114
1112
|
)
|
|
1115
1113
|
|
|
@@ -1143,7 +1141,7 @@ class DataFrameTable(DataTable):
|
|
|
1143
1141
|
# Check if we're highlighting or un-highlighting
|
|
1144
1142
|
if new_selected_count := self.selected_rows.count(True):
|
|
1145
1143
|
self.app.notify(
|
|
1146
|
-
f"Toggled selection - now showing [
|
|
1144
|
+
f"Toggled selection - now showing [$accent]{new_selected_count}[/] rows",
|
|
1147
1145
|
title="Toggle",
|
|
1148
1146
|
)
|
|
1149
1147
|
|
|
@@ -1167,7 +1165,7 @@ class DataFrameTable(DataTable):
|
|
|
1167
1165
|
self._highlight_rows(clear=True)
|
|
1168
1166
|
|
|
1169
1167
|
self.app.notify(
|
|
1170
|
-
f"Cleared [
|
|
1168
|
+
f"Cleared [$accent]{selected_count}[/] selected rows", title="Clear"
|
|
1171
1169
|
)
|
|
1172
1170
|
|
|
1173
1171
|
def _filter_selected_rows(self) -> None:
|
|
@@ -1190,7 +1188,7 @@ class DataFrameTable(DataTable):
|
|
|
1190
1188
|
self._setup_table()
|
|
1191
1189
|
|
|
1192
1190
|
self.app.notify(
|
|
1193
|
-
f"Removed unselected rows. Now showing [
|
|
1191
|
+
f"Removed unselected rows. Now showing [$accent]{selected_count}[/] rows",
|
|
1194
1192
|
title="Filter",
|
|
1195
1193
|
)
|
|
1196
1194
|
|
|
@@ -1255,7 +1253,7 @@ class DataFrameTable(DataTable):
|
|
|
1255
1253
|
self._setup_table()
|
|
1256
1254
|
|
|
1257
1255
|
self.app.notify(
|
|
1258
|
-
f"Filtered to [
|
|
1256
|
+
f"Filtered to [$accent]{matched_count}[/] matching rows",
|
|
1259
1257
|
title="Filter",
|
|
1260
1258
|
)
|
|
1261
1259
|
|
{dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/data_frame_viewer.py
RENAMED
|
@@ -117,7 +117,9 @@ class DataFrameViewer(App):
|
|
|
117
117
|
def on_key(self, event):
|
|
118
118
|
if event.key == "k":
|
|
119
119
|
self.theme = _next(list(BUILTIN_THEMES.keys()), self.theme)
|
|
120
|
-
self.notify(
|
|
120
|
+
self.notify(
|
|
121
|
+
f"Switched to theme: [on $primary]{self.theme}[/]", title="Theme"
|
|
122
|
+
)
|
|
121
123
|
|
|
122
124
|
def on_tabbed_content_tab_activated(
|
|
123
125
|
self, event: TabbedContent.TabActivated
|
|
@@ -202,12 +204,24 @@ class DataFrameViewer(App):
|
|
|
202
204
|
|
|
203
205
|
def _add_tab(self, df: pl.DataFrame, filename: str) -> None:
|
|
204
206
|
"""Add new table tab. If single file, replace table; if multiple, add tab."""
|
|
205
|
-
table = DataFrameTable(df, filename, zebra_stripes=True)
|
|
206
207
|
tabname = Path(filename).stem
|
|
207
208
|
if any(tab.name == tabname for tab in self.tabs):
|
|
208
209
|
tabname = f"{tabname}_{len(self.tabs) + 1}"
|
|
209
210
|
|
|
210
|
-
|
|
211
|
+
# Find an available tab index
|
|
212
|
+
tab_idx = f"tab_{len(self.tabs) + 1}"
|
|
213
|
+
for idx in range(len(self.tabs)):
|
|
214
|
+
pending_tab_idx = f"tab_{idx + 1}"
|
|
215
|
+
if any(tab.id == pending_tab_idx for tab in self.tabs):
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
tab_idx = pending_tab_idx
|
|
219
|
+
break
|
|
220
|
+
|
|
221
|
+
table = DataFrameTable(
|
|
222
|
+
df, filename, zebra_stripes=True, id=tab_idx, name=tabname
|
|
223
|
+
)
|
|
224
|
+
tab = TabPane(tabname, table, name=tabname, id=tab_idx)
|
|
211
225
|
self.tabbed.add_pane(tab)
|
|
212
226
|
self.tabs[tab] = table
|
|
213
227
|
|
|
@@ -226,6 +240,7 @@ class DataFrameViewer(App):
|
|
|
226
240
|
else:
|
|
227
241
|
if active_pane := self.tabbed.active_pane:
|
|
228
242
|
self.tabbed.remove_pane(active_pane.id)
|
|
243
|
+
self.tabs.pop(active_pane)
|
|
229
244
|
self.notify(
|
|
230
245
|
f"Closed tab [on $primary]{active_pane.name}[/]", title="Close"
|
|
231
246
|
)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"""Modal screens for displaying data in tables (row details and frequency)."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .data_frame_table import DataFrameTable
|
|
4
7
|
|
|
5
8
|
import polars as pl
|
|
6
9
|
from rich.text import Text
|
|
@@ -10,7 +13,7 @@ from textual.renderables.bar import Bar
|
|
|
10
13
|
from textual.screen import ModalScreen
|
|
11
14
|
from textual.widgets import DataTable
|
|
12
15
|
|
|
13
|
-
from .common import
|
|
16
|
+
from .common import DtypeConfig, _format_row
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class TableScreen(ModalScreen):
|
|
@@ -33,14 +36,14 @@ class TableScreen(ModalScreen):
|
|
|
33
36
|
}
|
|
34
37
|
"""
|
|
35
38
|
|
|
36
|
-
def __init__(self,
|
|
39
|
+
def __init__(self, dftable: DataFrameTable):
|
|
37
40
|
super().__init__()
|
|
38
|
-
self.df = df
|
|
39
|
-
self.
|
|
41
|
+
self.df: pl.DataFrame = dftable.df # Polars DataFrame
|
|
42
|
+
self.dftable = dftable # DataFrameTable
|
|
40
43
|
|
|
41
44
|
def compose(self) -> ComposeResult:
|
|
42
45
|
"""Create the table. Must be overridden by subclasses."""
|
|
43
|
-
self.table = DataTable(zebra_stripes=True
|
|
46
|
+
self.table = DataTable(zebra_stripes=True)
|
|
44
47
|
yield self.table
|
|
45
48
|
|
|
46
49
|
def on_key(self, event):
|
|
@@ -87,27 +90,26 @@ class TableScreen(ModalScreen):
|
|
|
87
90
|
expr = pl.col(col_name) == col_value
|
|
88
91
|
value_display = f"[on $primary]{col_value}[/]"
|
|
89
92
|
|
|
90
|
-
app = self.app
|
|
91
93
|
matched_indices = set(
|
|
92
|
-
|
|
94
|
+
self.dftable.df.with_row_index("__rid__").filter(expr)["__rid__"].to_list()
|
|
93
95
|
)
|
|
94
96
|
|
|
95
97
|
# Apply the action
|
|
96
98
|
if action == "filter":
|
|
97
99
|
# Update visible_rows to reflect the filter
|
|
98
|
-
for i in range(len(
|
|
99
|
-
|
|
100
|
+
for i in range(len(self.dftable.visible_rows)):
|
|
101
|
+
self.dftable.visible_rows[i] = i in matched_indices
|
|
100
102
|
title = "Filter"
|
|
101
103
|
message = f"Filtered by [on $primary]{col_name}[/] = {value_display}"
|
|
102
104
|
else: # action == "highlight"
|
|
103
105
|
# Update selected_rows to reflect the highlights
|
|
104
|
-
for i in range(len(
|
|
105
|
-
|
|
106
|
+
for i in range(len(self.dftable.selected_rows)):
|
|
107
|
+
self.dftable.selected_rows[i] = i in matched_indices
|
|
106
108
|
title = "Highlight"
|
|
107
109
|
message = f"Highlighted [on $primary]{col_name}[/] = {value_display}"
|
|
108
110
|
|
|
109
111
|
# Recreate the table display with updated data in the main app
|
|
110
|
-
|
|
112
|
+
self.dftable._setup_table()
|
|
111
113
|
|
|
112
114
|
# Dismiss the frequency screen
|
|
113
115
|
self.app.pop_screen()
|
|
@@ -120,8 +122,8 @@ class RowDetailScreen(TableScreen):
|
|
|
120
122
|
|
|
121
123
|
CSS = TableScreen.DEFAULT_CSS.replace("TableScreen", "RowDetailScreen")
|
|
122
124
|
|
|
123
|
-
def __init__(self, row_idx: int,
|
|
124
|
-
super().__init__(
|
|
125
|
+
def __init__(self, row_idx: int, dftable):
|
|
126
|
+
super().__init__(dftable)
|
|
125
127
|
self.row_idx = row_idx
|
|
126
128
|
|
|
127
129
|
def on_mount(self) -> None:
|
|
@@ -169,25 +171,54 @@ class FrequencyScreen(TableScreen):
|
|
|
169
171
|
|
|
170
172
|
CSS = TableScreen.DEFAULT_CSS.replace("TableScreen", "FrequencyScreen")
|
|
171
173
|
|
|
172
|
-
def __init__(self, col_idx: int,
|
|
173
|
-
super().__init__(
|
|
174
|
+
def __init__(self, col_idx: int, dftable: DataFrameTable):
|
|
175
|
+
super().__init__(dftable)
|
|
174
176
|
self.col_idx = col_idx
|
|
175
177
|
self.sorted_columns = {
|
|
176
178
|
1: True, # Count
|
|
177
179
|
2: True, # %
|
|
178
180
|
}
|
|
181
|
+
self.df: pl.DataFrame = (
|
|
182
|
+
dftable.df[dftable.df.columns[self.col_idx]]
|
|
183
|
+
.value_counts(sort=True)
|
|
184
|
+
.sort("count", descending=True)
|
|
185
|
+
)
|
|
179
186
|
|
|
180
187
|
def on_mount(self) -> None:
|
|
181
188
|
"""Create the frequency table."""
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
self.build_table()
|
|
190
|
+
|
|
191
|
+
def on_key(self, event):
|
|
192
|
+
if event.key == "left_square_bracket": # '['
|
|
193
|
+
# Sort by current column in ascending order
|
|
194
|
+
self._sort_by_column(descending=False)
|
|
195
|
+
event.stop()
|
|
196
|
+
elif event.key == "right_square_bracket": # ']'
|
|
197
|
+
# Sort by current column in descending order
|
|
198
|
+
self._sort_by_column(descending=True)
|
|
199
|
+
event.stop()
|
|
200
|
+
elif event.key == "v":
|
|
201
|
+
# Filter the main table by the selected value
|
|
202
|
+
self._filter_or_highlight_selected_value(
|
|
203
|
+
self._get_col_name_value(), action="filter"
|
|
204
|
+
)
|
|
205
|
+
event.stop()
|
|
206
|
+
elif event.key == "quotation_mark": # '"'
|
|
207
|
+
# Highlight the main table by the selected value
|
|
208
|
+
self._filter_or_highlight_selected_value(
|
|
209
|
+
self._get_col_name_value(), action="highlight"
|
|
210
|
+
)
|
|
211
|
+
event.stop()
|
|
212
|
+
|
|
213
|
+
def build_table(self) -> None:
|
|
214
|
+
# Create frequency table
|
|
215
|
+
column = self.dftable.df.columns[self.col_idx]
|
|
216
|
+
dtype = str(self.dftable.df.dtypes[self.col_idx])
|
|
184
217
|
dc = DtypeConfig(dtype)
|
|
185
218
|
|
|
186
219
|
# Calculate frequencies using Polars
|
|
187
|
-
|
|
188
|
-
total_count = len(self.df)
|
|
220
|
+
total_count = len(self.dftable.df)
|
|
189
221
|
|
|
190
|
-
# Create frequency table
|
|
191
222
|
self.table.add_column(Text(column, justify=dc.justify), key=column)
|
|
192
223
|
self.table.add_column(Text("Count", justify="right"), key="Count")
|
|
193
224
|
self.table.add_column(Text("%", justify="right"), key="%")
|
|
@@ -198,7 +229,7 @@ class FrequencyScreen(TableScreen):
|
|
|
198
229
|
ds_float = DtypeConfig("Float64")
|
|
199
230
|
|
|
200
231
|
# Add rows to the frequency table
|
|
201
|
-
for row_idx, row in enumerate(
|
|
232
|
+
for row_idx, row in enumerate(self.df.rows()):
|
|
202
233
|
value, count = row
|
|
203
234
|
percentage = (count / total_count) * 100
|
|
204
235
|
|
|
@@ -226,39 +257,22 @@ class FrequencyScreen(TableScreen):
|
|
|
226
257
|
Text("Total", style="bold", justify=dc.justify),
|
|
227
258
|
Text(f"{total_count:,}", style="bold", justify="right"),
|
|
228
259
|
Text("100.00", style="bold", justify="right"),
|
|
260
|
+
Bar(
|
|
261
|
+
highlight_range=(0.0, 10),
|
|
262
|
+
width=10,
|
|
263
|
+
),
|
|
229
264
|
key="total",
|
|
230
265
|
)
|
|
231
266
|
|
|
232
|
-
def on_key(self, event):
|
|
233
|
-
if event.key == "left_square_bracket": # '['
|
|
234
|
-
# Sort by current column in ascending order
|
|
235
|
-
self._sort_by_column(descending=False)
|
|
236
|
-
event.stop()
|
|
237
|
-
elif event.key == "right_square_bracket": # ']'
|
|
238
|
-
# Sort by current column in descending order
|
|
239
|
-
self._sort_by_column(descending=True)
|
|
240
|
-
event.stop()
|
|
241
|
-
elif event.key == "v":
|
|
242
|
-
# Filter the main table by the selected value
|
|
243
|
-
self._filter_or_highlight_selected_value(
|
|
244
|
-
self._get_col_name_value(), action="filter"
|
|
245
|
-
)
|
|
246
|
-
event.stop()
|
|
247
|
-
elif event.key == "quotation_mark": # '"'
|
|
248
|
-
# Highlight the main table by the selected value
|
|
249
|
-
self._filter_or_highlight_selected_value(
|
|
250
|
-
self._get_col_name_value(), action="highlight"
|
|
251
|
-
)
|
|
252
|
-
event.stop()
|
|
253
|
-
|
|
254
267
|
def _sort_by_column(self, descending: bool) -> None:
|
|
255
268
|
"""Sort the dataframe by the selected column and refresh the main table."""
|
|
256
|
-
freq_table = self.query_one(DataTable)
|
|
257
269
|
|
|
258
|
-
|
|
259
|
-
|
|
270
|
+
self.log(self.df)
|
|
271
|
+
|
|
272
|
+
row_idx, col_idx = self.table.cursor_coordinate
|
|
273
|
+
col_sort = col_idx if col_idx == 0 else 1
|
|
260
274
|
|
|
261
|
-
sort_dir = self.sorted_columns.get(
|
|
275
|
+
sort_dir = self.sorted_columns.get(col_sort)
|
|
262
276
|
if sort_dir is not None:
|
|
263
277
|
# If already sorted in the same direction, do nothing
|
|
264
278
|
if sort_dir == descending:
|
|
@@ -268,34 +282,16 @@ class FrequencyScreen(TableScreen):
|
|
|
268
282
|
return
|
|
269
283
|
|
|
270
284
|
self.sorted_columns.clear()
|
|
271
|
-
self.sorted_columns[
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
col_dtype = "Float64"
|
|
282
|
-
|
|
283
|
-
def key_fun(freq_col):
|
|
284
|
-
col_value = freq_col.plain
|
|
285
|
-
|
|
286
|
-
if col_dtype == "Int64":
|
|
287
|
-
return int(col_value)
|
|
288
|
-
elif col_dtype == "Float64":
|
|
289
|
-
return float(col_value)
|
|
290
|
-
elif col_dtype == "Boolean":
|
|
291
|
-
return BOOLS[col_value]
|
|
292
|
-
else:
|
|
293
|
-
return col_value
|
|
294
|
-
|
|
295
|
-
# Sort the table
|
|
296
|
-
freq_table.sort(
|
|
297
|
-
col_name, key=lambda freq_col: key_fun(freq_col), reverse=descending
|
|
298
|
-
)
|
|
285
|
+
self.sorted_columns[col_sort] = descending
|
|
286
|
+
|
|
287
|
+
col_name = self.df.columns[col_sort]
|
|
288
|
+
self.df = self.df.sort(col_name, descending=descending)
|
|
289
|
+
|
|
290
|
+
# Rebuild the frequency table
|
|
291
|
+
self.table.clear(columns=True)
|
|
292
|
+
self.build_table()
|
|
293
|
+
|
|
294
|
+
self.table.move_cursor(row=row_idx, column=col_idx)
|
|
299
295
|
|
|
300
296
|
# Notify the user
|
|
301
297
|
order = "desc" if descending else "asc"
|
|
@@ -65,7 +65,7 @@ class YesNoScreen(ModalScreen):
|
|
|
65
65
|
input: Optional input value to pre-fill an Input widget. If None, no Input is shown. If it is a 2-value tuple, the first value is the pre-filled input, and the second value is the type of input (e.g., "integer", "number", "text")
|
|
66
66
|
yes: Text for the Yes button. If None, hides the Yes button
|
|
67
67
|
no: Text for the No button. If None, hides the No button
|
|
68
|
-
on_yes_callback: Optional callable that takes no args and returns the value to dismiss with
|
|
68
|
+
on_yes_callback: Optional callable that takes no args and returns the value to dismiss with when Yes is pressed
|
|
69
69
|
"""
|
|
70
70
|
super().__init__()
|
|
71
71
|
self.title = title
|
|
@@ -179,7 +179,7 @@ class ConfirmScreen(YesNoScreen):
|
|
|
179
179
|
)
|
|
180
180
|
|
|
181
181
|
def handle_confirm(self) -> None:
|
|
182
|
-
|
|
182
|
+
return True
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
class EditCellScreen(YesNoScreen):
|
|
@@ -218,20 +218,18 @@ class EditCellScreen(YesNoScreen):
|
|
|
218
218
|
|
|
219
219
|
# Check if value changed
|
|
220
220
|
if new_value_str == self.input_value:
|
|
221
|
-
self.dismiss(None)
|
|
222
221
|
self.notify("No changes made", title="Edit", severity="warning")
|
|
223
|
-
return
|
|
222
|
+
return None
|
|
224
223
|
|
|
225
224
|
# Parse and validate based on column dtype
|
|
226
225
|
try:
|
|
227
226
|
new_value = DtypeConfig(self.col_dtype).convert(new_value_str)
|
|
228
227
|
except Exception as e:
|
|
229
|
-
self.dismiss(None) # Dismiss without changes
|
|
230
228
|
self.notify(f"Invalid value: {str(e)}", title="Edit", severity="error")
|
|
231
|
-
return
|
|
229
|
+
return None
|
|
232
230
|
|
|
233
|
-
#
|
|
234
|
-
self.
|
|
231
|
+
# New value
|
|
232
|
+
return self.row_key, self.col_idx, new_value
|
|
235
233
|
|
|
236
234
|
|
|
237
235
|
class SearchScreen(YesNoScreen):
|
|
@@ -265,8 +263,8 @@ class SearchScreen(YesNoScreen):
|
|
|
265
263
|
self.notify("Search term cannot be empty", title="Search", severity="error")
|
|
266
264
|
return
|
|
267
265
|
|
|
268
|
-
#
|
|
269
|
-
|
|
266
|
+
# Search term
|
|
267
|
+
return term, self.col_dtype, self.col_name
|
|
270
268
|
|
|
271
269
|
|
|
272
270
|
class FilterScreen(YesNoScreen):
|
|
@@ -303,20 +301,20 @@ class FilterScreen(YesNoScreen):
|
|
|
303
301
|
# Test the expression by evaluating it
|
|
304
302
|
expr = eval(expr_str, {"pl": pl})
|
|
305
303
|
|
|
306
|
-
#
|
|
307
|
-
|
|
304
|
+
# Expression is valid
|
|
305
|
+
return expr, expr_str
|
|
308
306
|
except Exception as e:
|
|
309
307
|
self.notify(
|
|
310
308
|
f"Error evaluating expression: {str(e)}",
|
|
311
309
|
title="Filter",
|
|
312
310
|
severity="error",
|
|
313
311
|
)
|
|
314
|
-
self.dismiss(None)
|
|
315
312
|
except ValueError as ve:
|
|
316
313
|
self.notify(
|
|
317
314
|
f"Invalid expression: {str(ve)}", title="Filter", severity="error"
|
|
318
315
|
)
|
|
319
|
-
|
|
316
|
+
|
|
317
|
+
return None
|
|
320
318
|
|
|
321
319
|
|
|
322
320
|
class FreezeScreen(YesNoScreen):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dataframe_textual-0.3.0 → dataframe_textual-0.3.2}/src/dataframe_textual/data_frame_help_panel.py
RENAMED
|
File without changes
|