Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 84 additions & 8 deletions pysimplesql/pysimplesql.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ def __init__(
self.column_info: ColumnInfo # ColumnInfo collection
self.rows: Union[pd.DataFrame, None] = None
self.search_order: List[str] = []
self._search_string: tk.StringVar = tk.StringVar()
self.selector: List[str] = []
self.callbacks: CallbacksDict = {}
self.transform: Optional[Callable[[pd.DataFrame, int], None]] = None
Expand Down Expand Up @@ -655,6 +656,14 @@ def __setitem__(self, column, value: Union[str, int]) -> None:
"""
self.set_current(column, value)

@property
def search_string(self):
return self._search_string.get()

@search_string.setter
def search_string(self, val: str):
self._search_string.set(val)

# Make current_index a property so that bounds can be respected
@property
def current_index(self):
Expand Down Expand Up @@ -1168,6 +1177,9 @@ def requery(
lambda x: x.rstrip() if isinstance(x, str) else x
)

# reset search string
self.search_string = ""

if select_first:
self.first(
update_elements=update_elements,
Expand Down Expand Up @@ -1572,7 +1584,7 @@ def set_by_pk(
idx = [
i for i, value in enumerate(self.rows[self.pk_column]) if value == pk
]
except IndexError:
except (IndexError, KeyError):
idx = None
logger.debug("Error finding pk!")

Expand Down Expand Up @@ -1677,10 +1689,14 @@ def get_current_row(self) -> Union[pd.Series, None]:
if not self.rows.empty:
# force the current_index to be in bounds!
# For child reparenting
self.current_index = self.current_index
idx = (
self.current_index
if self.current_index < len(self.rows)
else len(self.rows)
)

# make sure to return as python type
return self.rows.astype("O").iloc[self.current_index]
return self.rows.astype("O").iloc[idx]
return None

def add_selector(
Expand Down Expand Up @@ -1770,6 +1786,9 @@ def insert_record(
# Insert the new values using DataSet.insert_row(),
# marking the new row as virtual
self.insert_row(new_values)

# reset search string
self.search_string = ""

# and move to the new record
# do this in insert_record, because possibly current_index is already 0
Expand Down Expand Up @@ -1930,6 +1949,9 @@ def save_record(
for col, value in changed_row_dict.items()
if self.column_info[col] and not self.column_info[col]["generated"]
}

# reset search string
self.search_string = ""

# Save or Insert the record as needed
if keyed_queries is not None:
Expand Down Expand Up @@ -2448,6 +2470,16 @@ def table_values(
# we only want transform col once
break

if self.search_string not in ["", None]:
# Generate the mask dynamically
masks = [
rows[col].astype(str).str.contains(self.search_string, case=False)
for col in self.search_order
]
mask_pd = pd.concat(masks, axis=1).any(axis=1)
# Apply the mask to filter the DataFrame
rows = rows[mask_pd]

# transform bool
if themepack.display_boolean_as_checkbox:
bool_columns = [
Expand Down Expand Up @@ -3608,6 +3640,7 @@ def auto_map_events(self, win: sg.Window) -> None:
placeholder=lang.search_placeholder,
color=themepack.placeholder_color,
)
self.window[search_box].bind_dataset(self[data_key])
# elif event_type==EVENT_SEARCH_DB:
elif event_type == EVENT_QUICK_EDIT:
referring_table = table
Expand Down Expand Up @@ -4222,8 +4255,11 @@ def update_selectors(
found = False
if len(values):
# set to index by pk
index = [[v.pk for v in values].index(pk)]
found = True
try:
index = [[v.pk for v in values].index(pk)]
found = True
except ValueError:
index = []
else: # if empty
index = []

Expand Down Expand Up @@ -5240,7 +5276,7 @@ def SelectedRows(self):
This property assumes that the LazyTable is using a primary key (pk) value
to uniquely identify rows in the data list.
"""
if self.data:
if self.data and self.widget.selection():
index = [
[v.pk for v in self.data].index(
[int(x) for x in self.widget.selection()][0]
Expand Down Expand Up @@ -5470,6 +5506,46 @@ def on_focusout(event):
self.binds["<FocusOut>"] = widget.bind("<FocusOut>", on_focusout, "+")


class _SearchInput(_EnhancedInput):
def __init__(self, *args, **kwargs):
self.dataset = None
self.search_string = None # Track the StringVar
super().__init__(*args, **kwargs)

def _add_binds(self):
super()._add_binds() # Call the parent method to maintain existing binds

def on_key_release(event):
# update selectors after each key-release
non_keys = self._non_keys.copy()
non_keys.remove("BackSpace")
non_keys.remove("Delete")
if event.keysym not in non_keys:
self.search_string.set(self.get())
self.dataset.frm.update_selectors(self.dataset.key)

self.binds["<KeyRelease>"] = self.widget.bind(
"<KeyRelease>", on_key_release, "+"
)

def bind_dataset(self, dataset):
self.dataset = dataset
self.search_string = dataset._search_string
self.search_string.trace_add("write", self._on_search_string_change)

def _on_search_string_change(self, *args):
if (
not self.active_placeholder
and self.get() != self.search_string.get()
and self.search_string.get() == "" # noqa PLC1901
):
# reinsert placeholder if DataSet.search_string == ""
self.widget.delete(0, "end")
self.widget.insert(0, self.placeholder_text)
self.widget.config(fg=self.placeholder_color, font=self.placeholder_font)
self.active_placeholder = True


def _autocomplete_combo(widget, completion_list, delta=0):
"""Perform autocompletion on a Combobox widget based on the current input."""
if delta:
Expand Down Expand Up @@ -6370,7 +6446,7 @@ def actions(
}
if type(themepack.search) is bytes:
layout += [
_EnhancedInput(
_SearchInput(
"", key=keygen.get(f"{key}search_input"), size=search_size
),
sg.B(
Expand All @@ -6387,7 +6463,7 @@ def actions(
]
else:
layout += [
_EnhancedInput(
_SearchInput(
"", key=keygen.get(f"{key}search_input"), size=search_size
),
sg.B(
Expand Down