Académique Documents
Professionnel Documents
Culture Documents
PyQt QThreadPool
Résumé : dans ce tutoriel, vous apprendrez à créer une application multithreading PyQt qui utilise
et classe. QThreadPool QRunnable
En outre, la création de threads est assez coûteuse en termes de ressources informatiques. Par
conséquent, le programme doit réutiliser autant que possible les threads créés.
L’utilisation de la classe pour gérer les threads de travail présente donc deux défis principaux
: QThread
Heureusement, PyQt a une classe qui résout ces défis pour vous. La classe est souvent utilisée avec
la classe. QThreadPool QThreadPool QRunnable
La classe représente une tâche que vous souhaitez exécuter dans un thread de
travail. QRunnable
Pour utiliser les classes and, procédez comme suit : QThreadPool QRunnable
https://www.pythontutorial.net/pyqt/qthreadpool/ 1/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
class Worker(QRunnable):
@Slot()
def run(self):
# place a long-running task here
pass
Ensuite, accédez au pool de threads à partir de la fenêtre principale et démarrez les threads de
travail :
class MainWindow(QMainWindow):
# other methods
# ...
def start(self):
""" Create and execute worker threads
"""
pool = QThreadPool.globalInstance()
for _ in range(1, 100):
pool.start(Worker())
Pour mettre à jour la progression du collaborateur vers le thread principal, vous utilisez des signaux
et des emplacements. Cependant, le ne prend pas en charge le signal. QRunnable
Par conséquent, vous devez définir une classe distincte qui hérite de la et utilise cette classe dans la
classe Worker. Voici les étapes : QObject
class Signals(QObject):
completed = Signal()
Dans la classe, nous définissons un signal appelé . Notez que vous pouvez définir autant de signaux
que nécessaire. Signals completed
https://www.pythontutorial.net/pyqt/qthreadpool/ 2/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
Deuxièmement, émettez le signal lorsque le travail est terminé dans la classe: completed Worker
class Runnable(QRunnable):
def __init__(self):
super().__init__()
self.signals = Signals()
@Slot()
def run(self):
# long running task
# ...
# emit the completed signal
self.signals.completed.emit()
class MainWindow(QMainWindow):
# other methods
# ...
def start(self):
""" Create and execute worker threads
"""
pool = QThreadPool.globalInstance()
for _ in range(1, 100):
worker = Worker()
worker.signals.completed.connect(self.update)
pool.start(worker)
def update(self):
# update the worker
pass
Exemple QThreadPool
https://www.pythontutorial.net/pyqt/qthreadpool/ 3/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
import sys
import time
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QGridLayou
from PyQt6.QtCore import QRunnable, QObject, QThreadPool, pyqtSignal as Signal
class Signals(QObject):
started = Signal(int)
completed = Signal(int)
class Worker(QRunnable):
def __init__(self, n):
super().__init__()
self.n = n
self.signals = Signals()
@Slot()
def run(self):
self.signals.started.emit(self.n)
time.sleep(self.n*1.1)
self.signals.completed.emit(self.n)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('QThreadPool Demo')
self.job_count = 10
self.comleted_jobs = []
widget = QWidget()
https://www.pythontutorial.net/pyqt/qthreadpool/ 4/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
widget.setLayout(QGridLayout())
self.setCentralWidget(widget)
widget.layout().addWidget(self.list, 0, 0, 1, 2)
widget.layout().addWidget(self.progress_bar, 1, 0)
widget.layout().addWidget(self.btn_start, 1, 1)
self.show()
def start_jobs(self):
self.restart()
pool = QThreadPool.globalInstance()
for i in range(1, self.job_count+1):
worker = Worker(i)
worker.signals.completed.connect(self.complete)
worker.signals.started.connect(self.start)
pool.start(worker)
def restart(self):
self.progress_bar.setValue(0)
self.comleted_jobs = []
self.btn_start.setEnabled(False)
if len(self.comleted_jobs) == self.job_count:
https://www.pythontutorial.net/pyqt/qthreadpool/ 5/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
self.btn_start.setEnabled(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())
if len(self.comleted_jobs) == self.job_count:
self.btn_start.setEnabled(True)
Sortie:
Classe Signals
https://www.pythontutorial.net/pyqt/qthreadpool/ 6/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
Définissez la classe Signals qui hérite de la classe pour prendre en charge les signaux. Dans la
classe, nous définissons deux signaux : QObject Signals
class Signals(QObject):
started = Signal(int)
completed = Signal(int)
Catégorie ouvrière
La classe hérite de la classe. La classe représente une tâche de longue durée que nous déchargeons
vers un thread de travail : Worker QRunnable Worker
class Worker(QRunnable):
def __init__(self, n):
super().__init__()
self.n = n
self.signals = Signals()
@Slot()
def run(self):
self.signals.started.emit(self.n)
time.sleep(self.n*1.1)
self.signals.completed.emit(self.n)
Tout d’abord, initialisez le numéro de tâche (n) et l’objet Signals dans la méthode. __init__()
Deuxièmement, remplacez la méthode de la classe. Pour simuler une tâche de longue durée, nous
utilisons la fonction du module de temps. Avant de démarrer la minuterie, nous émettons le signal
de démarrage; Une fois la minuterie terminée, nous émettons le signal
terminé. run() QRunnable sleep()
https://www.pythontutorial.net/pyqt/qthreadpool/ 7/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
MainWindow, classe
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('QThreadPool Demo')
self.comleted_jobs = []
self.job_count = 10
widget = QWidget()
widget.setLayout(QGridLayout())
self.setCentralWidget(widget)
widget.layout().addWidget(self.list, 0, 0, 1, 2)
widget.layout().addWidget(self.progress_bar, 1, 0)
widget.layout().addWidget(self.btn_start, 1, 1)
self.show()
def start_jobs(self):
self.restart()
pool = QThreadPool.globalInstance()
for i in range(1, self.job_count+1):
runnable = Worker(i)
runnable.signals.completed.connect(self.complete)
runnable.signals.started.connect(self.start)
pool.start(runnable)
https://www.pythontutorial.net/pyqt/qthreadpool/ 8/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
def restart(self):
self.progress_bar.setValue(0)
self.comleted_jobs = []
self.btn_start.setEnabled(False)
if len(self.comleted_jobs) == self.job_count:
self.btn_start.setEnabled(True)
self.job_count = 10
self.comleted_jobs = []
Ensuite, définissez la méthode qui sera exécutée lorsque l’utilisateur cliquera sur le bouton
Démarrer : start_jobs()
def start_jobs(self):
self.restart()
pool = QThreadPool.globalInstance()
for i in range(1, self.job_count+1):
worker = Worker(i)
worker.signals.completed.connect(self.complete)
worker.signals.started.connect(self.start)
pool.start(worker)
https://www.pythontutorial.net/pyqt/qthreadpool/ 9/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
def restart(self):
self.progress_bar.setValue(0)
self.comleted_jobs = []
self.btn_start.setEnabled(False)
pool = QThreadPool.globalInstance()
Nous créons un certain nombre de travailleurs, connectons leurs signaux aux méthodes de la classe
et démarrons des threads de travail à l’aide de la méthode de
l’objet. MainWindow start() QThreadPool
La méthode s’exécute chaque fois qu’un thread de production est terminé. Il ajoute un message au
, met à jour la barre de progression et active le bouton Démarrer si tous les threads de travail sont
terminés : completed() QListWidget
if len(self.comleted_jobs) == self.job_count:
self.btn_start.setEnabled(True)
https://www.pythontutorial.net/pyqt/qthreadpool/ 10/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
Le programme de cotation en bourse suivant lit les symboles boursiers du fichier et utilise pour
obtenir les prix des actions sur le site Web de Yahoo Finance: symbols.txt QThreadPool
import sys
from pathlib import Path
class Signals(QObject):
completed = Signal(dict)
https://www.pythontutorial.net/pyqt/qthreadpool/ 11/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
class Stock(QRunnable):
BASE_URL = 'https://finance.yahoo.com/quote/'
@Slot()
def run(self):
stock_url = f'{self.BASE_URL}{self.symbol}'
response = requests.get(stock_url)
if response.status_code != 200:
self.signal.completed.emit({'symbol': self.symbol, 'price': 'N/A'}
return
tree = html.fromstring(response.text)
price_text = tree.xpath(
'//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]
)
if not price_text:
self.signal.completed.emit({'symbol': self.symbol, 'price': 'N/A'}
return
class Window(QMainWindow):
def __init__(self, filename, *args, **kwargs):
super().__init__(*args, **kwargs)
https://www.pythontutorial.net/pyqt/qthreadpool/ 12/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
self.symbols = self.read_symbols(filename)
self.results = []
self.setWindowTitle('Stock Listing')
self.setGeometry(100, 100, 400, 300)
self.setWindowIcon(QIcon('./assets/stock.png'))
widget = QWidget()
widget.setLayout(QGridLayout())
self.setCentralWidget(widget)
self.table.setHorizontalHeaderLabels(['Symbol', 'Price'])
widget.layout().addWidget(self.table, 0, 0, 1, 2)
widget.layout().addWidget(self.progress_bar, 1, 0)
widget.layout().addWidget(self.btn_start, 1, 1)
https://www.pythontutorial.net/pyqt/qthreadpool/ 13/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
text = path.read_text()
return [symbol.strip() for symbol in text.split('\n')]
def reset_ui(self):
self.progress_bar.setValue(1)
self.table.setRowCount(0)
def get_prices(self):
# reset ui
self.reset_ui()
if __name__ == '__main__':
app = QApplication(sys.argv)
https://www.pythontutorial.net/pyqt/qthreadpool/ 14/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
window = Window('symbols.txt')
sys.exit(app.exec())
Comment ça marche.
Classe Signals
Nous définissons la classe qui est une sous-classe du . La classe Signals a une variable de classe
terminée qui est une instance de la classe. Signals QObject Signal
Le signal terminé contient un dictionnaire et est émis une fois que le programme a terminé
d’obtenir le cours de l’action.
class Signals(QObject):
completed = Signal(dict)
Classe d’actions
La classe hérite de la classe. Il remplace la méthode qui obtient le cours de l’action sur le site Web
de Yahoo Finance. STock QRunnable run()
Une fois terminée, la méthode émet le signal terminé avec le symbole boursier et le prix. run()
Si une erreur se produit comme si le symbole est introuvable ou si le site Web modifie la façon
dont il affiche le cours de l’action, la méthode renvoie le symbole avec le prix sous forme de chaîne
N/A. run()
class Stock(QRunnable):
BASE_URL = 'https://finance.yahoo.com/quote/'
@Slot()
def run(self):
https://www.pythontutorial.net/pyqt/qthreadpool/ 15/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
stock_url = f'{self.BASE_URL}{self.symbol}'
response = requests.get(stock_url)
if response.status_code != 200:
self.signal.completed.emit({'symbol': self.symbol, 'price': 'N/A'}
return
tree = html.fromstring(response.text)
price_text = tree.xpath(
'//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]
)
if not price_text:
self.signal.completed.emit({'symbol': self.symbol, 'price': 'N/A'}
return
Notez que Yahoo Finance peut modifier sa structure. Pour que le programme fonctionne, vous
devez changer le XPath du prix du nouveau:
//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]/text()
MainWindow, classe
Tout d’abord, lisez les symboles d’un fichier et affectez-les aux variables : self.symbols
self.symbols = self.read_symbols(filename)
https://www.pythontutorial.net/pyqt/qthreadpool/ 16/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
text = path.read_text()
return [symbol.strip() for symbol in text.split('\n')]
AAPL
MSFT
GOOG
AMZN
TSLA
META
NVDA
BABA
CRM
INTC
PYPL
AMD
ATVI
EA
TTD
ORCL
Deuxièmement, définissez le qui utilise le pour créer des threads de travail pour obtenir les cours
des actions: get_prices QThreadPool
def get_prices(self):
# reset ui
self.reset_ui()
https://www.pythontutorial.net/pyqt/qthreadpool/ 17/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
stock.signal.completed.connect(self.update)
pool.start(stock)
La méthode efface toutes les lignes de la et définit la barre de progression sur sa valeur minimale
: reset_ui() QTableWidget
def reset_ui(self):
self.table.setRowCount(0)
self.progress_bar.setValue(1)
Troisièmement, définissez la méthode qui sera appelée une fois chaque thread de travail terminé.
La méthode ajoute une nouvelle ligne à la table, met à jour la barre de progression et trie les
symboles une fois que tous les threads de production sont terminés : update() update()
Résumé
Utilisez la classe pour représenter une tâche de longue durée qui sera déchargée sur un thread
de travail. QRunnable
https://www.pythontutorial.net/pyqt/qthreadpool/ 18/19
25/04/2023 02:01 Multithreading PyQt avec QThreadPool & QRunnable
https://www.pythontutorial.net/pyqt/qthreadpool/ 19/19