piker/.claude/skills/pyqtgraph-optimization/examples.md

2.1 KiB

PyQtGraph Optimization Examples

Real-world optimization case studies from piker.

Case Study: Gap Annotations (1285 gaps)

Before: Individual pg.ArrowItem + SelectRect

Total creation time: 6.6 seconds
Per-item overhead: ~5ms
Memory: 1285 ArrowItem + 1285 SelectRect objects

Each gap was rendered as two separate QGraphicsItem instances (arrow + highlight rect), resulting in 2570 Qt objects.

After: Single GapAnnotations batch renderer

Total creation time:
  104ms (server) + 376ms (client)
Effective per-item: ~0.08ms
Speedup: ~36x client, ~180x server
Memory: 1 GapAnnotations object

All 1285 gaps rendered via: - One PrimitiveArray for all rectangles - One QPainterPath for all arrows - Shared pen/brush across all items

Profiler Output (Client)

> Entering markup_gaps() for 1285 gaps
  initial redraw: 0.20ms, tot:0.20
  built annotation specs: 256.48ms, tot:256.68
  batch IPC call complete: 119.26ms, tot:375.94
  final redraw: 0.07ms, tot:376.02
< Exiting markup_gaps(), total: 376.04ms

Profiler Output (Server)

> Entering Batch annotate 1285 gaps
  `np.searchsorted()` complete!: 0.81ms, tot:0.81
  `time_to_row` creation: 98.45ms, tot:99.28
  created GapAnnotations item: 2.98ms, tot:102.26
< Exiting Batch annotate, total: 104.15ms

Positioning/Update Pattern

For annotations that need repositioning when the view scrolls or zooms:

def reposition(self, array):
    '''
    Update positions based on new array data.

    '''
    # vectorized timestamp lookups (not linear!)
    time_to_row = self._build_lookup(array)

    # update rect array in-place
    rect_memory = self._rectarray.ndarray()
    for i, spec in enumerate(self._specs):
        row = time_to_row.get(spec['time'])
        if row:
            rect_memory[i, 0] = row['index']
            rect_memory[i, 1] = row['close']
            # ... width, height

    # trigger repaint (single call, not per-item)
    self.update()

Key insight: Update the underlying memory arrays directly, then call .update() once. Never create/destroy Qt objects during reposition.