illuzioner1
1st May 2020, 02:35
Hi,
I've read conflicting blog and StackOverflow (SO) posts about PyQt5's signal & slot syntax. This site is supposed to be the reference:
https://www.riverbankcomputing.com/static/Docs/PyQt4/new_style_signals_slots.html
but their examples don't work, at least not in the way I'm trying them. I'm working in PyQt5, but the syntax should be exactly the same according to several posts on SO. I'm just trying to connect a bunch of items to a single signal, but the syntax just doesn't work as it keeps telling me there is no 'connect'. Some posts mention connectNotify, but I've tried that too with no success. Here is a small example of what I'd like to do.
I have a signal 'increase' as a class variable in class 'ChangeSignals', just like in the reference above. However, I want a different class, 'SignalWatchers' to connect to the 'increase' signal and call the SignalWatchers callback 'signal_handler' when 'increase' is emitted.
What is the proper way to construct this?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import QPen, QPainter, QColor
import string as st
#import time
from threading import Timer
from itertools import count
def print_dir(key, alphabetize=True):
cnt = 0
for i in sorted(dir(key)):
print(i, end=", ")
cnt += 1
if cnt % 10 == 0:
print()
print()
class GlobalTimer(object):
def __init__(self, interval=1.0):
self._registered = set()
self._interval = interval
self._timer = None
self.isActive = False
def register_callback(self, callback):
self._registered.add(callback)
def unregister_callback(self, callback):
self._registered.remove(callback)
def setInterval(self, interval=2.5):
if interval<0 or interval>10:
return
self._interval = interval
def start(self):
self._timer = Timer(self._interval, self._callback)
self._timer.start()
self.isActive = True
def stop(self):
self._timer.cancel()
self.isActive = False
def _callback(self):
self._timer.cancel()
self.isActive = False
for callback in self._registered:
callback()
#self._start_timer()
class ChangeSignals(QObject):
increase = pyqtSignal()
trigger = pyqtSignal()
# This defines a signal called 'rangeChanged' that takes two
# integer arguments.
range_changed = pyqtSignal(int, int, name='rangeChanged')
# This defines a signal called 'valueChanged' that has two overloads,
# one that takes an integer argument and one that takes a QString
# argument. Note that because we use a string to specify the type of
# the QString argument then this code will run under Python v2 and v3.
valueChanged = pyqtSignal([int], ['QString'])
#@staticmethod
def connect_trigger(self):
# Connect the trigger signal to a slot.
self.trigger.connect(self.handle_trigger)
#@staticmethod
def emit_trigger(self):
# Emit the signal.
self.trigger.emit()
sig = ChangeSignals()
sig.connectNotify()
class SignalWatchers(QtWidgets.QGraphicsRectItem):
_instance_count = count(0)
def __init__(self, chr_num, parent=None):
super(SignalWatchers, self).__init__(parent)
self.chr_num = chr_num
self.count = self._instance_count
self._instance_count = next(self._instance_count)
#print(dir(self._instance_count))
self.timer = GlobalTimer(4)
self.timer.register_callback(self.time_callback)
self.font_size = 9
print("count = {}".format(self.count))
#ChangeSignals.increase.connect(self.signal_handle r)
#ChangeSignals.increase.connectNotify(self.signal_ handler)
if self._instance_count == 1:
print_dir(ChangeSignals.increase)
def connect_and_emit_trigger(self):
# Connect the trigger signal to a slot.
self.trigger.connect(self.handle_trigger)
# Emit the signal.
self.trigger.emit()
@pyqtSlot()
def signal_handler(self):
self.chr_num += 1
def time_callback(self):
pass
def paint(self, p, opts, widget):
self.paint_me(p)
def paint_me(self, p):
p.setRenderHint(QtGui.QPainter.Antialiasing)
txt = chr(self.chr_num)
penWidth = 1
font = QtGui.QFont("Helvetica", 9, QtGui.QFont.Bold)
pen = QtGui.QPen(Qt.black, 1)
p.setPen(pen)
p.setFont(font)
p.drawRoundedRect(self.rect(), 5, 5)
p.drawText(12*(self._instance_count -2), 10, txt)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.app_width = 605
self.keyboard_height = 220
self.iteration_delay = 50
self.press_cnt = 0
self.setWindowTitle("Signal Test")
self.scene = QtWidgets.QGraphicsScene(self)
self.scene.setItemIndexMethod(QtWidgets.QGraphicsS cene.NoIndex)
self.create_central_widget()
self.sigs = {}
for i in range(65,70):
self.sigs[i] = SignalWatchers(i)
self.scene.addItem(self.sigs[i])
def handle_signal(self, signal_val):
print("signal_val = {}".format(signal_val))
pass
def create_layout(self):
self.layout = QtWidgets.QGraphicsLinearLayout()
self.widget = QtWidgets.QGraphicsWidget()
self.widget.setLayout(self.layout)
self.scene.addItem(self.widget)
width = self.app_width
height = self.keyboard_height
self.setMinimumSize(width, height)
self.scene.setSceneRect(0, 0, width, height)
def create_central_widget(self):
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
self.view.setBackgroundBrush((QColor("bisque")))
self.setCentralWidget(self.view)
scale_val = 1
self.view.scale(scale_val, scale_val)
def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
I've read conflicting blog and StackOverflow (SO) posts about PyQt5's signal & slot syntax. This site is supposed to be the reference:
https://www.riverbankcomputing.com/static/Docs/PyQt4/new_style_signals_slots.html
but their examples don't work, at least not in the way I'm trying them. I'm working in PyQt5, but the syntax should be exactly the same according to several posts on SO. I'm just trying to connect a bunch of items to a single signal, but the syntax just doesn't work as it keeps telling me there is no 'connect'. Some posts mention connectNotify, but I've tried that too with no success. Here is a small example of what I'd like to do.
I have a signal 'increase' as a class variable in class 'ChangeSignals', just like in the reference above. However, I want a different class, 'SignalWatchers' to connect to the 'increase' signal and call the SignalWatchers callback 'signal_handler' when 'increase' is emitted.
What is the proper way to construct this?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import QPen, QPainter, QColor
import string as st
#import time
from threading import Timer
from itertools import count
def print_dir(key, alphabetize=True):
cnt = 0
for i in sorted(dir(key)):
print(i, end=", ")
cnt += 1
if cnt % 10 == 0:
print()
print()
class GlobalTimer(object):
def __init__(self, interval=1.0):
self._registered = set()
self._interval = interval
self._timer = None
self.isActive = False
def register_callback(self, callback):
self._registered.add(callback)
def unregister_callback(self, callback):
self._registered.remove(callback)
def setInterval(self, interval=2.5):
if interval<0 or interval>10:
return
self._interval = interval
def start(self):
self._timer = Timer(self._interval, self._callback)
self._timer.start()
self.isActive = True
def stop(self):
self._timer.cancel()
self.isActive = False
def _callback(self):
self._timer.cancel()
self.isActive = False
for callback in self._registered:
callback()
#self._start_timer()
class ChangeSignals(QObject):
increase = pyqtSignal()
trigger = pyqtSignal()
# This defines a signal called 'rangeChanged' that takes two
# integer arguments.
range_changed = pyqtSignal(int, int, name='rangeChanged')
# This defines a signal called 'valueChanged' that has two overloads,
# one that takes an integer argument and one that takes a QString
# argument. Note that because we use a string to specify the type of
# the QString argument then this code will run under Python v2 and v3.
valueChanged = pyqtSignal([int], ['QString'])
#@staticmethod
def connect_trigger(self):
# Connect the trigger signal to a slot.
self.trigger.connect(self.handle_trigger)
#@staticmethod
def emit_trigger(self):
# Emit the signal.
self.trigger.emit()
sig = ChangeSignals()
sig.connectNotify()
class SignalWatchers(QtWidgets.QGraphicsRectItem):
_instance_count = count(0)
def __init__(self, chr_num, parent=None):
super(SignalWatchers, self).__init__(parent)
self.chr_num = chr_num
self.count = self._instance_count
self._instance_count = next(self._instance_count)
#print(dir(self._instance_count))
self.timer = GlobalTimer(4)
self.timer.register_callback(self.time_callback)
self.font_size = 9
print("count = {}".format(self.count))
#ChangeSignals.increase.connect(self.signal_handle r)
#ChangeSignals.increase.connectNotify(self.signal_ handler)
if self._instance_count == 1:
print_dir(ChangeSignals.increase)
def connect_and_emit_trigger(self):
# Connect the trigger signal to a slot.
self.trigger.connect(self.handle_trigger)
# Emit the signal.
self.trigger.emit()
@pyqtSlot()
def signal_handler(self):
self.chr_num += 1
def time_callback(self):
pass
def paint(self, p, opts, widget):
self.paint_me(p)
def paint_me(self, p):
p.setRenderHint(QtGui.QPainter.Antialiasing)
txt = chr(self.chr_num)
penWidth = 1
font = QtGui.QFont("Helvetica", 9, QtGui.QFont.Bold)
pen = QtGui.QPen(Qt.black, 1)
p.setPen(pen)
p.setFont(font)
p.drawRoundedRect(self.rect(), 5, 5)
p.drawText(12*(self._instance_count -2), 10, txt)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.app_width = 605
self.keyboard_height = 220
self.iteration_delay = 50
self.press_cnt = 0
self.setWindowTitle("Signal Test")
self.scene = QtWidgets.QGraphicsScene(self)
self.scene.setItemIndexMethod(QtWidgets.QGraphicsS cene.NoIndex)
self.create_central_widget()
self.sigs = {}
for i in range(65,70):
self.sigs[i] = SignalWatchers(i)
self.scene.addItem(self.sigs[i])
def handle_signal(self, signal_val):
print("signal_val = {}".format(signal_val))
pass
def create_layout(self):
self.layout = QtWidgets.QGraphicsLinearLayout()
self.widget = QtWidgets.QGraphicsWidget()
self.widget.setLayout(self.layout)
self.scene.addItem(self.widget)
width = self.app_width
height = self.keyboard_height
self.setMinimumSize(width, height)
self.scene.setSceneRect(0, 0, width, height)
def create_central_widget(self):
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
self.view.setBackgroundBrush((QColor("bisque")))
self.setCentralWidget(self.view)
scale_val = 1
self.view.scale(scale_val, scale_val)
def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()