You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
9.9 KiB
294 lines
9.9 KiB
#!/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()
|