{"id":5359,"date":"2022-10-24T03:13:47","date_gmt":"2022-10-24T03:13:47","guid":{"rendered":"https:\/\/www.pythontutorial.net\/?page_id=5359"},"modified":"2023-06-05T13:58:23","modified_gmt":"2023-06-05T13:58:23","slug":"qthreadpool","status":"publish","type":"page","link":"https:\/\/www.pythontutorial.net\/pyqt\/qthreadpool\/","title":{"rendered":"PyQt QThreadPool"},"content":{"rendered":"\n<p><strong>Summary<\/strong>: in this tutorial, you&#8217;ll learn how to create a PyQt multithreading application that uses <code>QThreadPool<\/code> and <code>QRunnable<\/code> classes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='introduction-to-the-qthreadpool-qrunnable-classes'>Introduction to the QThreadPool &amp; QRunnable classes <a href=\"#introduction-to-the-qthreadpool-qrunnable-classes\" class=\"anchor\" id=\"introduction-to-the-qthreadpool-qrunnable-classes\" title=\"Anchor for Introduction to the QThreadPool &amp; QRunnable classes\">#<\/a><\/h2>\n\n\n\n<p>The <code><a href=\"https:\/\/www.pythontutorial.net\/pyqt\/pyqt-qthread\/\">QThread<\/a><\/code> class allows you to offload a long-running task to a worker thread to make the application more responsive. The QThread class works fine if the application has a few worker threads.<\/p>\n\n\n\n<p>A multithreaded program is efficient when it has a number of <code>QThread<\/code> objects that correspond to the number of CPU cores.<\/p>\n\n\n\n<p>Also, creating threads is quite expensive in terms of computer resources. Therefore, the program should reuse created threads as much as possible.<\/p>\n\n\n\n<p>So using the <code>QThread<\/code> class to manage worker threads has two main challenges:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Determine the ideal number of threads for the application based on the number of CPU cores.<\/li>\n\n\n\n<li>Reuse and recycle the threads as much as possible.<\/li>\n<\/ul>\n\n\n\n<p>Fortunately, PyQt has <code><code>QThreadPool<\/code><\/code> class that solves these challenges for you. The <code>QThreadPool<\/code> class is often used with the <code>QRunnable<\/code> class.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>QRunnable<\/code> class represents a task you want to execute in a worker thread.<\/li>\n\n\n\n<li>The <code>QThreadPool<\/code> executes a <code>QRunnable<\/code> object, and manages and recycles threads automatically.<\/li>\n<\/ul>\n\n\n\n<p>Each Qt application has a global <code><code>QThreadPool<\/code><\/code> object which can be accessed via the <code>globalInstance()<\/code> static method of the <code><code>QThreadPool<\/code><\/code> class.<\/p>\n\n\n\n<p>To use the <code>QThreadPool<\/code> and <code>QRunnable<\/code> classes, you follow these steps:<\/p>\n\n\n\n<p>First, create a class that <a href=\"https:\/\/www.pythontutorial.net\/python-oop\/python-inheritance\/\">inherits<\/a> from the <code>QRunnable<\/code> class and override the <code>run()<\/code> method:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Worker<\/span><span class=\"hljs-params\">(QRunnable)<\/span>:<\/span>\n<span class=\"hljs-meta\">    @Slot()<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-comment\"># place a long-running task here<\/span>\n        <span class=\"hljs-keyword\">pass<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Second, access the thread pool from the main window and start the worker threads:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MainWindow<\/span><span class=\"hljs-params\">(QMainWindow)<\/span>:<\/span>\n    <span class=\"hljs-comment\"># other methods<\/span>\n    <span class=\"hljs-comment\"># ...<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\" Create and execute worker threads\n        \"\"\"<\/span>\n        pool = QThreadPool.globalInstance()\n        <span class=\"hljs-keyword\">for<\/span> _ <span class=\"hljs-keyword\">in<\/span> range(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">100<\/span>):\n            pool.start(Worker())<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To update the worker&#8217;s progress to the main thread, you use signals and slots. However, the <code>QRunnable<\/code> doesn&#8217;t support signal. <\/p>\n\n\n\n<p>Therefore, you need to define a separate class that inherits from the <code>QObject<\/code> and uses that class in the Worker class. Here are the steps:<\/p>\n\n\n\n<p>First, define the <code>Signals<\/code> class that subclasses the <code>QObject<\/code> class:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Signals<\/span><span class=\"hljs-params\">(QObject)<\/span>:<\/span>\n    completed = Signal()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the <code>Signals<\/code> class, we define one signal called <code>completed<\/code>. Note that you can define as many signals as needed.<\/p>\n\n\n\n<p>Second, emit the <code>completed<\/code> signal when the job is done in the <code>Worker<\/code> class:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Runnable<\/span><span class=\"hljs-params\">(QRunnable)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        super().__init__()\n        self.signals = Signals()\n\n<span class=\"hljs-meta\">    @Slot()<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-comment\"># long running task<\/span>\n        <span class=\"hljs-comment\"># ...<\/span>\n        <span class=\"hljs-comment\"># emit the completed signal<\/span>\n        self.signals.completed.emit()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Third, connect the signal of the worker thread with a slot of the main window before submitting the worker to the pool:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MainWindow<\/span><span class=\"hljs-params\">(QMainWindow)<\/span>:<\/span>\n    <span class=\"hljs-comment\"># other methods<\/span>\n    <span class=\"hljs-comment\"># ...<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\" Create and execute worker threads\n        \"\"\"<\/span>\n        pool = QThreadPool.globalInstance()\n        <span class=\"hljs-keyword\">for<\/span> _ <span class=\"hljs-keyword\">in<\/span> range(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">100<\/span>):\n            worker = Worker()\n            worker.signals.completed.connect(self.update)\n            pool.start(worker)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">update<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-comment\"># update the worker<\/span>\n        <span class=\"hljs-keyword\">pass<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='qthreadpool-example'>QThreadPool example <a href=\"#qthreadpool-example\" class=\"anchor\" id=\"qthreadpool-example\" title=\"Anchor for QThreadPool example\">#<\/a><\/h2>\n\n\n\n<p>The following example illustrates how to use the <code>QThreadPool<\/code> and <code>QRunnable<\/code> class:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-keyword\">import<\/span> sys\n<span class=\"hljs-keyword\">import<\/span> time\n<span class=\"hljs-keyword\">from<\/span> PyQt6.QtWidgets <span class=\"hljs-keyword\">import<\/span> QApplication, QMainWindow, QPushButton, QGridLayout, QWidget, QProgressBar, QListWidget\n<span class=\"hljs-keyword\">from<\/span> PyQt6.QtCore <span class=\"hljs-keyword\">import<\/span> QRunnable, QObject, QThreadPool, pyqtSignal <span class=\"hljs-keyword\">as<\/span> Signal, pyqtSlot <span class=\"hljs-keyword\">as<\/span> Slot\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Signals<\/span><span class=\"hljs-params\">(QObject)<\/span>:<\/span>\n    started = Signal(int)\n    completed = Signal(int)\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Worker<\/span><span class=\"hljs-params\">(QRunnable)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n        super().__init__()\n        self.n = n\n        self.signals = Signals()\n\n<span class=\"hljs-meta\">    @Slot()<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.signals.started.emit(self.n)\n        time.sleep(self.n*<span class=\"hljs-number\">1.1<\/span>)\n        self.signals.completed.emit(self.n)\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MainWindow<\/span><span class=\"hljs-params\">(QMainWindow)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, parent=None)<\/span>:<\/span>\n        super().__init__(parent)\n\n        self.setWindowTitle(<span class=\"hljs-string\">'QThreadPool Demo'<\/span>)\n\n        self.job_count = <span class=\"hljs-number\">10<\/span>\n        self.comleted_jobs = &#91;]\n\n        widget = QWidget()\n        widget.setLayout(QGridLayout())\n        self.setCentralWidget(widget)\n\n        self.btn_start = QPushButton(<span class=\"hljs-string\">'Start'<\/span>, clicked=self.start_jobs)\n        self.progress_bar = QProgressBar(minimum=<span class=\"hljs-number\">0<\/span>, maximum=self.job_count)\n        self.list = QListWidget()\n\n        widget.layout().addWidget(self.list, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>)\n        widget.layout().addWidget(self.progress_bar, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">0<\/span>)\n        widget.layout().addWidget(self.btn_start, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">1<\/span>)\n\n        self.show()\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start_jobs<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.restart()\n        pool = QThreadPool.globalInstance()\n        <span class=\"hljs-keyword\">for<\/span> i <span class=\"hljs-keyword\">in<\/span> range(<span class=\"hljs-number\">1<\/span>, self.job_count+<span class=\"hljs-number\">1<\/span>):\n            worker = Worker(i)\n            worker.signals.completed.connect(self.complete)\n            worker.signals.started.connect(self.start)\n            pool.start(worker)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">restart<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.progress_bar.setValue(<span class=\"hljs-number\">0<\/span>)\n        self.comleted_jobs = &#91;]\n        self.btn_start.setEnabled(<span class=\"hljs-literal\">False<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n        self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> started...'<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">complete<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n        self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> completed.'<\/span>)\n        self.comleted_jobs.append(n)\n        self.progress_bar.setValue(len(self.comleted_jobs))\n\n        <span class=\"hljs-keyword\">if<\/span> len(self.comleted_jobs) == self.job_count:\n            self.btn_start.setEnabled(<span class=\"hljs-literal\">True<\/span>)\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">'__main__'<\/span>:\n    app = QApplication(sys.argv)\n    window = MainWindow()\n    sys.exit(app.exec())\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">complete<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n    self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> completed.'<\/span>)\n    self.comleted_jobs.append(n)\n    self.progress_bar.setValue(len(self.comleted_jobs))\n\n    <span class=\"hljs-keyword\">if<\/span> len(self.comleted_jobs) == self.job_count:\n        self.btn_start.setEnabled(<span class=\"hljs-literal\">True<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Output:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"496\" height=\"406\" src=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2022\/10\/PyQt-QThreadPool.png\" alt=\"\" class=\"wp-image-5365\" srcset=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2022\/10\/PyQt-QThreadPool.png 496w, https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2022\/10\/PyQt-QThreadPool-300x246.png 300w\" sizes=\"auto, (max-width: 496px) 100vw, 496px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id='signals-class'>Signals class <a href=\"#signals-class\" class=\"anchor\" id=\"signals-class\" title=\"Anchor for Signals class\">#<\/a><\/h3>\n\n\n\n<p>Define the Signals class that inherits from the <code>QObject<\/code> class to support signals. In the <code>Signals<\/code> class, we define two signals:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>started<\/code> signal will be emitted when a worker is started.<\/li>\n\n\n\n<li>The <code>completed<\/code> signal will be emitted when a worker is completed.<\/li>\n<\/ul>\n\n\n\n<p>Both signals accept an integer that identifies the job number:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Signals<\/span><span class=\"hljs-params\">(QObject)<\/span>:<\/span>\n    started = Signal(int)\n    completed = Signal(int)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id='worker-class'>Worker class <a href=\"#worker-class\" class=\"anchor\" id=\"worker-class\" title=\"Anchor for Worker class\">#<\/a><\/h3>\n\n\n\n<p>The <code>Worker<\/code> class inherits from the <code>QRunnable<\/code> class. The <code>Worker<\/code> class represents a long-running task that we offload to a worker thread:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Worker<\/span><span class=\"hljs-params\">(QRunnable)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n        super().__init__()\n        self.n = n\n        self.signals = Signals()\n\n<span class=\"hljs-meta\">    @Slot()<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.signals.started.emit(self.n)\n        time.sleep(self.n*<span class=\"hljs-number\">1.1<\/span>)\n        self.signals.completed.emit(self.n)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>First, initialize the job number (n) and Signals object in the <code>__init__()<\/code> method.<\/p>\n\n\n\n<p>Second, override the <code>run()<\/code> method of the <code>QRunnable<\/code> class. To simulate a long-running task, we use the <code>sleep()<\/code> function of the time module. Before starting the timer, we emit the started signal; after the timer completes, we emit the completed signal.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id='mainwindow-class'>MainWindow class <a href=\"#mainwindow-class\" class=\"anchor\" id=\"mainwindow-class\" title=\"Anchor for MainWindow class\">#<\/a><\/h3>\n\n\n\n<p>The <code>MainWindow<\/code> class defines the <code>UI<\/code> for the application:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MainWindow<\/span><span class=\"hljs-params\">(QMainWindow)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, parent=None)<\/span>:<\/span>\n        super().__init__(parent)\n\n        self.setWindowTitle(<span class=\"hljs-string\">'QThreadPool Demo'<\/span>)\n\n        self.comleted_jobs = &#91;]\n        self.job_count = <span class=\"hljs-number\">10<\/span>\n\n        widget = QWidget()\n        widget.setLayout(QGridLayout())\n        self.setCentralWidget(widget)\n\n        self.btn_start = QPushButton(<span class=\"hljs-string\">'Start'<\/span>, clicked=self.start_jobs)\n        self.progress_bar = QProgressBar(minimum=<span class=\"hljs-number\">0<\/span>, maximum=self.job_count)\n        self.list = QListWidget()\n\n        widget.layout().addWidget(self.list, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>)\n        widget.layout().addWidget(self.progress_bar, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">0<\/span>)\n        widget.layout().addWidget(self.btn_start, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">1<\/span>)\n\n        self.show()\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start_jobs<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.restart()\n\n        pool = QThreadPool.globalInstance()\n        <span class=\"hljs-keyword\">for<\/span> i <span class=\"hljs-keyword\">in<\/span> range(<span class=\"hljs-number\">1<\/span>, self.job_count+<span class=\"hljs-number\">1<\/span>):\n            runnable = Worker(i)\n            runnable.signals.completed.connect(self.complete)\n            runnable.signals.started.connect(self.start)\n            pool.start(runnable)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">restart<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.progress_bar.setValue(<span class=\"hljs-number\">0<\/span>)\n        self.comleted_jobs = &#91;]\n        self.btn_start.setEnabled(<span class=\"hljs-literal\">False<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n        self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> started...'<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">complete<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n        self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> completed.'<\/span>)\n        self.comleted_jobs.append(n)\n        self.progress_bar.setValue(len(self.comleted_jobs))\n\n        <span class=\"hljs-keyword\">if<\/span> len(self.comleted_jobs) == self.job_count:\n            self.btn_start.setEnabled(<span class=\"hljs-literal\">True<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>First, initialize the number of jobs (<code>job_count<\/code>) and <code>completed_jobs<\/code> list in the <code>__init__()<\/code> method of the <code>MainWindow<\/code> class:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">self.job_count = <span class=\"hljs-number\">10<\/span>\nself.comleted_jobs = &#91;]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Second, define the <code>start_jobs()<\/code> method that will be executed when the user clicks the start button:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start_jobs<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n    self.restart()\n    pool = QThreadPool.globalInstance()\n    <span class=\"hljs-keyword\">for<\/span> i <span class=\"hljs-keyword\">in<\/span> range(<span class=\"hljs-number\">1<\/span>, self.job_count+<span class=\"hljs-number\">1<\/span>):\n        worker = Worker(i)\n        worker.signals.completed.connect(self.complete)\n        worker.signals.started.connect(self.start)\n        pool.start(worker)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>restart()<\/code> resets the <code>completed_jobs<\/code>, updates the progress bar to zero, and disables the start button:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">restart<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n    self.progress_bar.setValue(<span class=\"hljs-number\">0<\/span>)\n    self.comleted_jobs = &#91;]\n    self.btn_start.setEnabled(<span class=\"hljs-literal\">False<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To get the <code><code>QThreadPool<\/code><\/code> object, we use the <code>globalInstance()<\/code> of the <code><code>QThreadPool<\/code><\/code> class:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">pool = QThreadPool.globalInstance()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We create a number of workers, connect their signals to the methods of the <code>MainWindow<\/code> class, and start worker threads using the <code>start()<\/code> method of the <code>QThreadPool<\/code> object.<\/p>\n\n\n\n<p>The <code>start()<\/code> method adds the message that starts a worker thread to the <code>QListWidget<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">start<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n    self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> started...'<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>completed()<\/code> method runs each time a worker thread is completed. It adds a message to the <code>QListWidget<\/code>, updates the progress bar, and enables the start button if all worker threads are completed:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">complete<\/span><span class=\"hljs-params\">(self, n)<\/span>:<\/span>\n    self.list.addItem(<span class=\"hljs-string\">f'Job #<span class=\"hljs-subst\">{n}<\/span> completed.'<\/span>)\n    self.comleted_jobs.append(n)\n    self.progress_bar.setValue(len(self.comleted_jobs))\n\n    <span class=\"hljs-keyword\">if<\/span> len(self.comleted_jobs) == self.job_count:\n        self.btn_start.setEnabled(<span class=\"hljs-literal\">True<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='using-qthreadpool-to-get-stock-prices'>Using QThreadPool to get stock prices <a href=\"#using-qthreadpool-to-get-stock-prices\" class=\"anchor\" id=\"using-qthreadpool-to-get-stock-prices\" title=\"Anchor for Using QThreadPool to get stock prices\">#<\/a><\/h2>\n\n\n\n<p>The following Stock Listing program reads stock symbols from <code>symbols.txt<\/code> file and uses <code>QThreadPool<\/code> to get the stock prices from Yahoo Finance website:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"602\" height=\"496\" src=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2022\/10\/PyQt-QThreadPool-Stock-Demo.png\" alt=\"\" class=\"wp-image-5368\" srcset=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2022\/10\/PyQt-QThreadPool-Stock-Demo.png 602w, https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2022\/10\/PyQt-QThreadPool-Stock-Demo-300x247.png 300w\" sizes=\"auto, (max-width: 602px) 100vw, 602px\" \/><\/figure>\n\n\n\n<p>Stock Listing Program:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-keyword\">import<\/span> sys\n<span class=\"hljs-keyword\">from<\/span> pathlib <span class=\"hljs-keyword\">import<\/span> Path\n\n<span class=\"hljs-keyword\">from<\/span> PyQt6.QtCore <span class=\"hljs-keyword\">import<\/span> QRunnable, Qt, QObject, QThreadPool, pyqtSignal <span class=\"hljs-keyword\">as<\/span> Signal, pyqtSlot <span class=\"hljs-keyword\">as<\/span> Slot\n<span class=\"hljs-keyword\">from<\/span> PyQt6.QtWidgets <span class=\"hljs-keyword\">import<\/span> QApplication,  QMainWindow, QPushButton, QWidget, QGridLayout, QProgressBar, QTableWidget, QTableWidgetItem\n<span class=\"hljs-keyword\">from<\/span> PyQt6.QtGui <span class=\"hljs-keyword\">import<\/span> QIcon\n\n<span class=\"hljs-keyword\">from<\/span> lxml <span class=\"hljs-keyword\">import<\/span> html\n<span class=\"hljs-keyword\">import<\/span> requests\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Signals<\/span><span class=\"hljs-params\">(QObject)<\/span>:<\/span>\n    completed = Signal(dict)\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Stock<\/span><span class=\"hljs-params\">(QRunnable)<\/span>:<\/span>\n    BASE_URL = <span class=\"hljs-string\">'https:\/\/finance.yahoo.com\/quote\/'<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, symbol)<\/span>:<\/span>\n        super().__init__()\n        self.symbol = symbol\n        self.signal = Signals()\n\n<span class=\"hljs-meta\">    @Slot()<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        stock_url = <span class=\"hljs-string\">f'<span class=\"hljs-subst\">{self.BASE_URL}<\/span><span class=\"hljs-subst\">{self.symbol}<\/span>'<\/span>\n        headers = {<span class=\"hljs-string\">\"User-Agent\"<\/span>: <span class=\"hljs-string\">\"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/114.0.0.0 Safari\/537.36\"<\/span>}\r\n        response = requests.get(stock_url, headers=headers)\r\n\n        <span class=\"hljs-keyword\">if<\/span> response.status_code != <span class=\"hljs-number\">200<\/span>:\n            self.signal.completed.emit({<span class=\"hljs-string\">'symbol'<\/span>: self.symbol, <span class=\"hljs-string\">'price'<\/span>: <span class=\"hljs-string\">'N\/A'<\/span>})\n            <span class=\"hljs-keyword\">return<\/span>\n\n        tree = html.fromstring(response.text)\n        price_text = tree.xpath(\n            <span class=\"hljs-string\">'\/\/*&#91;@id=\"quote-header-info\"]\/div&#91;3]\/div&#91;1]\/div&#91;1]\/fin-streamer&#91;1]\/text()'<\/span>\n        )\n\n        <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> price_text:\n            self.signal.completed.emit({<span class=\"hljs-string\">'symbol'<\/span>: self.symbol, <span class=\"hljs-string\">'price'<\/span>: <span class=\"hljs-string\">'N\/A'<\/span>})\n            <span class=\"hljs-keyword\">return<\/span>\n\n        price = float(price_text&#91;<span class=\"hljs-number\">0<\/span>].replace(<span class=\"hljs-string\">','<\/span>, <span class=\"hljs-string\">''<\/span>))\n\n        self.signal.completed.emit({<span class=\"hljs-string\">'symbol'<\/span>: self.symbol, <span class=\"hljs-string\">'price'<\/span>: price})\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Window<\/span><span class=\"hljs-params\">(QMainWindow)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, filename, *args, **kwargs)<\/span>:<\/span>\n        super().__init__(*args, **kwargs)\n\n        self.symbols = self.read_symbols(filename)\n\n        self.results = &#91;]\n\n        self.setWindowTitle(<span class=\"hljs-string\">'Stock Listing'<\/span>)\n        self.setGeometry(<span class=\"hljs-number\">100<\/span>, <span class=\"hljs-number\">100<\/span>, <span class=\"hljs-number\">400<\/span>, <span class=\"hljs-number\">300<\/span>)\n        self.setWindowIcon(QIcon(<span class=\"hljs-string\">'.\/assets\/stock.png'<\/span>))\n\n        widget = QWidget()\n        widget.setLayout(QGridLayout())\n        self.setCentralWidget(widget)\n\n        <span class=\"hljs-comment\"># set up button &amp; progress bar<\/span>\n        self.btn_start = QPushButton(<span class=\"hljs-string\">'Get Prices'<\/span>, clicked=self.get_prices)\n        self.progress_bar = QProgressBar(minimum=<span class=\"hljs-number\">1<\/span>, maximum=len(self.symbols))\n\n        <span class=\"hljs-comment\"># set up table widget<\/span>\n        self.table = QTableWidget(widget)\n        self.table.setColumnCount(<span class=\"hljs-number\">2<\/span>)\n        self.table.setColumnWidth(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">150<\/span>)\n        self.table.setColumnWidth(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">150<\/span>)\n\n        self.table.setHorizontalHeaderLabels(&#91;<span class=\"hljs-string\">'Symbol'<\/span>, <span class=\"hljs-string\">'Price'<\/span>])\n\n        widget.layout().addWidget(self.table, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>)\n        widget.layout().addWidget(self.progress_bar, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">0<\/span>)\n        widget.layout().addWidget(self.btn_start, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">1<\/span>)\n\n        <span class=\"hljs-comment\"># show the window<\/span>\n        self.show()\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">read_symbols<\/span><span class=\"hljs-params\">(self, filename)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\" \n        Read symbols from a file\n        \"\"\"<\/span>\n        path = Path(filename)\n        text = path.read_text()\n        <span class=\"hljs-keyword\">return<\/span> &#91;symbol.strip() <span class=\"hljs-keyword\">for<\/span> symbol <span class=\"hljs-keyword\">in<\/span> text.split(<span class=\"hljs-string\">'\\n'<\/span>)]\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">reset_ui<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        self.progress_bar.setValue(<span class=\"hljs-number\">1<\/span>)\n        self.table.setRowCount(<span class=\"hljs-number\">0<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_prices<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-comment\"># reset ui<\/span>\n        self.reset_ui()\n\n        <span class=\"hljs-comment\"># start worker threads<\/span>\n        pool = QThreadPool.globalInstance()\n        stocks = &#91;Stock(symbol) <span class=\"hljs-keyword\">for<\/span> symbol <span class=\"hljs-keyword\">in<\/span> self.symbols]\n        <span class=\"hljs-keyword\">for<\/span> stock <span class=\"hljs-keyword\">in<\/span> stocks:\n            stock.signal.completed.connect(self.update)\n            pool.start(stock)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">update<\/span><span class=\"hljs-params\">(self, data)<\/span>:<\/span>\n        <span class=\"hljs-comment\"># add a row to the table<\/span>\n        row = self.table.rowCount()\n        self.table.insertRow(row)\n        self.table.setItem(row, <span class=\"hljs-number\">0<\/span>, QTableWidgetItem(data&#91;<span class=\"hljs-string\">'symbol'<\/span>]))\n        self.table.setItem(row, <span class=\"hljs-number\">1<\/span>, QTableWidgetItem(str(data&#91;<span class=\"hljs-string\">'price'<\/span>])))\n\n        <span class=\"hljs-comment\"># update the progress bar<\/span>\n        self.progress_bar.setValue(row + <span class=\"hljs-number\">1<\/span>)\n\n        <span class=\"hljs-comment\"># sort the list by symbols once completed<\/span>\n        <span class=\"hljs-keyword\">if<\/span> row == len(self.symbols) - <span class=\"hljs-number\">1<\/span>:\n            self.table.sortItems(<span class=\"hljs-number\">0<\/span>, Qt.SortOrder.AscendingOrder)\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">'__main__'<\/span>:\n    app = QApplication(sys.argv)\n    window = Window(<span class=\"hljs-string\">'symbols.txt'<\/span>)\n    sys.exit(app.exec())<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>How it works.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id='signals-class'>Signals class <a href=\"#signals-class\" class=\"anchor\" id=\"signals-class\" title=\"Anchor for Signals class\">#<\/a><\/h3>\n\n\n\n<p>We define the <code>Signals<\/code> class that is a subclass of the <code>QObject<\/code>. The Signals class has one class variable completed which is an instance of the <code>Signal<\/code> class. <\/p>\n\n\n\n<p>The completed signal holds a dictionary and is emitted once the program completes getting the stock price.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Signals<\/span><span class=\"hljs-params\">(QObject)<\/span>:<\/span>\n    completed = Signal(dict)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id='stock-class'>Stock class  <a href=\"#stock-class\" class=\"anchor\" id=\"stock-class\" title=\"Anchor for Stock class \">#<\/a><\/h3>\n\n\n\n<p>The <code>STock<\/code> class inherits from the <code>QRunnable<\/code> class. It overrides the <code>run()<\/code> method that gets the stock price from the Yahoo Finance website. <\/p>\n\n\n\n<p>Once completed, the <code>run()<\/code> method emits the completed signal with the stock symbol and price.<\/p>\n\n\n\n<p>If an error occurs like the symbol is not found or the website changes the way it displays the stock price, the <code>run()<\/code> method returns the symbol with the price as a N\/A string.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Stock<\/span><span class=\"hljs-params\">(QRunnable)<\/span>:<\/span>\n    BASE_URL = <span class=\"hljs-string\">'https:\/\/finance.yahoo.com\/quote\/'<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, symbol)<\/span>:<\/span>\n        super().__init__()\n        self.symbol = symbol\n        self.signal = Signals()\n\n<span class=\"hljs-meta\">    @Slot()<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        stock_url = <span class=\"hljs-string\">f'<span class=\"hljs-subst\">{self.BASE_URL}<\/span><span class=\"hljs-subst\">{self.symbol}<\/span>'<\/span>\n        headers = {<span class=\"hljs-string\">\"User-Agent\"<\/span>: <span class=\"hljs-string\">\"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/114.0.0.0 Safari\/537.36\"<\/span>}\n        response = requests.get(stock_url, headers=headers)\n        <span class=\"hljs-keyword\">if<\/span> response.status_code != <span class=\"hljs-number\">200<\/span>:\n            self.signal.completed.emit({<span class=\"hljs-string\">'symbol'<\/span>: self.symbol, <span class=\"hljs-string\">'price'<\/span>: <span class=\"hljs-string\">'N\/A'<\/span>})\n            <span class=\"hljs-keyword\">return<\/span>\n\n        tree = html.fromstring(response.text)\n        price_text = tree.xpath(\n            <span class=\"hljs-string\">'\/\/*&#91;@id=\"quote-header-info\"]\/div&#91;3]\/div&#91;1]\/div&#91;1]\/fin-streamer&#91;1]\/text()'<\/span>\n        )\n\n        <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> price_text:\n            self.signal.completed.emit({<span class=\"hljs-string\">'symbol'<\/span>: self.symbol, <span class=\"hljs-string\">'price'<\/span>: <span class=\"hljs-string\">'N\/A'<\/span>})\n            <span class=\"hljs-keyword\">return<\/span>\n\n        price = float(price_text&#91;<span class=\"hljs-number\">0<\/span>].replace(<span class=\"hljs-string\">','<\/span>, <span class=\"hljs-string\">''<\/span>))\n\n        self.signal.completed.emit({<span class=\"hljs-string\">'symbol'<\/span>: self.symbol, <span class=\"hljs-string\">'price'<\/span>: price})<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that Yahoo Finance may change its structure. To make the program works, you need to change the XPath of the price of the new one:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">\/\/*&#91;@id=<span class=\"hljs-string\">\"quote-header-info\"<\/span>]\/div&#91;<span class=\"hljs-number\">3<\/span>]\/div&#91;<span class=\"hljs-number\">1<\/span>]\/div&#91;<span class=\"hljs-number\">1<\/span>]\/fin-streamer&#91;<span class=\"hljs-number\">1<\/span>]\/text()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id='mainwindow-class'>MainWindow Class <a href=\"#mainwindow-class\" class=\"anchor\" id=\"mainwindow-class\" title=\"Anchor for MainWindow Class\">#<\/a><\/h3>\n\n\n\n<p>First, read the symbols from a file and assign them to the <code>self.symbols<\/code> variables:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"> self.symbols = self.read_symbols(filename)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>read_symbols()<\/code> method looks like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">read_symbols<\/span><span class=\"hljs-params\">(self, filename)<\/span>:<\/span>\n    path = Path(filename)\n    text = path.read_text()\n    <span class=\"hljs-keyword\">return<\/span> &#91;symbol.strip() <span class=\"hljs-keyword\">for<\/span> symbol <span class=\"hljs-keyword\">in<\/span> text.split(<span class=\"hljs-string\">'\\n'<\/span>)]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The text file (<code>symbols.txt<\/code>) contains each symbol per line:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\">AAPL\t\nMSFT\nGOOG\t\nAMZN\nTSLA\nMETA\nNVDA\nBABA\nCRM\nINTC\nPYPL\t\nAMD\nATVI\nEA\nTTD\nORCL<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Second, define the <code>get_prices<\/code> that uses the <code>QThreadPool<\/code> to create worker threads for getting stock prices:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_prices<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n    <span class=\"hljs-comment\"># reset ui<\/span>\n    self.reset_ui()\n\n    <span class=\"hljs-comment\"># start worker threads<\/span>\n    pool = QThreadPool.globalInstance()\n    stocks = &#91;Stock(symbol) <span class=\"hljs-keyword\">for<\/span> symbol <span class=\"hljs-keyword\">in<\/span> self.symbols]\n    <span class=\"hljs-keyword\">for<\/span> stock <span class=\"hljs-keyword\">in<\/span> stocks:\n        stock.signal.completed.connect(self.update)\n        pool.start(stock)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>reset_ui()<\/code> method clear all rows of the <code>QTableWidget<\/code> and set the progress bar to its minimum value:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">reset_ui<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n    self.table.setRowCount(<span class=\"hljs-number\">0<\/span>)\n    self.progress_bar.setValue(<span class=\"hljs-number\">1<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Third, define the <code><code>update()<\/code><\/code> method that will be called once each worker thread is completed. The <code><code>update()<\/code><\/code> method adds a new row to the table, updates the progress bar, and sorts the symbols once all the worker threads are completed:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">update<\/span><span class=\"hljs-params\">(self, data)<\/span>:<\/span>\n    <span class=\"hljs-comment\"># add a row to the table<\/span>\n    row = self.table.rowCount()\n    self.table.insertRow(row)\n    self.table.setItem(row, <span class=\"hljs-number\">0<\/span>, QTableWidgetItem(data&#91;<span class=\"hljs-string\">'symbol'<\/span>]))\n    self.table.setItem(row, <span class=\"hljs-number\">1<\/span>, QTableWidgetItem(str(data&#91;<span class=\"hljs-string\">'price'<\/span>])))\n\n    <span class=\"hljs-comment\"># update the progress bar<\/span>\n    self.progress_bar.setValue(row + <span class=\"hljs-number\">1<\/span>)\n\n    <span class=\"hljs-comment\"># sort the list by symbols once completed<\/span>\n    <span class=\"hljs-keyword\">if<\/span> row == len(self.symbols) - <span class=\"hljs-number\">1<\/span>:\n        self.table.sortItems(<span class=\"hljs-number\">0<\/span>, Qt.SortOrder.AscendingOrder)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='summary'>Summary <a href=\"#summary\" class=\"anchor\" id=\"summary\" title=\"Anchor for Summary\">#<\/a><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use the <code>QRunnable<\/code> class to represent a long-running task that will be offloaded to a worker thread.<\/li>\n\n\n\n<li>Use the <code>QThreadPool<\/code> class to manage worker threads automatically.<\/li>\n\n\n\n<li>Each PyQt application has one <code><code>QThreadPool<\/code><\/code> object. Use the <code>globalInstance()<\/code> method to get the global <code><code>QThreadPool<\/code><\/code> object.<\/li>\n\n\n\n<li>Use the <code>start()<\/code> method of the <code>QThreadPool<\/code> object to start a worker thread.<\/li>\n<\/ul>\n<div class=\"helpful-block-content\" data-title=\"\">\n\t<header>\n\t\t<div class=\"wth-question\">Was this tutorial helpful ?<\/div>\n\t\t<div class=\"wth-thumbs\">\n\t\t\t<button\n\t\t\t\tdata-post=\"5359\"\n\t\t\t\tdata-post-url=\"https:\/\/www.pythontutorial.net\/pyqt\/qthreadpool\/\"\n\t\t\t\tdata-post-title=\"PyQt QThreadPool\"\n\t\t\t\tdata-response=\"1\"\n\t\t\t\tclass=\"wth-btn-rounded wth-yes-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\tclass=\"feather feather-thumbs-up block w-full h-full\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> Yes <\/span>\n\t\t\t<\/button>\n\n\t\t\t<button\n\t\t\t\tdata-response=\"0\"\n\t\t\t\tdata-post=\"5359\"\n\t\t\t\tdata-post-url=\"https:\/\/www.pythontutorial.net\/pyqt\/qthreadpool\/\"\n\t\t\t\tdata-post-title=\"PyQt QThreadPool\"\n\t\t\t\tclass=\"wth-btn-rounded wth-no-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> No <\/span>\n\t\t\t<\/button>\n\t\t<\/div>\n\t<\/header>\n\n\t<div class=\"wth-form hidden\">\n\t\t<div class=\"wth-form-wrapper\">\n\t\t\t<div class=\"wth-title\"><\/div>\n\t\t\t<textarea class=\"wth-message\"><\/textarea>\n\t\t\t<input type=\"button\" name=\"wth-submit\" class=\"wth-btn wth-btn-submit\" id=\"wth-submit\" \/>\n\t\t\t<input type=\"button\" class=\"wth-btn wth-btn-cancel\" value=\"Cancel\" \/>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, you&#8217;ll learn how to create a PyQt multithreading application that uses QThreadPool and QRunnable classes.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":4862,"menu_order":35,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-5359","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/5359","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/comments?post=5359"}],"version-history":[{"count":0,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/5359\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/4862"}],"wp:attachment":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/media?parent=5359"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}