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/s...als_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?

Qt Code:
  1. import sys
  2. from PyQt5 import QtCore, QtGui, QtWidgets
  3. from PyQt5.QtCore import *
  4. from PyQt5.QtGui import QPen, QPainter, QColor
  5. import string as st
  6. #import time
  7. from threading import Timer
  8. from itertools import count
  9.  
  10. def print_dir(key, alphabetize=True):
  11. cnt = 0
  12. for i in sorted(dir(key)):
  13. print(i, end=", ")
  14. cnt += 1
  15. if cnt % 10 == 0:
  16. print()
  17. print()
  18.  
  19. class GlobalTimer(object):
  20. def __init__(self, interval=1.0):
  21. self._registered = set()
  22. self._interval = interval
  23. self._timer = None
  24. self.isActive = False
  25.  
  26. def register_callback(self, callback):
  27. self._registered.add(callback)
  28.  
  29. def unregister_callback(self, callback):
  30. self._registered.remove(callback)
  31.  
  32. def setInterval(self, interval=2.5):
  33. if interval<0 or interval>10:
  34. return
  35. self._interval = interval
  36.  
  37. def start(self):
  38. self._timer = Timer(self._interval, self._callback)
  39. self._timer.start()
  40. self.isActive = True
  41.  
  42. def stop(self):
  43. self._timer.cancel()
  44. self.isActive = False
  45.  
  46. def _callback(self):
  47. self._timer.cancel()
  48. self.isActive = False
  49. for callback in self._registered:
  50. callback()
  51. #self._start_timer()
  52.  
  53. class ChangeSignals(QObject):
  54.  
  55. increase = pyqtSignal()
  56. trigger = pyqtSignal()
  57.  
  58. # This defines a signal called 'rangeChanged' that takes two
  59. # integer arguments.
  60. range_changed = pyqtSignal(int, int, name='rangeChanged')
  61.  
  62. # This defines a signal called 'valueChanged' that has two overloads,
  63. # one that takes an integer argument and one that takes a QString
  64. # argument. Note that because we use a string to specify the type of
  65. # the QString argument then this code will run under Python v2 and v3.
  66. valueChanged = pyqtSignal([int], ['QString'])
  67.  
  68. #@staticmethod
  69. def connect_trigger(self):
  70. # Connect the trigger signal to a slot.
  71. self.trigger.connect(self.handle_trigger)
  72.  
  73. #@staticmethod
  74. def emit_trigger(self):
  75. # Emit the signal.
  76. self.trigger.emit()
  77.  
  78. sig = ChangeSignals()
  79. sig.connectNotify()
  80.  
  81. class SignalWatchers(QtWidgets.QGraphicsRectItem):
  82. _instance_count = count(0)
  83.  
  84. def __init__(self, chr_num, parent=None):
  85. super(SignalWatchers, self).__init__(parent)
  86. self.chr_num = chr_num
  87. self.count = self._instance_count
  88. self._instance_count = next(self._instance_count)
  89. #print(dir(self._instance_count))
  90.  
  91. self.timer = GlobalTimer(4)
  92. self.timer.register_callback(self.time_callback)
  93.  
  94. self.font_size = 9
  95. print("count = {}".format(self.count))
  96.  
  97. #ChangeSignals.increase.connect(self.signal_handler)
  98. #ChangeSignals.increase.connectNotify(self.signal_handler)
  99. if self._instance_count == 1:
  100. print_dir(ChangeSignals.increase)
  101.  
  102.  
  103. def connect_and_emit_trigger(self):
  104. # Connect the trigger signal to a slot.
  105. self.trigger.connect(self.handle_trigger)
  106.  
  107. # Emit the signal.
  108. self.trigger.emit()
  109.  
  110. @pyqtSlot()
  111. def signal_handler(self):
  112. self.chr_num += 1
  113.  
  114. def time_callback(self):
  115. pass
  116.  
  117. def paint(self, p, opts, widget):
  118. self.paint_me(p)
  119.  
  120. def paint_me(self, p):
  121. p.setRenderHint(QtGui.QPainter.Antialiasing)
  122. txt = chr(self.chr_num)
  123. penWidth = 1
  124. font = QtGui.QFont("Helvetica", 9, QtGui.QFont.Bold)
  125. pen = QtGui.QPen(Qt.black, 1)
  126. p.setPen(pen)
  127. p.setFont(font)
  128. p.drawRoundedRect(self.rect(), 5, 5)
  129. p.drawText(12*(self._instance_count -2), 10, txt)
  130.  
  131.  
  132. class MainWindow(QtWidgets.QMainWindow):
  133. def __init__(self, parent=None):
  134. super(MainWindow, self).__init__(parent)
  135. self.app_width = 605
  136. self.keyboard_height = 220
  137. self.iteration_delay = 50
  138. self.press_cnt = 0
  139. self.setWindowTitle("Signal Test")
  140. self.scene = QtWidgets.QGraphicsScene(self)
  141. self.scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)
  142.  
  143. self.create_central_widget()
  144. self.sigs = {}
  145. for i in range(65,70):
  146. self.sigs[i] = SignalWatchers(i)
  147. self.scene.addItem(self.sigs[i])
  148.  
  149. def handle_signal(self, signal_val):
  150. print("signal_val = {}".format(signal_val))
  151. pass
  152.  
  153.  
  154. def create_layout(self):
  155. self.layout = QtWidgets.QGraphicsLinearLayout()
  156. self.widget = QtWidgets.QGraphicsWidget()
  157. self.widget.setLayout(self.layout)
  158. self.scene.addItem(self.widget)
  159. width = self.app_width
  160. height = self.keyboard_height
  161. self.setMinimumSize(width, height)
  162. self.scene.setSceneRect(0, 0, width, height)
  163.  
  164. def create_central_widget(self):
  165. self.view = QtWidgets.QGraphicsView(self.scene)
  166. self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
  167. self.view.setBackgroundBrush((QColor("bisque")))
  168. self.setCentralWidget(self.view)
  169. scale_val = 1
  170. self.view.scale(scale_val, scale_val)
  171.  
  172. def main():
  173. app = QtWidgets.QApplication(sys.argv)
  174. w = MainWindow()
  175. w.show()
  176. ret = app.exec_()
  177. sys.exit(ret)
  178.  
  179. if __name__ == "__main__":
  180.  
  181. main()
To copy to clipboard, switch view to plain text mode