feat: Implement lazy-loading for SearchFilterWidget to improve performance and resource management
This commit is contained in:
+41
-16
@@ -24,7 +24,6 @@ from pathology_management_window import PathologyManagementWindow
|
|||||||
from pathology_manager import PathologyManager
|
from pathology_manager import PathologyManager
|
||||||
from preferences import get_config_dir, get_pref, save_preferences, set_pref
|
from preferences import get_config_dir, get_pref, save_preferences, set_pref
|
||||||
from search_filter import DataFilter
|
from search_filter import DataFilter
|
||||||
from search_filter_ui import SearchFilterWidget
|
|
||||||
from settings_window import SettingsWindow
|
from settings_window import SettingsWindow
|
||||||
from theme_manager import ThemeManager
|
from theme_manager import ThemeManager
|
||||||
from ui_manager import UIManager
|
from ui_manager import UIManager
|
||||||
@@ -346,6 +345,8 @@ class MedTrackerApp:
|
|||||||
# --- Main Frame ---
|
# --- Main Frame ---
|
||||||
main_frame: ttk.Frame = ttk.Frame(self.root, padding="10", style="Card.TFrame")
|
main_frame: ttk.Frame = ttk.Frame(self.root, padding="10", style="Card.TFrame")
|
||||||
main_frame.grid(row=0, column=0, sticky="nsew")
|
main_frame.grid(row=0, column=0, sticky="nsew")
|
||||||
|
# Store for lazy child creation (search panel)
|
||||||
|
self.main_frame = main_frame
|
||||||
|
|
||||||
# Configure root window grid
|
# Configure root window grid
|
||||||
self.root.grid_rowconfigure(0, weight=1)
|
self.root.grid_rowconfigure(0, weight=1)
|
||||||
@@ -400,19 +401,14 @@ class MedTrackerApp:
|
|||||||
self.tree: ttk.Treeview = table_ui["tree"]
|
self.tree: ttk.Treeview = table_ui["tree"]
|
||||||
self.tree.bind("<Double-1>", self.handle_double_click)
|
self.tree.bind("<Double-1>", self.handle_double_click)
|
||||||
|
|
||||||
# --- Create Search/Filter Widget ---
|
# --- Search/Filter Widget (lazy-loaded) ---
|
||||||
self.search_filter_widget = SearchFilterWidget(
|
self.search_filter_widget = None # Created on demand
|
||||||
main_frame,
|
# Restore prior visibility preference, but only create when needed
|
||||||
self.data_filter,
|
|
||||||
self._on_filter_update,
|
|
||||||
self.medicine_manager,
|
|
||||||
self.pathology_manager,
|
|
||||||
logger,
|
|
||||||
)
|
|
||||||
# Restore prior visibility state from preferences
|
|
||||||
self.search_filter_visible = bool(get_pref("search_panel_visible", False))
|
self.search_filter_visible = bool(get_pref("search_panel_visible", False))
|
||||||
if self.search_filter_visible:
|
if self.search_filter_visible:
|
||||||
self.search_filter_widget.show()
|
self._ensure_search_widget()
|
||||||
|
# mypy: widget ensured above
|
||||||
|
self.search_filter_widget.show() # type: ignore[union-attr]
|
||||||
|
|
||||||
# --- Create Status Bar ---
|
# --- Create Status Bar ---
|
||||||
self.status_bar = self.ui_manager.create_status_bar(main_frame)
|
self.status_bar = self.ui_manager.create_status_bar(main_frame)
|
||||||
@@ -426,9 +422,8 @@ class MedTrackerApp:
|
|||||||
# Force one-time restoration in refresh and reflect in the UI if visible
|
# Force one-time restoration in refresh and reflect in the UI if visible
|
||||||
try:
|
try:
|
||||||
self.refresh_data_display(apply_filters=True)
|
self.refresh_data_display(apply_filters=True)
|
||||||
if self.search_filter_visible and hasattr(
|
if self.search_filter_visible and self.search_filter_widget is not None:
|
||||||
self.search_filter_widget, "sync_ui_from_filter"
|
# Keep UI in sync only if panel is actually instantiated
|
||||||
):
|
|
||||||
self.search_filter_widget.sync_ui_from_filter()
|
self.search_filter_widget.sync_ui_from_filter()
|
||||||
except Exception:
|
except Exception:
|
||||||
self.refresh_data_display()
|
self.refresh_data_display()
|
||||||
@@ -1169,18 +1164,44 @@ Use Ctrl+S to save entries and Ctrl+Q to quit."""
|
|||||||
def _toggle_search_filter(self) -> None:
|
def _toggle_search_filter(self) -> None:
|
||||||
"""Toggle the search and filter panel."""
|
"""Toggle the search and filter panel."""
|
||||||
if self.search_filter_visible:
|
if self.search_filter_visible:
|
||||||
|
if self.search_filter_widget is not None:
|
||||||
self.search_filter_widget.hide()
|
self.search_filter_widget.hide()
|
||||||
self.search_filter_visible = False
|
self.search_filter_visible = False
|
||||||
self.ui_manager.update_status("Search panel hidden", "info")
|
self.ui_manager.update_status("Search panel hidden", "info")
|
||||||
set_pref("search_panel_visible", False)
|
set_pref("search_panel_visible", False)
|
||||||
save_preferences()
|
save_preferences()
|
||||||
else:
|
else:
|
||||||
self.search_filter_widget.show()
|
self._ensure_search_widget()
|
||||||
|
# mypy: widget ensured above
|
||||||
|
self.search_filter_widget.show() # type: ignore[union-attr]
|
||||||
self.search_filter_visible = True
|
self.search_filter_visible = True
|
||||||
self.ui_manager.update_status("Search panel shown", "info")
|
self.ui_manager.update_status("Search panel shown", "info")
|
||||||
set_pref("search_panel_visible", True)
|
set_pref("search_panel_visible", True)
|
||||||
save_preferences()
|
save_preferences()
|
||||||
|
|
||||||
|
def _ensure_search_widget(self) -> None:
|
||||||
|
"""Create the search widget on demand to support lazy-loading."""
|
||||||
|
if getattr(self, "search_filter_widget", None) is not None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
# Local import to defer module load cost until first use
|
||||||
|
from search_filter_ui import SearchFilterWidget # type: ignore
|
||||||
|
|
||||||
|
self.search_filter_widget = SearchFilterWidget(
|
||||||
|
self.main_frame,
|
||||||
|
self.data_filter,
|
||||||
|
self._on_filter_update,
|
||||||
|
self.medicine_manager,
|
||||||
|
self.pathology_manager,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
# If filters were restored earlier, reflect state in UI now
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
self.search_filter_widget.sync_ui_from_filter()
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error(f"Failed to initialize search panel: {exc}")
|
||||||
|
self.ui_manager.update_status("Search panel unavailable", "error")
|
||||||
|
|
||||||
def _on_filter_update(self) -> None:
|
def _on_filter_update(self) -> None:
|
||||||
"""Handle filter updates from the search widget."""
|
"""Handle filter updates from the search widget."""
|
||||||
# Debounce rapid filter changes to avoid repeated heavy refresh.
|
# Debounce rapid filter changes to avoid repeated heavy refresh.
|
||||||
@@ -1608,6 +1629,10 @@ Use Ctrl+S to save entries and Ctrl+Q to quit."""
|
|||||||
if current_scroll_top > 0:
|
if current_scroll_top > 0:
|
||||||
self.tree.yview_moveto(current_scroll_top)
|
self.tree.yview_moveto(current_scroll_top)
|
||||||
|
|
||||||
|
# Ensure alternating stripes are normalized after any update
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
self.ui_manager.normalize_tree_stripes(self.tree)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error updating tree efficiently: {e}")
|
logger.error(f"Error updating tree efficiently: {e}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user