Implement photos without faces feature in Search GUI and SearchStats
This commit enhances the SearchGUI and SearchStats classes by adding functionality to search for and display photos that do not have detected faces. The SearchGUI now includes logic to handle the visibility of relevant columns based on the selected search type, ensuring a streamlined user experience. Additionally, the SearchStats class has been updated to retrieve photos without faces from the database, improving the overall search capabilities of the application.
This commit is contained in:
parent
40ffc0692e
commit
8a9834b056
@ -25,7 +25,7 @@ class SearchGUI:
|
||||
"Search photos by multiple people (planned)",
|
||||
"Most common tags (planned)",
|
||||
"Most photographed people (planned)",
|
||||
"Photos without faces (planned)",
|
||||
"Photos without faces",
|
||||
"Photos without tags (planned)",
|
||||
"Duplicate faces (planned)",
|
||||
"Face quality distribution (planned)",
|
||||
@ -197,6 +197,9 @@ class SearchGUI:
|
||||
# Show person column for name search
|
||||
tree.column("person", width=180, minwidth=50, anchor="w")
|
||||
tree.heading("person", text="Person", command=lambda: sort_treeview("person"))
|
||||
# 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
|
||||
tree["displaycolumns"] = ("select", "person", "tags", "open_dir", "open_photo", "path")
|
||||
elif choice == self.SEARCH_TYPES[2]: # Search photos by tags
|
||||
@ -208,8 +211,22 @@ class SearchGUI:
|
||||
# Hide person column completely for tag search
|
||||
tree.column("person", width=0, minwidth=0, anchor="w")
|
||||
tree.heading("person", text="")
|
||||
# Restore people icon column for tag search
|
||||
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")
|
||||
elif choice == self.SEARCH_TYPES[6]: # Photos without faces
|
||||
# No input needed for this search type
|
||||
search_btn.configure(state="normal")
|
||||
# Hide person column since photos without faces won't have person info
|
||||
tree.column("person", width=0, minwidth=0, anchor="w")
|
||||
tree.heading("person", text="")
|
||||
# Hide the people icon column since there are no faces/people
|
||||
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")
|
||||
else:
|
||||
planned_label.pack(anchor="w")
|
||||
name_entry.configure(state="disabled")
|
||||
@ -219,6 +236,9 @@ class SearchGUI:
|
||||
# Show person column for other search types
|
||||
tree.column("person", width=180, minwidth=50, anchor="w")
|
||||
tree.heading("person", text="Person", command=lambda: sort_treeview("person"))
|
||||
# Restore people icon column for other search types
|
||||
tree.column("open_photo", width=50, minwidth=50, anchor="center")
|
||||
tree.heading("open_photo", text="👤")
|
||||
# Restore all columns to display
|
||||
tree["displaycolumns"] = ("select", "person", "tags", "open_dir", "open_photo", "path")
|
||||
|
||||
@ -244,6 +264,11 @@ class SearchGUI:
|
||||
path, tag_info = row
|
||||
photo_tags = get_photo_tags_for_display(path)
|
||||
tree.insert("", tk.END, values=("☐", "", photo_tags, "📁", "👤", path))
|
||||
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)
|
||||
tree.insert("", tk.END, values=("☐", "", photo_tags, "📁", "", path))
|
||||
else:
|
||||
# For name search: (full_name, path) - show person column
|
||||
full_name, p = row
|
||||
@ -256,6 +281,9 @@ class SearchGUI:
|
||||
if search_type_var.get() == self.SEARCH_TYPES[2]: # Tag search
|
||||
# Sort by tags column for tag search
|
||||
self.sort_column = "tags"
|
||||
elif search_type_var.get() == self.SEARCH_TYPES[6]: # Photos without faces
|
||||
# Sort by path column for photos without faces
|
||||
self.sort_column = "path"
|
||||
else:
|
||||
# Sort by person column for name search
|
||||
self.sort_column = "person"
|
||||
@ -302,6 +330,15 @@ class SearchGUI:
|
||||
mode_text = "all" if match_all else "any"
|
||||
messagebox.showinfo("Search", f"No photos found with {mode_text} of the tags: {', '.join(tags)}", parent=root)
|
||||
add_results(rows)
|
||||
elif choice == self.SEARCH_TYPES[6]: # Photos without faces
|
||||
rows = self.search_stats.get_photos_without_faces()
|
||||
if not rows:
|
||||
messagebox.showinfo("Search", "No photos without faces found.", parent=root)
|
||||
else:
|
||||
# Convert to the format expected by add_results: (path, tag_info)
|
||||
# For photos without faces, we don't have person info, so we use empty string
|
||||
formatted_rows = [(path, "") for path, filename in rows]
|
||||
add_results(formatted_rows)
|
||||
|
||||
def open_dir(path: str):
|
||||
try:
|
||||
@ -861,6 +898,7 @@ class SearchGUI:
|
||||
|
||||
# Determine column offsets based on search type
|
||||
is_tag_search = (search_type_var.get() == self.SEARCH_TYPES[2])
|
||||
is_photos_without_faces = (search_type_var.get() == self.SEARCH_TYPES[6])
|
||||
if is_tag_search:
|
||||
# Tag search: person column is hidden, select is first
|
||||
select_col = "#1" # select is now column 1
|
||||
@ -868,6 +906,13 @@ class SearchGUI:
|
||||
face_col = "#4" # open_photo is now column 4
|
||||
path_col = "#5" # path is now column 5
|
||||
path_index = 5 # path is at index 5 in values array
|
||||
elif is_photos_without_faces:
|
||||
# Photos without faces: person and people icon columns are hidden
|
||||
select_col = "#1" # select is now column 1
|
||||
open_dir_col = "#3" # open_dir is now column 3
|
||||
face_col = "#4" # open_photo is now column 4 (but hidden)
|
||||
path_col = "#4" # path is now column 4 (since people icon is hidden)
|
||||
path_index = 5 # path is at index 5 in values array
|
||||
else:
|
||||
# Name search: all columns visible, select is first
|
||||
select_col = "#1" # select is now column 1
|
||||
@ -931,6 +976,7 @@ class SearchGUI:
|
||||
|
||||
# Determine column offsets based on search type
|
||||
is_tag_search = (search_type_var.get() == self.SEARCH_TYPES[2])
|
||||
is_photos_without_faces = (search_type_var.get() == self.SEARCH_TYPES[6])
|
||||
if is_tag_search:
|
||||
# Tag search: person column is hidden, select is first
|
||||
tags_col = "#2" # tags is now column 2
|
||||
@ -938,6 +984,13 @@ class SearchGUI:
|
||||
face_col = "#4" # open_photo is now column 4
|
||||
path_col = "#5" # path is now column 5
|
||||
path_index = 5 # path is at index 5 in values array
|
||||
elif is_photos_without_faces:
|
||||
# Photos without faces: person and people icon columns are hidden
|
||||
tags_col = "#2" # tags is now column 2
|
||||
open_dir_col = "#3" # open_dir is now column 3
|
||||
face_col = "#4" # open_photo is now column 4 (but hidden)
|
||||
path_col = "#4" # path is now column 4 (since people icon is hidden)
|
||||
path_index = 5 # path is at index 5 in values array
|
||||
else:
|
||||
# Name search: all columns visible, select is first
|
||||
tags_col = "#3" # tags is column 3
|
||||
|
||||
@ -276,10 +276,31 @@ class SearchStats:
|
||||
return []
|
||||
|
||||
def get_photos_without_faces(self) -> List[Tuple]:
|
||||
"""Get photos that have no detected faces"""
|
||||
# This would need to be implemented in the database module
|
||||
# For now, return empty list
|
||||
return []
|
||||
"""Get photos that have no detected faces
|
||||
|
||||
Returns:
|
||||
List of tuples: (photo_path, filename)
|
||||
"""
|
||||
results = []
|
||||
try:
|
||||
with self.db.get_db_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
# Find photos that have no faces associated with them
|
||||
cursor.execute('''
|
||||
SELECT p.path, p.filename
|
||||
FROM photos p
|
||||
LEFT JOIN faces f ON p.id = f.photo_id
|
||||
WHERE f.photo_id IS NULL
|
||||
ORDER BY p.filename
|
||||
''')
|
||||
for row in cursor.fetchall():
|
||||
if row and row[0]:
|
||||
results.append((row[0], row[1]))
|
||||
except Exception as e:
|
||||
if self.verbose > 0:
|
||||
print(f"Error searching photos without faces: {e}")
|
||||
|
||||
return results
|
||||
|
||||
def get_photos_without_tags(self) -> List[Tuple]:
|
||||
"""Get photos that have no tags"""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user