# Interaction Debouncing Implementation

**Author:** Anzal K Shahul
**Email:** anzal.ks@gmail.com
**Date:** October 21, 2025

## Overview

Implemented comprehensive interaction debouncing for all zoom and scroll controls in the `ExplorerTab` to eliminate lag and improve UI responsiveness during rapid slider/scrollbar adjustments.

## Changes Made

### 1. Timer Initialization in `__init__` (Lines 135-164)

Added debounce timers for all zoom/scroll controls:

- **X-axis Zoom Timer**: `_x_zoom_apply_timer` (50ms delay)
- **X-axis Scroll Timer**: `_x_scroll_apply_timer` (50ms delay)
- **Global Y-axis Zoom Timer**: `_y_global_zoom_apply_timer` (50ms delay)
- **Global Y-axis Scroll Timer**: `_y_global_scroll_apply_timer` (50ms delay)
- **Individual Y-axis Zoom Timers**: `_individual_y_zoom_timers` (per-channel, 50ms delay)
- **Individual Y-axis Scroll Timers**: `_individual_y_scroll_timers` (per-channel, 50ms delay)

Each timer is configured as single-shot with a 50ms interval, meaning the actual action is delayed by 50ms after the last slider/scrollbar event.

### 2. Signal Handler Updates

Modified all zoom/scroll signal handlers to use the debouncing pattern:

#### X-axis Controls:
- `_on_x_zoom_changed`: Stores value, starts timer, logs debouncing
- `_on_x_scrollbar_changed`: Stores value, starts timer (only if not updating), logs debouncing

#### Global Y-axis Controls:
- `_on_global_y_zoom_changed`: Stores value, starts timer, logs debouncing
- `_on_global_y_scrollbar_changed`: Stores value, starts timer (only if not updating), logs debouncing

#### Individual Y-axis Controls:
- `_on_individual_y_zoom_changed`: Stores value per channel, creates/starts timer, logs debouncing
- `_on_individual_y_scrollbar_changed`: Stores value per channel, creates/starts timer (only if not updating), logs debouncing

### 3. Debounced Apply Methods

Added dedicated methods that contain the actual zoom/scroll logic, called after the debounce delay:

#### X-axis:
- `_apply_debounced_x_zoom`: Applies X zoom with full logic inline
- `_apply_debounced_x_scroll`: Applies X scroll with full logic inline

#### Global Y-axis:
- `_apply_debounced_y_global_zoom`: Applies global Y zoom with full logic inline (not delegating to helper)
- `_apply_debounced_y_global_scroll`: Applies global Y scroll with full logic inline (not delegating to helper)

#### Individual Y-axis:
- `_get_or_create_individual_y_zoom_timer`: Helper to lazily create per-channel timers
- `_get_or_create_individual_y_scroll_timer`: Helper to lazily create per-channel timers
- `_apply_debounced_individual_y_zoom`: Applies individual Y zoom with full logic inline
- `_apply_debounced_individual_y_scroll`: Applies individual Y scroll with full logic inline

### 4. Key Implementation Details

1. **Inline Logic**: All debounced apply methods contain the full logic inline rather than delegating to helper methods. This eliminates additional function call overhead and potential signal cascades.

2. **State Guards**: Each apply method checks `_updating_viewranges` and `_updating_scrollbars` flags to prevent recursive updates during the application of changes.

3. **Lazy Timer Creation**: Individual Y-axis timers are created on-demand per channel to avoid memory overhead for channels that aren't used.

4. **Debug Logging**: All methods include detailed debug logging to track debouncing behavior and actual application of changes.

5. **Scrollbar Blocking**: Scroll handlers check `if not self._updating_scrollbars` before triggering debounce to prevent feedback loops.

## Performance Benefits

1. **Reduced Redraws**: Instead of redrawing on every slider/scrollbar event (potentially hundreds per second), redraws only occur 50ms after the user stops adjusting.

2. **Eliminated Cascading Updates**: Inline logic prevents helper methods from triggering additional signals or updates.

3. **Smoother User Experience**: The 50ms delay is imperceptible to users but dramatically reduces computational load.

4. **Scalability**: Per-channel timers for individual controls ensure efficient resource usage even with many channels.

## Verification

All tests passing (73 passed, 1 skipped):
- No regressions in existing functionality
- Debouncing implementation doesn't interfere with test behavior
- Signal handlers properly guard against update loops

## Usage

When running the application with debug logging enabled, you'll see messages like:
```
[_on_x_zoom_changed] Debouncing X zoom: 150
[_apply_debounced_x_zoom] Applying X zoom: 150
```

This confirms the debouncing is working: the first message appears immediately when the slider moves, the second appears 50ms after the last movement.

## Future Enhancements

If additional lag is observed:
1. Consider increasing debounce interval to 75-100ms for slower systems
2. Implement adaptive debouncing based on render time
3. Add downsampling hints to PyQtGraph during active zooming
4. Consider using QTimer.singleShot with lambda for even lighter-weight timers

