Enhance Search GUI with processed status and improved results display

This commit updates the SearchGUI class to include a new "Processed" column, displaying the processing status of photos. The results area now features a header with item count, and sorting functionality has been added for the processed status. Additionally, the treeview has been enhanced with a vertical scrollbar for better navigation. The logic for displaying and sorting results has been updated to accommodate these changes, improving the overall user experience in managing photo collections.
This commit is contained in:
tanyar09 2025-10-09 14:00:30 -04:00
parent 18e65e88fc
commit aa67f12a20

View File

@ -225,13 +225,21 @@ class SearchGUI:
# Results area
results_frame = ttk.Frame(main)
results_frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(results_frame, text="Results:", font=("Arial", 10, "bold")).pack(anchor="w")
# Results header with count
results_header = ttk.Frame(results_frame)
results_header.pack(fill=tk.X)
results_label = ttk.Label(results_header, text="Results:", font=("Arial", 10, "bold"))
results_label.pack(side=tk.LEFT)
results_count_label = ttk.Label(results_header, text="(0 items)", font=("Arial", 10), foreground="gray")
results_count_label.pack(side=tk.LEFT, padx=(6, 0))
columns = ("select", "person", "tags", "open_dir", "open_photo", "path", "date_taken")
columns = ("select", "person", "tags", "processed", "open_dir", "open_photo", "path", "date_taken")
tree = ttk.Treeview(results_frame, columns=columns, show="headings", selectmode="browse")
tree.heading("select", text="")
tree.heading("person", text="Person", command=lambda: sort_treeview("person"))
tree.heading("tags", text="Tags", command=lambda: sort_treeview("tags"))
tree.heading("processed", text="Processed", command=lambda: sort_treeview("processed"))
tree.heading("open_dir", text="📁")
tree.heading("open_photo", text="👤")
tree.heading("path", text="Photo path", command=lambda: sort_treeview("path"))
@ -239,11 +247,19 @@ class SearchGUI:
tree.column("select", width=50, anchor="center")
tree.column("person", width=180, anchor="w")
tree.column("tags", width=200, anchor="w")
tree.column("processed", width=80, anchor="center")
tree.column("open_dir", width=50, anchor="center")
tree.column("open_photo", width=50, anchor="center")
tree.column("path", width=400, anchor="w")
tree.column("date_taken", width=100, anchor="center")
tree.pack(fill=tk.BOTH, expand=True, pady=(4, 0))
# Add vertical scrollbar for the treeview
tree_v_scrollbar = ttk.Scrollbar(results_frame, orient="vertical", command=tree.yview)
tree.configure(yscrollcommand=tree_v_scrollbar.set)
# Pack treeview and scrollbar
tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, pady=(4, 0))
tree_v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=(4, 0))
# Buttons
btns = ttk.Frame(main)
@ -275,6 +291,7 @@ class SearchGUI:
# Sort the items
# For person, tags, and path columns, sort alphabetically
# For date_taken column, sort by date
# For processed column, sort by processed status (Yes/No)
# For icon columns, maintain original order
if col in ['person', 'tags', 'path']:
items.sort(key=lambda x: x[0].lower(), reverse=self.sort_reverse)
@ -286,6 +303,15 @@ class SearchGUI:
return "9999-12-31" # Put "No date" entries at the end
return date_str
items.sort(key=date_sort_key, reverse=self.sort_reverse)
elif col == 'processed':
# Sort by processed status (Yes comes before No)
def processed_sort_key(item):
processed_str = item[0]
if processed_str == "Yes":
return "0" # Yes comes first
else:
return "1" # No comes second
items.sort(key=processed_sort_key, reverse=self.sort_reverse)
else:
# For icon columns, just reverse if clicking same column
if self.sort_column == col and self.sort_reverse:
@ -303,6 +329,7 @@ class SearchGUI:
# Reset all headers
tree.heading("person", text="Person")
tree.heading("tags", text="Tags")
tree.heading("processed", text="Processed")
tree.heading("path", text="Photo path")
tree.heading("date_taken", text="Date Taken")
@ -313,6 +340,9 @@ class SearchGUI:
elif self.sort_column == "tags":
indicator = "" if self.sort_reverse else ""
tree.heading("tags", text="Tags" + indicator)
elif self.sort_column == "processed":
indicator = "" if self.sort_reverse else ""
tree.heading("processed", text="Processed" + indicator)
elif self.sort_column == "path":
indicator = "" if self.sort_reverse else ""
tree.heading("path", text="Photo path" + indicator)
@ -344,7 +374,7 @@ class SearchGUI:
# Restore people icon column for name search
tree.column("open_photo", width=50, minwidth=50, anchor="center")
tree.heading("open_photo", text="👤")
# Restore all columns to display
# Restore all columns to display (hide processed column for name search)
tree["displaycolumns"] = ("select", "person", "tags", "open_dir", "open_photo", "path", "date_taken")
elif choice == self.SEARCH_TYPES[1]: # Search photos by date
date_frame.pack(fill=tk.X)
@ -364,7 +394,7 @@ class SearchGUI:
tree.column("open_photo", width=50, minwidth=50, anchor="center")
tree.heading("open_photo", text="👤")
# Show all columns except person for date search
tree["displaycolumns"] = ("select", "tags", "open_dir", "open_photo", "path", "date_taken")
tree["displaycolumns"] = ("select", "tags", "processed", "open_dir", "open_photo", "path", "date_taken")
elif choice == self.SEARCH_TYPES[2]: # Search photos by tags
tag_frame.pack(fill=tk.X)
tag_mode_frame.pack(fill=tk.X, pady=(4, 0))
@ -383,7 +413,7 @@ class SearchGUI:
tree.column("open_photo", width=50, minwidth=50, anchor="center")
tree.heading("open_photo", text="👤")
# Also hide the column from display
tree["displaycolumns"] = ("select", "tags", "open_dir", "open_photo", "path", "date_taken")
tree["displaycolumns"] = ("select", "tags", "processed", "open_dir", "open_photo", "path", "date_taken")
elif choice == self.SEARCH_TYPES[6]: # Photos without faces
# No input needed for this search type
search_btn.configure(state="normal")
@ -394,7 +424,7 @@ class SearchGUI:
tree.column("open_photo", width=0, minwidth=0, anchor="center")
tree.heading("open_photo", text="")
# Also hide the columns from display
tree["displaycolumns"] = ("select", "tags", "open_dir", "path", "date_taken")
tree["displaycolumns"] = ("select", "tags", "processed", "open_dir", "path", "date_taken")
# Auto-run search for photos without faces
do_search()
elif choice == self.SEARCH_TYPES[7]: # Photos without tags
@ -407,7 +437,7 @@ class SearchGUI:
tree.column("open_photo", width=50, minwidth=50, anchor="center")
tree.heading("open_photo", text="👤")
# Show all columns except person for photos without tags search
tree["displaycolumns"] = ("select", "tags", "open_dir", "open_photo", "path", "date_taken")
tree["displaycolumns"] = ("select", "tags", "processed", "open_dir", "open_photo", "path", "date_taken")
# Auto-run search for photos without tags
do_search()
else:
@ -427,7 +457,7 @@ class SearchGUI:
tree.column("open_photo", width=50, minwidth=50, anchor="center")
tree.heading("open_photo", text="👤")
# Show all columns except person for other search types
tree["displaycolumns"] = ("select", "tags", "open_dir", "open_photo", "path", "date_taken")
tree["displaycolumns"] = ("select", "tags", "processed", "open_dir", "open_photo", "path", "date_taken")
def filter_results_by_folder(results, folder_path):
"""Filter search results by folder path if specified."""
@ -458,6 +488,8 @@ class SearchGUI:
self.selected_photos.clear()
# Clear tag cache
self.photo_tags_cache.clear()
# Reset results count
results_count_label.config(text="(0 items)")
update_header_display()
def add_results(rows: List[tuple]):
@ -468,33 +500,38 @@ class SearchGUI:
# For date search: (path, date_taken) - hide person column
path, date_taken = row
photo_tags = get_photo_tags_for_display(path)
tree.insert("", tk.END, values=("", "", photo_tags, "📁", "👤", path, date_taken))
processed_status = get_photo_processed_status(path)
tree.insert("", tk.END, values=("", "", photo_tags, processed_status, "📁", "👤", path, date_taken))
elif search_type_var.get() == self.SEARCH_TYPES[2]: # Tag search
# For tag search: (path, tag_info) - hide person column
# Show ALL tags for the photo, not just matching ones
path, tag_info = row
photo_tags = get_photo_tags_for_display(path)
date_taken = get_photo_date_taken(path)
tree.insert("", tk.END, values=("", "", photo_tags, "📁", "👤", path, date_taken))
processed_status = get_photo_processed_status(path)
tree.insert("", tk.END, values=("", "", photo_tags, processed_status, "📁", "👤", path, date_taken))
elif search_type_var.get() == self.SEARCH_TYPES[6]: # Photos without faces
# For photos without faces: (path, tag_info) - hide person and people icon columns
path, tag_info = row
photo_tags = get_photo_tags_for_display(path)
date_taken = get_photo_date_taken(path)
tree.insert("", tk.END, values=("", "", photo_tags, "📁", "", path, date_taken))
processed_status = get_photo_processed_status(path)
tree.insert("", tk.END, values=("", "", photo_tags, processed_status, "📁", "", path, date_taken))
elif search_type_var.get() == self.SEARCH_TYPES[7]: # Photos without tags
# For photos without tags: (path, filename) - hide person column
path, filename = row
photo_tags = get_photo_tags_for_display(path) # Will be "No tags"
date_taken = get_photo_date_taken(path)
tree.insert("", tk.END, values=("", "", photo_tags, "📁", "👤", path, date_taken))
processed_status = get_photo_processed_status(path)
tree.insert("", tk.END, values=("", "", photo_tags, processed_status, "📁", "👤", path, date_taken))
else:
# For name search: (path, full_name) - show person column
p, full_name = row
# Get tags for this photo
photo_tags = get_photo_tags_for_display(p)
date_taken = get_photo_date_taken(p)
tree.insert("", tk.END, values=("", full_name, photo_tags, "📁", "👤", p, date_taken))
processed_status = get_photo_processed_status(p)
tree.insert("", tk.END, values=("", full_name, photo_tags, processed_status, "📁", "👤", p, date_taken))
# Sort by appropriate column by default when results are first loaded
if rows and self.sort_column is None:
@ -532,6 +569,10 @@ class SearchGUI:
tree.move(child, '', index)
# Update header display
update_header_display()
# Update results count
item_count = len(tree.get_children())
results_count_label.config(text=f"({item_count} items)")
def do_search():
clear_results()
@ -658,10 +699,10 @@ class SearchGUI:
def toggle_photo_selection(row_id, vals):
"""Toggle checkbox selection for a photo."""
if len(vals) < 6:
if len(vals) < 7:
return
current_state = vals[0] # Checkbox is now in column 0 (first)
path = vals[5] # Photo path is now in column 5 (last)
path = vals[6] # Photo path is now in column 6 (last)
if current_state == "":
# Select photo
new_state = ""
@ -714,7 +755,7 @@ class SearchGUI:
# Update all checkboxes to unselected state
for item in tree.get_children():
vals = tree.item(item, "values")
if len(vals) >= 6 and vals[0] == "":
if len(vals) >= 7 and vals[0] == "":
new_vals = list(vals)
new_vals[0] = ""
tree.item(item, values=new_vals)
@ -857,6 +898,20 @@ class SearchGUI:
except Exception:
return "No date"
def get_photo_processed_status(photo_path):
"""Get processed status for a photo to display in the processed column."""
try:
with self.db.get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT processed FROM photos WHERE path = ?', (photo_path,))
result = cursor.fetchone()
if result and result[0] is not None:
return "Yes" if result[0] else "No"
else:
return "No" # Default to not processed
except Exception:
return "No"
def get_photo_people_tooltip(photo_path):
"""Get people information for a photo to display in tooltip."""
try:
@ -1180,8 +1235,8 @@ class SearchGUI:
# Update each affected row in the search results
for item in tree.get_children():
vals = tree.item(item, "values")
if len(vals) >= 6:
photo_path = vals[5] # Photo path is at index 5
if len(vals) >= 7:
photo_path = vals[6] # Photo path is at index 6
if photo_path in affected_photo_paths:
# Get current tags for this photo from cache
current_tags = get_photo_tags_for_display(photo_path)
@ -1217,26 +1272,26 @@ class SearchGUI:
is_photos_without_faces = (search_type_var.get() == self.SEARCH_TYPES[6])
if is_name_search:
# Name search: all columns visible including person
# Name search: all columns visible including person (processed column hidden)
select_col = "#1" # select is column 1
open_dir_col = "#4" # open_dir is column 4
face_col = "#5" # open_photo is column 5
path_col = "#6" # path is column 6
path_index = 5 # path is at index 5 in values array
path_index = 6 # path is at index 6 in values array (still same since processed is hidden from display)
elif is_photos_without_faces:
# Photos without faces: person and people icon columns are hidden
select_col = "#1" # select is column 1
open_dir_col = "#3" # open_dir is column 3
face_col = "#4" # open_photo is column 4 (but hidden)
path_col = "#4" # path is column 4 (since people icon is hidden)
path_index = 5 # path is at index 5 in values array
open_dir_col = "#4" # open_dir is column 4
face_col = "#5" # open_photo is column 5 (but hidden)
path_col = "#5" # path is column 5 (since people icon is hidden)
path_index = 6 # path is at index 6 in values array
else:
# All other searches: person column is hidden, people icon visible
select_col = "#1" # select is column 1
open_dir_col = "#3" # open_dir is column 3
face_col = "#4" # open_photo is column 4
path_col = "#5" # path is column 5
path_index = 5 # path is at index 5 in values array
open_dir_col = "#4" # open_dir is column 4
face_col = "#5" # open_photo is column 5
path_col = "#6" # path is column 6
path_index = 6 # path is at index 6 in values array
path = vals[path_index] # Photo path
if col_id == open_dir_col: # Open directory column
@ -1330,26 +1385,26 @@ class SearchGUI:
is_photos_without_faces = (search_type_var.get() == self.SEARCH_TYPES[6])
if is_name_search:
# Name search: all columns visible including person
# Name search: all columns visible including person (processed column hidden)
tags_col = "#3" # tags is column 3
open_dir_col = "#4" # open_dir is column 4
face_col = "#5" # open_photo is column 5
path_col = "#6" # path is column 6
path_index = 5 # path is at index 5 in values array
path_index = 6 # path is at index 6 in values array (still same since processed is hidden from display)
elif is_photos_without_faces:
# Photos without faces: person and people icon columns are hidden
tags_col = "#2" # tags is column 2
open_dir_col = "#3" # open_dir is column 3
face_col = "#4" # open_photo is column 4 (but hidden)
path_col = "#4" # path is column 4 (since people icon is hidden)
path_index = 5 # path is at index 5 in values array
open_dir_col = "#4" # open_dir is column 4
face_col = "#5" # open_photo is column 5 (but hidden)
path_col = "#5" # path is column 5 (since people icon is hidden)
path_index = 6 # path is at index 6 in values array
else:
# All other searches: person column is hidden, people icon visible
tags_col = "#2" # tags is column 2
open_dir_col = "#3" # open_dir is column 3
face_col = "#4" # open_photo is column 4
path_col = "#5" # path is column 5
path_index = 5 # path is at index 5 in values array
open_dir_col = "#4" # open_dir is column 4
face_col = "#5" # open_photo is column 5
path_col = "#6" # path is column 6
path_index = 6 # path is at index 6 in values array
if col_id == tags_col: # Tags column
tree.config(cursor="")
@ -1395,8 +1450,8 @@ class SearchGUI:
# Show and center
root.update_idletasks()
# Widened to ensure all columns are visible by default, including the new date taken column
self.gui_core.center_window(root, 1200, 520)
# Widened to ensure all columns are visible by default, including the new processed column
self.gui_core.center_window(root, 1300, 520)
root.deiconify()
root.mainloop()
return 0