Search code examples
pythonqthreadpyside6

Disable elements when running QThread with PySide/PyQt


This is what I get when I click on the OK button.

But I would like to disable the two buttons while the run_action() is still running and finally reset the bar to 0.

This is my current code:

import sys
import time

from PySide6.QtCore import QThread, Signal
from PySide6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QProgressBar,
    QPushButton,
    QWidget,
)


class External(QThread):
    progressChanged = Signal(int)

    def run(self):
        progress = 0
        while progress < 100:
            progress += 10
            time.sleep(1)
            self.progressChanged.emit(progress)


class Window(QWidget):
    """The main application Window."""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Example")

        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(6, 6, 6, 6)

        self.bar = QProgressBar()
        self.bar.setTextVisible(False)
        self.bar.setValue(0)
        self.layout.addWidget(self.bar)

        self.cancel_btn = QPushButton("Cancel")
        self.cancel_btn.clicked.connect(self.close)
        self.layout.addWidget(self.cancel_btn)

        self.ok_btn = QPushButton("OK")
        self.ok_btn.clicked.connect(self.run_action)
        self.layout.addWidget(self.ok_btn)

        self.setLayout(self.layout)

    def run_action(self):
        self.ok_btn.setEnabled(False)
        self.cancel_btn.setEnabled(False)

        self.calc = External()
        self.calc.progressChanged.connect(self.onProgressChanged)
        self.calc.start()

        self.cancel_btn.setEnabled(True)
        self.ok_btn.setEnabled(True)
        self.bar.setValue(0)

    def onProgressChanged(self, value):
        self.bar.setValue(value)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Solution

  • void QThread::finished()

    This signal is emitted from the associated thread right before it finishes executing.

    When this signal is emitted, the event loop has already stopped running. No more events will be processed in the thread, except for deferred deletion events. This signal can be connected to QObject::deleteLater(), to free objects in that thread.

    import sys
    #import time
    
    #from PySide6.QtCore import QThread, Signal
    from PyQt5.QtCore import QThread, pyqtSignal
    
    #from PySide6.QtWidgets import (
    from PyQt5.QtWidgets import (
        QApplication,
        QHBoxLayout,
        QProgressBar,
        QPushButton,
        QWidget,
    )
    
    
    class External(QThread):
    #    progressChanged = Signal(int)
        progressChanged = pyqtSignal(int)
    
        def run(self):
            progress = 0
            while progress <= 100:
                self.progressChanged.emit(progress)
                self.msleep(500)
                progress += 10
    
    
    class Window(QWidget):
        """The main application Window."""
    
        def __init__(self):
            super().__init__()
            self.setWindowTitle("Example")
    
            self.layout = QHBoxLayout()
            self.layout.setContentsMargins(6, 6, 6, 6)
    
            self.bar = QProgressBar()
            self.bar.setTextVisible(False)
            self.bar.setValue(0)
            self.layout.addWidget(self.bar)
    
            self.cancel_btn = QPushButton("Cancel")
            self.cancel_btn.clicked.connect(self.close)
            self.layout.addWidget(self.cancel_btn)
    
            self.ok_btn = QPushButton("OK")
            self.ok_btn.clicked.connect(self.run_action)
            self.layout.addWidget(self.ok_btn)
    
            self.setLayout(self.layout)
    
        def run_action(self):
            self.ok_btn.setEnabled(False)        
            self.cancel_btn.setEnabled(False)        
    
            self.calc = External()
            self.calc.progressChanged.connect(self.onProgressChanged)
            self.calc.finished.connect(self.onFinished)                     # +++
            self.calc.start()
    
    #        self.cancel_btn.setEnabled(True)
    #        self.ok_btn.setEnabled(True)
    #        self.bar.setValue(0)
    
        def onProgressChanged(self, value):
            self.bar.setValue(value)
    
        def onFinished(self):                                               # +++
            self.cancel_btn.setEnabled(True)
            self.ok_btn.setEnabled(True)
            self.bar.setValue(0)        
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = Window()
        window.show()
        sys.exit(app.exec_())
    

    enter image description here