Display¶
See what Argus sees. One window per camera. Optional — headless doesn't need it.
Constraint¶
All cv2.imshow() and cv2.waitKey() calls MUST happen on the main thread.
This is not a suggestion. OpenCV's highGUI backend (Qt, Cocoa, GTK) requires it. Call from a background thread and you get a crash, a hang, or silent corruption. The StreamManager enforces this by calling Display.update() and Display.tick() from the main thread only.
Display¶
Constructor¶
Creates one window per camera:
window_name = f"Argus — {cam.name} [{cam.id}]"
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(window_name, 640, 480)
| Detail | Value |
|---|---|
| Window flag | WINDOW_NORMAL — resizable by the user |
| Initial size | 640 × 480 |
| Naming | "Argus — Front Door [cam_01]" |
Internal state:
| Attribute | Type | Purpose |
|---|---|---|
_cameras |
dict[str, CameraConfig] |
Camera ID → config |
_window_names |
dict[str, str] |
Camera ID → window name string |
update()¶
def update(
self,
camera_id: str,
frame: np.ndarray,
detections: list[MatchEvent] | None = None,
) -> None:
Called once per camera per detection cycle from the main thread.
- Copies the frame (never modifies the original).
- If detections exist, draws overlays for each.
- Calls
cv2.imshow(window_name, display_frame).
If camera_id doesn't match any known window, returns silently.
_draw_detection()¶
Draws on the frame in-place. Modifies the copy, not the original.
Drawing Steps¶
1. Green bounding box (2px)
cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
2. Compute label text size
label = "John Doe (87%)"
label_size = cv2.getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.6, 2)[0]
3. Green filled rectangle above the bbox (label background)
cv2.rectangle(frame,
(left, top - label_size[1] - 10),
(left + label_size[0] + 4, top),
(0, 255, 0), cv2.FILLED)
4. Black text on green background
cv2.putText(frame, label, (left + 2, top - 5),
FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2)
Label format: "{target_name} ({confidence:.0%})" → "John Doe (87%)".
Color scheme: green box, green label background, black text. High contrast on any scene. The same drawing logic exists in AlertHandler._save_screenshot() — duplicated intentionally for independence.
tick()¶
| Return | Meaning |
|---|---|
True |
Keep running |
False |
User pressed q — shutdown requested |
cv2.waitKey(1) waits 1ms for a key event and processes all pending GUI events (window redraws, resize, focus changes). Without this call, windows freeze.
The & 0xFF mask is necessary on some platforms where waitKey returns modifier key bits.
destroy()¶
Closes every OpenCV window. Called from _shutdown() after all streams are stopped.
Window Management¶
| Operation | Thread | Timing |
|---|---|---|
namedWindow / resizeWindow |
Main (constructor) | Once at startup |
imshow |
Main (update) | Every detection cycle per camera |
waitKey |
Main (tick) | Every main loop iteration |
destroyAllWindows |
Main (shutdown) | Once at teardown |
All GUI operations are confined to the main thread. No locks needed — there's only one writer.