Signal Patterns¶
Qt signal-based interactivity patterns.
Basic Signal Connection¶
# Connect signals to handlers
button.clicked.connect(self.on_button_click)
table.row_clicked.connect(lambda idx, data: self.select_row(idx))
search.text_changed.connect(self.filter_results)
Lambda Functions¶
Use lambdas for inline handlers or to pass additional data:
# Pass additional context
for i, item in enumerate(items):
button = Button(item.name)
button.clicked.connect(lambda checked, idx=i: self.select_item(idx))
layout.addWidget(button)
Signal Forwarding¶
Forward signals to parent components:
from PySide6.QtCore import Signal
class FilePanel(Panel):
file_selected = Signal(str) # Forward selection
def __init__(self):
super().__init__()
self.file_list = FileList()
self.file_list.item_clicked.connect(self.file_selected.emit)
self.add_widget(self.file_list)
Custom Signals¶
Define custom signals for component communication:
from PySide6.QtCore import QObject, Signal
class DataManager(QObject):
data_loaded = Signal(list)
data_error = Signal(str)
loading_started = Signal()
loading_finished = Signal()
def load_data(self):
self.loading_started.emit()
try:
data = fetch_data()
self.data_loaded.emit(data)
except Exception as e:
self.data_error.emit(str(e))
finally:
self.loading_finished.emit()
Signal Disconnection¶
Disconnect signals when needed:
class DynamicView(Panel):
def __init__(self):
super().__init__()
self.current_handler = None
def set_handler(self, handler):
# Disconnect previous handler
if self.current_handler:
self.button.clicked.disconnect(self.current_handler)
# Connect new handler
self.current_handler = handler
self.button.clicked.connect(handler)
Debouncing¶
Debounce rapid signals (e.g., search input):
from PySide6.QtCore import QTimer
class SearchPanel(Panel):
def __init__(self):
super().__init__()
self.search = SearchBox()
self.search.text_changed.connect(self.on_text_changed)
self.debounce_timer = QTimer()
self.debounce_timer.setSingleShot(True)
self.debounce_timer.timeout.connect(self.execute_search)
self.pending_query = ""
def on_text_changed(self, text):
self.pending_query = text
self.debounce_timer.start(300) # 300ms debounce
def execute_search(self):
self.perform_search(self.pending_query)
Event Bus Pattern¶
Central event bus for application-wide communication:
from PySide6.QtCore import QObject, Signal
class EventBus(QObject):
_instance = None
# Application events
theme_changed = Signal(str)
project_loaded = Signal(str)
file_opened = Signal(str)
code_applied = Signal(str, str, int, int)
@classmethod
def instance(cls):
if cls._instance is None:
cls._instance = EventBus()
return cls._instance
# Usage
EventBus.instance().theme_changed.connect(self.on_theme_changed)
EventBus.instance().theme_changed.emit("dark")
Signal Best Practices¶
Do¶
- Use descriptive signal names (
item_selected, notclicked) - Include relevant data in signal parameters
- Disconnect signals when components are destroyed
- Use
blockSignals()when updating programmatically
Don't¶
- Connect signals in loops without proper cleanup
- Emit signals during initialization (use
QTimer.singleShot) - Create circular signal connections
- Ignore signal parameters that provide context