#!/usr/bin/env python3 """ Compact Buffer Widget - One Line Display ========================================= Minimal, compact widget showing everything in ONE line. Shows: [Capacity] [Progress Bar] [Overflow] Manual update - call update_display() when you want. """ from PyQt6.QtWidgets import QWidget, QHBoxLayout, QLabel, QProgressBar from PyQt6.QtCore import Qt from .circular_buffer import ( CircularBufferHandle, cb_capacity, cb_fill_bytes, cb_fill_pct, cb_overflows ) class CompactBufferWidget(QWidget): """ Compact one-line buffer display. Layout: [Size Label] [===== Progress Bar =====] [Overflow] Usage: widget = CompactBufferWidget(buffer) # Update manually: widget.update_display() """ def __init__(self, buffer: CircularBufferHandle, parent=None): super().__init__(parent) self.buffer = buffer # Horizontal layout (everything in ONE line) layout = QHBoxLayout() layout.setContentsMargins(2, 2, 2, 2) # Tight margins layout.setSpacing(5) self.setLayout(layout) # Size label (left) self.lbl_size = QLabel() self.lbl_size.setMinimumWidth(80) layout.addWidget(self.lbl_size) # Progress bar (center - takes most space) self.progress = QProgressBar() self.progress.setRange(0, 100) self.progress.setTextVisible(True) self.progress.setFormat("%p%") self.progress.setMaximumHeight(20) # Compact height layout.addWidget(self.progress, stretch=1) # Stretch to fill # Overflow label (right) self.lbl_overflow = QLabel() self.lbl_overflow.setMinimumWidth(100) # Wider for "Overflows: X" self.lbl_overflow.setAlignment(Qt.AlignmentFlag.AlignRight) layout.addWidget(self.lbl_overflow) self.update_display() def update_display(self): """Update display - call manually when needed.""" cap = cb_capacity(self.buffer) fill = cb_fill_bytes(self.buffer) pct = cb_fill_pct(self.buffer) ovf = cb_overflows(self.buffer) # Update labels self.lbl_size.setText(f"{fill}/{cap}B") self.progress.setValue(pct) # Overflow label - just show count (no color logic) self.lbl_overflow.setText(f"Overflows: {ovf}") if ovf > 0: self.lbl_overflow.setStyleSheet("color: red; font-weight: bold;") else: self.lbl_overflow.setStyleSheet("color: gray;") # ============================================================================= # Version with callback support # ============================================================================= class CompactBufferWidgetWithCallback(QWidget): """ Compact widget WITH callback support. Callback is called whenever update_display() is called. Useful for logging, debugging, or triggering other actions. Usage: def my_callback(capacity, fill, percent, overflows): print(f"Buffer: {fill}/{capacity} ({percent}%)") widget = CompactBufferWidgetWithCallback(buffer, on_update=my_callback) widget.update_display() # Callback is called! """ def __init__(self, buffer: CircularBufferHandle, on_update=None, parent=None): """ Args: buffer: CircularBufferHandle to monitor on_update: Callback function(capacity, fill, percent, overflows) parent: Parent widget """ super().__init__(parent) self.buffer = buffer self.on_update_callback = on_update # Same layout as compact version layout = QHBoxLayout() layout.setContentsMargins(2, 2, 2, 2) layout.setSpacing(5) self.setLayout(layout) self.lbl_size = QLabel() self.lbl_size.setMinimumWidth(80) layout.addWidget(self.lbl_size) self.progress = QProgressBar() self.progress.setRange(0, 100) self.progress.setTextVisible(True) self.progress.setFormat("%p%") self.progress.setMaximumHeight(20) layout.addWidget(self.progress, stretch=1) self.lbl_overflow = QLabel() self.lbl_overflow.setMinimumWidth(100) # Wider for "Overflows: X" self.lbl_overflow.setAlignment(Qt.AlignmentFlag.AlignRight) layout.addWidget(self.lbl_overflow) self.update_display() def update_display(self): """ Update display and call callback if set. Callback signature: callback(capacity: int, fill: int, percent: int, overflows: int) """ cap = cb_capacity(self.buffer) fill = cb_fill_bytes(self.buffer) pct = cb_fill_pct(self.buffer) ovf = cb_overflows(self.buffer) # Update UI self.lbl_size.setText(f"{fill}/{cap}B") self.progress.setValue(pct) # Overflow label - just show count self.lbl_overflow.setText(f"Overflows: {ovf}") if ovf > 0: self.lbl_overflow.setStyleSheet("color: red; font-weight: bold;") else: self.lbl_overflow.setStyleSheet("color: gray;") # Call callback if set if self.on_update_callback: try: self.on_update_callback(cap, fill, pct, ovf) except Exception as e: print(f"Callback error: {e}") # ============================================================================= # Demo # ============================================================================= def demo(): """Demo showing both widgets.""" import sys from PyQt6.QtWidgets import ( QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QLabel, QGroupBox ) from circular_buffer import cb_init, cb_write class DemoWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Compact Buffer Widget Demo") central = QWidget() self.setCentralWidget(central) layout = QVBoxLayout() central.setLayout(layout) # Title title = QLabel("Compact One-Line Buffer Widgets") title.setStyleSheet("font-size: 14px; font-weight: bold;") layout.addWidget(title) # ====== Widget 1: Without callback ====== group1 = QGroupBox("Widget 1: Basic (no callback)") group1_layout = QVBoxLayout() group1.setLayout(group1_layout) status1, self.buffer1 = cb_init(100) self.widget1 = CompactBufferWidget(self.buffer1) group1_layout.addWidget(self.widget1) layout.addWidget(group1) # ====== Widget 2: With callback ====== group2 = QGroupBox("Widget 2: With Callback") group2_layout = QVBoxLayout() group2.setLayout(group2_layout) status2, self.buffer2 = cb_init(100) # Define callback def on_buffer_update(cap, fill, pct, ovf): print(f"Callback: {fill}/{cap}B ({pct}%) - Overflows: {ovf}") self.widget2 = CompactBufferWidgetWithCallback( self.buffer2, on_update=on_buffer_update ) group2_layout.addWidget(self.widget2) layout.addWidget(group2) # ====== Controls ====== controls = QGroupBox("Controls") controls_layout = QVBoxLayout() controls.setLayout(controls_layout) btn_write_5 = QPushButton("Write 5 bytes to both") btn_write_5.clicked.connect(self.write_5) controls_layout.addWidget(btn_write_5) btn_write_50 = QPushButton("Write 50 bytes to both") btn_write_50.clicked.connect(self.write_50) controls_layout.addWidget(btn_write_50) btn_update = QPushButton("⟳ Update Displays") btn_update.setStyleSheet("background-color: #4CAF50; color: white;") btn_update.clicked.connect(self.update_all) controls_layout.addWidget(btn_update) layout.addWidget(controls) # Instructions instructions = QLabel( "\n💡 Instructions:\n" "1. Click 'Write X bytes' to add data\n" "2. Click 'Update Displays' to refresh\n" "3. Widget 2 prints callback info to console" ) instructions.setStyleSheet("background-color: #f0f0f0; padding: 5px;") layout.addWidget(instructions) self.resize(500, 400) def write_5(self): cb_write(self.buffer1, b"HELLO") cb_write(self.buffer2, b"HELLO") print("Wrote 5 bytes - click Update to see") def write_50(self): cb_write(self.buffer1, b"X" * 50) cb_write(self.buffer2, b"X" * 50) print("Wrote 50 bytes - click Update to see") def update_all(self): print("\n--- Updating displays ---") self.widget1.update_display() self.widget2.update_display() # This calls the callback! app = QApplication(sys.argv) window = DemoWindow() window.show() print("\n" + "="*60) print("COMPACT BUFFER WIDGET DEMO") print("="*60) print("Widget 1: Basic compact widget") print("Widget 2: Same but with callback function") print("\nClick buttons and watch:") print(" • Compact one-line display") print(" • Manual updates") print(" • Callback prints to console") print("="*60 + "\n") sys.exit(app.exec()) if __name__ == "__main__": demo()