#!/usr/bin/env python3 """ Add detailed logging to filtered_flow_view.py to trace button lifecycle """ import sys from pathlib import Path def add_debug_prints_to_filtered_flow_view(): """Add comprehensive logging to track button issues""" view_path = Path("analyzer/tui/textual/widgets/filtered_flow_view.py") if not view_path.exists(): print("āŒ filtered_flow_view.py not found") return False # Read current content with open(view_path, 'r') as f: content = f.read() # Check if debug prints already added if "šŸ” DEBUG:" in content: print("āš ļø Debug prints already added") return True # Create backup backup_path = view_path.with_suffix('.py.debug_backup') with open(backup_path, 'w') as f: f.write(content) print(f"šŸ“ Backup created: {backup_path}") # Add debug imports at the top debug_imports = ''' import time import traceback def debug_log(message): """Debug logging with timestamp""" timestamp = time.strftime("%H:%M:%S.%f")[:-3] print(f"[{timestamp}] šŸ” DEBUG: {message}") def debug_button_state(frame_type_buttons, phase): """Log current button state""" debug_log(f"=== BUTTON STATE - {phase} ===") debug_log(f"Total buttons in dict: {len(frame_type_buttons)}") for name, btn in frame_type_buttons.items(): if hasattr(btn, 'parent') and btn.parent: parent_info = f"parent: {btn.parent.__class__.__name__}" else: parent_info = "NO PARENT" debug_log(f" {name}: {btn.__class__.__name__} ({parent_info})") debug_log("=" * 40) ''' # Insert debug imports after the existing imports lines = content.split('\n') insert_index = 0 # Find where to insert (after imports but before class definition) for i, line in enumerate(lines): if line.startswith('class FilteredFlowView'): insert_index = i break # Insert debug functions debug_lines = debug_imports.strip().split('\n') for j, debug_line in enumerate(debug_lines): lines.insert(insert_index + j, debug_line) # Now add debug prints to key methods # 1. Add to __init__ init_debug = ''' debug_log("FilteredFlowView.__init__ called")''' # 2. Add to compose method compose_debug = ''' debug_log("compose() - Creating filter bar and buttons") debug_button_state(self.frame_type_buttons, "BEFORE_COMPOSE")''' compose_end_debug = ''' debug_log("compose() - All widgets created") debug_button_state(self.frame_type_buttons, "AFTER_COMPOSE")''' # 3. Add to on_mount method mount_debug = ''' debug_log("on_mount() - Initializing view") debug_button_state(self.frame_type_buttons, "BEFORE_MOUNT_SETUP")''' mount_end_debug = ''' debug_log("on_mount() - Initialization complete") debug_button_state(self.frame_type_buttons, "AFTER_MOUNT_COMPLETE")''' # 4. Add to refresh_frame_types method refresh_start_debug = ''' debug_log("refresh_frame_types() - Starting refresh") debug_button_state(self.frame_type_buttons, "BEFORE_REFRESH") # Log throttling decision import time current_time = time.time() debug_log(f"Refresh timing - current: {current_time}, last: {self._last_refresh_time}, throttle: {self._refresh_throttle_seconds}")''' refresh_throttle_debug = ''' debug_log("refresh_frame_types() - THROTTLED, skipping refresh") return # Skip refresh if called too recently''' refresh_frame_types_debug = ''' debug_log(f"Frame types detected: {frame_types}") # If no frame types yet, skip button update if not frame_types: debug_log("refresh_frame_types() - No frame types, skipping") return''' refresh_before_remove_debug = ''' debug_log("refresh_frame_types() - About to remove/recreate buttons") debug_button_state(self.frame_type_buttons, "BEFORE_BUTTON_REMOVAL")''' refresh_after_create_debug = ''' debug_log("refresh_frame_types() - Buttons recreated") debug_button_state(self.frame_type_buttons, "AFTER_BUTTON_CREATION")''' # Apply the debug additions modified_content = '\n'.join(lines) # Insert debug prints using string replacement replacements = [ ('def __init__(self, analyzer: \'EthernetAnalyzer\', **kwargs):\n super().__init__(**kwargs)', 'def __init__(self, analyzer: \'EthernetAnalyzer\', **kwargs):\n debug_log("FilteredFlowView.__init__ called")\n super().__init__(**kwargs)'), ('def compose(self):\n """Create the filter bar and flow grid"""', 'def compose(self):\n """Create the filter bar and flow grid"""\n debug_log("compose() - Creating filter bar and buttons")\n debug_button_state(self.frame_type_buttons, "BEFORE_COMPOSE")'), ('yield self.flow_table', 'yield self.flow_table\n debug_log("compose() - All widgets created")\n debug_button_state(self.frame_type_buttons, "AFTER_COMPOSE")'), ('def on_mount(self):\n """Initialize the view"""', 'def on_mount(self):\n """Initialize the view"""\n debug_log("on_mount() - Initializing view")\n debug_button_state(self.frame_type_buttons, "BEFORE_MOUNT_SETUP")'), ('self._update_button_highlighting()', 'self._update_button_highlighting()\n debug_log("on_mount() - Initialization complete")\n debug_button_state(self.frame_type_buttons, "AFTER_MOUNT_COMPLETE")'), ('def refresh_frame_types(self):\n """Update frame type button counts and reorder by count (highest to left)"""', 'def refresh_frame_types(self):\n """Update frame type button counts and reorder by count (highest to left)"""\n debug_log("refresh_frame_types() - Starting refresh")\n debug_button_state(self.frame_type_buttons, "BEFORE_REFRESH")'), ('if current_time - self._last_refresh_time < self._refresh_throttle_seconds:\n return # Skip refresh if called too recently', 'if current_time - self._last_refresh_time < self._refresh_throttle_seconds:\n debug_log("refresh_frame_types() - THROTTLED, skipping refresh")\n return # Skip refresh if called too recently'), ('# If no frame types yet, skip button update\n if not frame_types:\n return', '# If no frame types yet, skip button update\n if not frame_types:\n debug_log("refresh_frame_types() - No frame types, skipping")\n return'), ('# Order changed, need to recreate buttons\n try:', '# Order changed, need to recreate buttons\n debug_log("refresh_frame_types() - About to remove/recreate buttons")\n debug_button_state(self.frame_type_buttons, "BEFORE_BUTTON_REMOVAL")\n try:'), ('# Update button highlighting\n self._update_button_highlighting()', '# Update button highlighting\n self._update_button_highlighting()\n debug_log("refresh_frame_types() - Buttons recreated")\n debug_button_state(self.frame_type_buttons, "AFTER_BUTTON_CREATION")') ] for old, new in replacements: if old in modified_content: modified_content = modified_content.replace(old, new) print(f"āœ… Added debug to: {old.split('\\n')[0][:50]}...") else: print(f"āš ļø Could not find: {old.split('\\n')[0][:50]}...") # Write the modified content with open(view_path, 'w') as f: f.write(modified_content) print(f"āœ… Debug prints added to {view_path}") return True def main(): print("šŸ”§ Adding Debug Prints to Track Button Lifecycle") print("=" * 60) success = add_debug_prints_to_filtered_flow_view() if success: print("\nāœ… Debug prints added successfully!") print("\nšŸš€ Now run the app and watch the console output:") print(" python debug_streamlens.py") print("\nšŸ“Š You'll see detailed logs showing:") print(" • When buttons are created") print(" • When refresh_frame_types() is called") print(" • Button parent relationships") print(" • Throttling decisions") print(" • Button removal/recreation") print("\nšŸ” Look for patterns like:") print(" • Buttons created but losing parents") print(" • Refresh called too frequently") print(" • Button removal without recreation") print("\nšŸ’” To remove debug prints later:") print(" • Restore from .debug_backup file") else: print("\nāŒ Failed to add debug prints") if __name__ == "__main__": main()