04_signals_slots.py
Python/UI_APP/pyside6_examples/01_beginner/04_signals_slots.py
"""
PySide6 Beginner Example 04: Signals and Slots
==============================================
This example demonstrates:
- Connecting signals to slot methods
- Using lambda functions for connections
- Custom signals with Signal class
- Passing data through signals
- Disconnecting signals
Run: python 04_signals_slots.py
"""
import sys
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QLineEdit, QSlider, QProgressBar,
QSpinBox, QGroupBox, QTextEdit
)
from PySide6.QtCore import Qt, Signal, QObject
class DataEmitter(QObject):
"""Custom class that emits signals with data."""
# Define custom signals
message_sent = Signal(str)
progress_updated = Signal(int)
calculation_done = Signal(float, float, float) # a, b, result
def send_message(self, text):
"""Send a message signal."""
self.message_sent.emit(text)
def update_progress(self, value):
"""Update progress signal."""
self.progress_updated.emit(value)
def calculate(self, a, b):
"""Perform calculation and emit result."""
result = a + b
self.calculation_done.emit(a, b, result)
class SignalSlotsDemo(QMainWindow):
"""Demonstrates signals and slots mechanism."""
# Custom signal for this class
counter_changed = Signal(int)
def __init__(self):
super().__init__()
self.setWindowTitle("Signals and Slots Demo")
self.setMinimumSize(600, 600)
self.click_count = 0
self.emitter = DataEmitter()
central = QWidget()
self.setCentralWidget(central)
layout = QVBoxLayout(central)
layout.addWidget(self.create_basic_signals_group())
layout.addWidget(self.create_slider_signals_group())
layout.addWidget(self.create_custom_signals_group())
layout.addWidget(self.create_log_group())
# Connect custom signals
self.setup_connections()
def create_basic_signals_group(self):
"""Basic signal connections."""
group = QGroupBox("Basic Signals")
layout = QVBoxLayout(group)
# Button signals
btn_layout = QHBoxLayout()
# Method connection
self.count_btn = QPushButton("Click me (0)")
self.count_btn.clicked.connect(self.on_button_clicked)
btn_layout.addWidget(self.count_btn)
# Lambda connection
reset_btn = QPushButton("Reset")
reset_btn.clicked.connect(lambda: self.reset_counter())
btn_layout.addWidget(reset_btn)
layout.addLayout(btn_layout)
# Text input signals
text_layout = QHBoxLayout()
text_layout.addWidget(QLabel("Type here:"))
self.text_input = QLineEdit()
self.text_input.setPlaceholderText("Text changes are tracked...")
# Connect to multiple slots
self.text_input.textChanged.connect(self.on_text_changed)
self.text_input.returnPressed.connect(self.on_return_pressed)
text_layout.addWidget(self.text_input)
layout.addLayout(text_layout)
# Display
self.text_display = QLabel("You typed: ")
layout.addWidget(self.text_display)
return group
def create_slider_signals_group(self):
"""Slider and spinbox synchronization."""
group = QGroupBox("Synchronized Widgets (Slider ↔ SpinBox)")
layout = QVBoxLayout(group)
sync_layout = QHBoxLayout()
# Slider
self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
sync_layout.addWidget(self.slider)
# SpinBox
self.spinbox = QSpinBox()
self.spinbox.setRange(0, 100)
self.spinbox.setValue(50)
sync_layout.addWidget(self.spinbox)
# Progress bar
self.progress = QProgressBar()
self.progress.setRange(0, 100)
self.progress.setValue(50)
sync_layout.addWidget(self.progress)
# Connect them together (bidirectional)
self.slider.valueChanged.connect(self.spinbox.setValue)
self.slider.valueChanged.connect(self.progress.setValue)
self.spinbox.valueChanged.connect(self.slider.setValue)
layout.addLayout(sync_layout)
# Value label
self.value_label = QLabel("Current value: 50")
self.slider.valueChanged.connect(
lambda v: self.value_label.setText(f"Current value: {v}")
)
layout.addWidget(self.value_label)
return group
def create_custom_signals_group(self):
"""Custom signal demonstrations."""
group = QGroupBox("Custom Signals")
layout = QVBoxLayout(group)
# Message signal
msg_layout = QHBoxLayout()
self.msg_input = QLineEdit()
self.msg_input.setPlaceholderText("Enter a message...")
msg_layout.addWidget(self.msg_input)
send_btn = QPushButton("Send Message")
send_btn.clicked.connect(self.send_message)
msg_layout.addWidget(send_btn)
layout.addLayout(msg_layout)
# Calculation signal
calc_layout = QHBoxLayout()
calc_layout.addWidget(QLabel("Calculate:"))
self.num_a = QSpinBox()
self.num_a.setRange(-100, 100)
self.num_a.setValue(5)
calc_layout.addWidget(self.num_a)
calc_layout.addWidget(QLabel("+"))
self.num_b = QSpinBox()
self.num_b.setRange(-100, 100)
self.num_b.setValue(3)
calc_layout.addWidget(self.num_b)
calc_btn = QPushButton("=")
calc_btn.clicked.connect(self.do_calculation)
calc_layout.addWidget(calc_btn)
self.result_label = QLabel("?")
self.result_label.setStyleSheet("font-weight: bold; min-width: 50px;")
calc_layout.addWidget(self.result_label)
calc_layout.addStretch()
layout.addLayout(calc_layout)
return group
def create_log_group(self):
"""Log display."""
group = QGroupBox("Event Log")
layout = QVBoxLayout(group)
self.log_display = QTextEdit()
self.log_display.setReadOnly(True)
self.log_display.setMaximumHeight(150)
layout.addWidget(self.log_display)
clear_btn = QPushButton("Clear Log")
clear_btn.clicked.connect(self.log_display.clear)
layout.addWidget(clear_btn)
return group
def setup_connections(self):
"""Set up custom signal connections."""
# Connect custom signals from emitter
self.emitter.message_sent.connect(self.on_message_received)
self.emitter.calculation_done.connect(self.on_calculation_done)
# Connect class signal
self.counter_changed.connect(self.on_counter_changed)
def log(self, message):
"""Add message to log."""
self.log_display.append(message)
# Slot methods
def on_button_clicked(self):
"""Handle button click."""
self.click_count += 1
self.count_btn.setText(f"Click me ({self.click_count})")
self.counter_changed.emit(self.click_count)
self.log(f"Button clicked! Count: {self.click_count}")
def reset_counter(self):
"""Reset the counter."""
self.click_count = 0
self.count_btn.setText("Click me (0)")
self.counter_changed.emit(0)
self.log("Counter reset to 0")
def on_text_changed(self, text):
"""Handle text change."""
self.text_display.setText(f"You typed: {text}")
def on_return_pressed(self):
"""Handle Enter key press."""
text = self.text_input.text()
self.log(f"Enter pressed! Text: '{text}'")
self.text_input.clear()
def on_counter_changed(self, value):
"""Handle counter change signal."""
if value > 0 and value % 5 == 0:
self.log(f"🎉 Counter milestone reached: {value}")
def send_message(self):
"""Send message through custom signal."""
text = self.msg_input.text()
if text:
self.emitter.send_message(text)
self.msg_input.clear()
def on_message_received(self, message):
"""Handle received message."""
self.log(f"📨 Message received: '{message}'")
def do_calculation(self):
"""Trigger calculation."""
a = self.num_a.value()
b = self.num_b.value()
self.emitter.calculate(a, b)
def on_calculation_done(self, a, b, result):
"""Handle calculation result."""
self.result_label.setText(str(int(result)))
self.log(f"🔢 Calculation: {a} + {b} = {result}")
def main():
app = QApplication(sys.argv)
window = SignalSlotsDemo()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
関連記事
01_getting_started.py
01_getting_started.py — python source code from the Python learning materials (Python/01_beginner_level/01_getting_started.py).
記事を読む →02_core_syntax.py
02_core_syntax.py — python source code from the Python learning materials (Python/01_beginner_level/02_core_syntax.py).
記事を読む →03_control_flow.py
03_control_flow.py — python source code from the Python learning materials (Python/01_beginner_level/03_control_flow.py).
記事を読む →04_data_structures.py
04_data_structures.py — python source code from the Python learning materials (Python/01_beginner_level/04_data_structures.py).
記事を読む →05_functions.py
05_functions.py — python source code from the Python learning materials (Python/01_beginner_level/05_functions.py).
記事を読む →06_error_handling.py
06_error_handling.py — python source code from the Python learning materials (Python/01_beginner_level/06_error_handling.py).
記事を読む →