This project extends our Tkinter DataFrame search tutorial. After filtering a Pandas DataFrame and displaying the matching records in Treeview, the user can select one result, display its values and delete that exact row from the working DataFrame.

The application keeps a connection between every displayed Treeview item and the corresponding row in the source DataFrame. This is more reliable than assuming the first DataFrame column contains a unique ID.
Source DataFrame
|
v
Search / Filter
|
v
Filtered DataFrame
|
v
Treeview
|
v
Select row
|
v
Source DataFrame index
|
v
Delete row
|
v
Run search again
|
v
Updated Treeview
The search logic comes from Part I of the DataFrame search project.
A numeric query can perform an exact comparison against the id column:
if query.isdigit() and 'id' in df.columns:
id_values=pd.to_numeric(
df['id'],
errors='coerce'
)
filtered_df=df[
id_values==int(query)
].copy()
Text searches use case-insensitive matching:
names=df['name'].fillna('').astype(str)
mask=names.str.contains(
word,
case=False,
na=False,
regex=False
)
Multiple search words are combined with a Boolean mask rather than repeatedly appending or concatenating DataFrames.
The Treeview is created once when the application starts.
Whenever the search changes, only its rows are replaced:
for item in tree.get_children():
tree.delete(item)
The filtered DataFrame is then inserted again.
This is better than creating another Treeview every time the user presses a key or deletes a row.
The older application used the first value of a row as the Treeview item ID:
iid=v[0]
and later assumed that value was the DataFrame id.
This can fail when:
The revised application creates its own Treeview item IDs and stores a mapping to the actual source DataFrame index:
row_map={}
for display_no,(source_index,row) in enumerate(
filtered_df.iterrows()
):
iid=f'row_{display_no}'
row_map[iid]=source_index
tree.insert(
'',
tk.END,
iid=iid,
values=row.tolist()
)
The Treeview ID and the business-data ID are now separate concepts.
The Treeview <<TreeviewSelect>> event runs a function whenever the selection changes:
tree.bind(
'<<TreeviewSelect>>',
collect_selection
)
The selected Treeview item is obtained with:
selection=tree.selection()
if not selection:
return
selected_iid=selection[0]
This avoids assuming that a selection always exists.
The row values can be read from the selected Treeview item:
values=tree.item(
selected_iid,
'values'
)
The program displays the values in a Label:
selected_var.set(
'Selected: '+
' | '.join(
map(
str,
values
)
)
)
The Delete Button remains disabled until a valid row has been selected.

First use the Treeview item ID to find the source DataFrame index:
source_index=row_map.get(
selected_iid
)
The selected row is then removed from the working source DataFrame using Pandas drop():
df=df.drop(
index=source_index
).reset_index(
drop=True
)
A confirmation dialog is shown before the deletion:
confirmed=messagebox.askyesno(
'Delete row',
'Delete the selected row from the working DataFrame?'
)
The older application deleted the selected row only from the filtered result:
df2.drop(...)
However, the next search rebuilt df2 from the original df. The deleted row could therefore return.
The revised flow is:
delete from df
|
v
run current search again
|
v
create new filtered_df
|
v
refresh Treeview
The row remains deleted throughout the current application session.
After deleting from the source DataFrame, call the search function again:
search_data()
If the current search still matches other rows, they remain visible. If the deleted row was the only match, the Treeview becomes empty and the result count becomes zero.
The current filtered DataFrame can be saved using a Tkinter Save As dialog.
if file_path.lower().endswith(
'.csv'
):
filtered_df.to_csv(
file_path,
index=False
)
else:
filtered_df.to_excel(
file_path,
index=False
)
This saves the records currently represented by the filtered DataFrame, including any in-memory deletion already made.
Pandas to_clipboard() copies the filtered DataFrame:
filtered_df.to_clipboard(
index=False
)
A temporary message confirms the operation.
The original page added a vertical scrollbar when more than ten results were available. The revised application uses both directions.
y_scroll=ttk.Scrollbar(
table_frame,
orient='vertical',
command=tree.yview
)
x_scroll=ttk.Scrollbar(
table_frame,
orient='horizontal',
command=tree.xview
)
tree.configure(
yscrollcommand=y_scroll.set,
xscrollcommand=x_scroll.set
)
For the sample student data, the id column happens to contain unique values. That made code such as this appear to work:
trv.insert(
'',
'end',
iid=v[0],
values=v
)
However, Treeview is a display widget and the DataFrame may come from many different sources. The first column should not automatically become the widget's identity.
The revised version maintains:
Treeview iid
|
v
row_map
|
v
source DataFrame index
This lets the program delete the exact selected row regardless of the actual CSV or Excel column structure.
Suppose the source DataFrame contains:
1 John Deo
2 Max Ruin
3 Arnold
4 Krish Star
A search for:
Max
displays one row. After selecting that row and clicking Delete Selected:
df
|
+-- John Deo
+-- Arnold
+-- Krish Star
The current search is run again and now finds zero matching records.
Clearing the search displays all remaining working DataFrame rows. The deleted Max row does not return.
Bind <<TreeviewSelect>> to a function and use tree.selection() to get the currently selected Treeview item.
Use tree.item(selected_iid, 'values') to read the values stored in the selected Treeview row.
The first column may not be unique or may not be an ID. The revised application creates its own Treeview identifiers and maps them to DataFrame indexes.
The filtered DataFrame is recreated whenever the search changes. Deleting from the source working DataFrame prevents the deleted row from returning during the same application session.
No. It modifies only the DataFrame in memory. The original file remains unchanged unless the user explicitly saves modified data to a file.
A confirmation dialog prevents an accidental row-selection click followed by an unintended deletion.
Yes. The current filtered DataFrame can be saved as Excel or CSV or copied to the clipboard.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.