I wanted to make a simple example of multithreading with QThread in PyQt5 / Python3. The script generates a set of QLineEdits and buttons to start and stop a set of threads. Each thread simply increments whatever integer was in the box before the start button is pressed, once per second. Signals and slots are used between the counting threads and the main GUI thread for thread safety. The number of boxes and button sets is assigned in "n" during class declaration.

It seems clunky to make a bunch of threads beforehand. There is some talk of QThreadPool and QRunnables, but it is my understanding that QRunnables cannot use signals and slots, so what can I do there?

import time
import sys
from PyQt5 import QtCore, QtWidgets

class countingWorker(QtCore.QObject):
#explicit signal sent in the startCounting function
set_box_signal = QtCore.pyqtSignal(int,str)

#explicit slot that takes input from the start_counting_signal
@QtCore.pyqtSlot(int,str)
def startCounting(self,boxNumber,boxText):
value = int(boxText)
while True:
value=1+value
self.set_box_signal.emit(boxNumber,str(value))
time.sleep(1)

class firstGui(QtWidgets.QDialog):
#explicit signals from the start_counting_emitter function
n = 7 # make this many counters
start_counting_signal=QtCore.pyqtSignal(int, str)

def __init__(self):
super(firstGui, self).__init__()
self.workers=[]
for j in range(self.n):
self.workers.append(countingWorker())
self.init_ui()

def init_ui(self):
self.boxes=[]
self.starts=[]
self.stops=[]
for j in range(self.n):
# Creating and naming the boxes
self.boxes.append(QtWidgets.QLineEdit(self))
self.boxes[j].setText('0')
self.boxes[j].setObjectName('box'+str(j))
# creating the start buttons
self.starts.append(QtWidgets.QPushButton('Start', self))
self.starts[j].setObjectName('start' + str(j))
self.stops.append(QtWidgets.QPushButton('Stop', self))
self.stops[j].setObjectName('stop' + str(j))

# Creating a Horizontal Layout to add all the widgets
self.hboxLayout = QtWidgets.QHBoxLayout(self)

for j in range(self.n):

# Setting the hBoxLayout as the main layout
self.setLayout(self.hboxLayout)
self.setWindowTitle('gui_4b')

# making all of the connections
for j in range(self.n):
self.starts[j].clicked.connect(self.start_counting_emitter)
self.stops[j].clicked.connect(self.stop_counting)
self.workers[j].set_box_signal.connect(self.set_box_text)

# show the GUI
self.show()

# this is an implicit slot for the startbuttons to connect to
def start_counting_emitter(self):
start_button_name=self.sender().objectName()
start_number=int(start_button_name[5:])
self.start_counting_signal.connect(self.workers[start_number].startCounting)
self.start_counting_signal.emit(start_number,self.boxes[start_number].text())
self.start_counting_signal.disconnect(self.workers[start_number].startCounting)

# implicit slot of the stop buttons
def stop_counting(self):
stop_button_name = self.sender().objectName()
stop_number = int(stop_button_name[4:])

# explicit slot that takes input from the countingWorker and sets the text of the provided box
@QtCore.pyqtSlot(int,str)
def set_box_text(self,boxNumber,boxText):
self.boxes[boxNumber].setText(boxText)

def main():
app = QtWidgets.QApplication(sys.argv)
a = firstGui()
sys.exit(app.exec_())

if __name__ == '__main__':
main()

• It turns out QRunnables actually can use signals and slots. Here is a very good reference: martinfitzpatrick.name/article/… – SDiv Sep 5 '17 at 13:24
• So personally I'm dealing with the same thing and this is one of the best examples I have come across so far! – RMann Jul 14 '19 at 17:16