Designing and Handling Custom Signals in PyQt5 with QTableView
Custom signals in PyQt5 are declared on the class, emitted from instance methods, and connected to callables (slots) that accept matching arguments.
- Declaration: my_signal = pyqtSignal(type1, type2, ...)
- Emission: self.my_signal.emit(value1, value2, ...)
- Connection: instance.my_signal.connect(receiver)
Examples of valid signal signatures:
from PyQt5.QtCore import pyqtSignal
valueReady = pyqtSignal(int)
progressChanged = pyqtSignal(float)
textAndCount = pyqtSignal(str, int)
itemsUpdated = pyqtSignal(list)
Subclassing QTableView and declarnig a custom signal
Emit a string containing the text from the first column of the double-clicked row, regardless of which column was clicked.
from PyQt5.QtCore import pyqtSignal, QModelIndex
from PyQt5.QtWidgets import QTableView
class ClickableTable(QTableView):
firstColumnText = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
# Connect the built-in signal to our handler
self.doubleClicked.connect(self._on_double_clicked)
def _on_double_clicked(self, index: QModelIndex):
# Repoint to the first column in the same row
first_col_index = index.sibling(index.row(), 0)
value = first_col_index.data()
if value is None:
return # no data in the model for that cell
self.firstColumnText.emit(str(value))
Notes:
- Signals must be defined as class attributes (not inside init).
- doubleClicked provides a QModelIndex; a compatible slot must accept the same parameter type.
Connecting to the custom signal
Any callable with a compatible signature can receive the emitted value.
view = ClickableTable()
view.firstColumnText.connect(print) # prints the first-column text on double-click
Parameter matching rules
The number and order of emitted values must match the signal delcaration, and receivers must accept the same number of parameters in the same order.
from PyQt5.QtCore import pyqtSignal
multi = pyqtSignal(str, float, int)
def handle_multi(a, b, c):
# a: str, b: float, c: int
pass
# later
# instance.multi.connect(handle_multi)
# instance.multi.emit("ok", 1.5, 3)
Complete runnable example
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWidgets import QTableView
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtCore import pyqtSignal, QModelIndex
class ClickableTable(QTableView):
firstColumnText = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.doubleClicked.connect(self._on_double_clicked)
def _on_double_clicked(self, index: QModelIndex):
idx0 = index.sibling(index.row(), 0)
data = idx0.data()
if data is None:
return
self.firstColumnText.emit(str(data))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Signal with QTableView")
self._setup()
def _setup(self):
table = ClickableTable(self)
model = QStandardItemModel(3, 3)
for r in range(3):
for c in range(3):
model.setItem(r, c, QStandardItem(str(r + c)))
table.setModel(model)
table.firstColumnText.connect(print)
self.setCentralWidget(table)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())