TransWikia.com

Привязать QThread к QProgressBar и кнопкам

Stack Overflow на русском Asked on December 11, 2021

Есть простой интерфейс.

Необходимо чтобы при нажатии кнопки ‘Start’ стартовал прирост 1 единица прогресса в секунду используя обычный метод, например такой:

def add_value(prog_bar):
    for i in range(100):
        time.sleep(1)
        prog_bar.setValue(i)

а при нажатии ‘Stop’ прирост останавливался.
Сделать это необходимо с помощю QThread.

Мой код:

from PyQt5 import QtWidgets
import sys

app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setWindowTitle('ProgressBar')
vbx = QtWidgets.QVBoxLayout()
prg_bar = QtWidgets.QProgressBar()
prg_bar.setRange(0,100)
button_start = QtWidgets.QPushButton('Start')
button_stop = QtWidgets.QPushButton('Stop')
vbx.addWidget(prg_bar)
vbx.addWidget(button_start)
vbx.addWidget(button_stop)
window.setLayout(vbx)
window.show()
sys.exit(app.exec_())

введите сюда описание изображения

2 Answers

Можно наследоваться от QThread и использовать QWaitCondition, QMutex и QThread.sleep для того, чтобы генерировать периодические события:

from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal, QWaitCondition, QMutex

import sys

class WoofThread(QThread):

    # Сигнал потока
    signal_woof = pyqtSignal()

    def __init__(self):
        QThread.__init__(self)

        # Поле для работы вечного цикла
        self._action = True
        # интервал сна/паузы
        self._timeout = 1

        # объект погружения/пробуждения потока в сон/ из сна
        self._waitCondition = QWaitCondition()

    def run(self):
        mutex = QMutex()

        # Поле-признак того, что пока спать, или просыпаться
        self._wait = True
        self._action = True
        while self._action:
            # Если поступил приказ ждать - ждем
            if self._wait:
                mutex.lock()
                self._waitCondition.wait(mutex)
                mutex.unlock()

            # Спим _timeout - времени
            QThread.sleep(self._timeout)
            # Гав!
            self.signal_woof.emit()

    # Поехали
    def go(self):
        self._wait = False
        self._waitCondition.wakeAll()

    # Погодите
    def wait(self):
        self._wait = True

    # Завершить работу потока
    def cancel(self):
        self._action = False
        self._waitCondition.wakeAll()

app = QtWidgets.QApplication(sys.argv)

thread = WoofThread()

window = QtWidgets.QWidget()
window.setWindowTitle('ProgressBar')
vbx = QtWidgets.QVBoxLayout()
prg_bar = QtWidgets.QProgressBar()
prg_bar.setRange(0,100)
prg_bar.setValue(0)

button_start = QtWidgets.QPushButton('Start')

# Клик вызывает пробуждение и продолжение работы потока
button_start.clicked.connect(thread.go)

button_stop = QtWidgets.QPushButton('Stop')
# Клик вызывает ожидание потока
button_stop.clicked.connect(thread.wait)

# Получили сигнал из потока - увеличили прогресс на +1
thread.signal_woof.connect(lambda: prg_bar.setValue(prg_bar.value() +1))

vbx.addWidget(prg_bar)
vbx.addWidget(button_start)
vbx.addWidget(button_stop)
window.setLayout(vbx)
window.show()

# Запустили поток
thread.start()

sys.exit(app.exec_())

Answered by Alexander Chernin on December 11, 2021

Как вариант:

from PyQt5 import QtCore, QtWidgets


class Worker(QtCore.QObject):
    started  = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    data     = QtCore.pyqtSignal(int) 

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.running = False
        self.num = 0

    @QtCore.pyqtSlot()
    def read_data_from_sensor(self):
        self.started.emit()
        QtCore.QThread.msleep(1000)                              # Моделируем процесс блокировки
        while self.running and self.num < 100:
            self.num += 1
            self.data.emit(self.num)
            QtCore.QThread.msleep(1000)                           
        self.finished.emit()


class App(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Реализация QThread-moveToThread")
        self.resize(400, 300)
        centralWidget = QtWidgets.QWidget(self)
        self.setCentralWidget(centralWidget)

        self.boton_iniciar = QtWidgets.QPushButton('Начать', self)
        self.boton_iniciar.clicked.connect(self.read_data)

        self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
        self.label_data.setText('В ожидании')
        self.label_data.adjustSize()
        
        self.prg_bar = QtWidgets.QProgressBar()
        
        layout = QtWidgets.QGridLayout(centralWidget) 
        layout.addWidget(self.label_data)        
        layout.addWidget(self.prg_bar)
        layout.addWidget(self.boton_iniciar)

        self._worker = Worker()
        self._worker.started.connect(self.on_started)
        self._worker.finished.connect(self.on_finished)
        self._worker.data.connect(self.update_label)

        self._thread = QtCore.QThread(self)
        self._thread.start()
        self._worker.moveToThread(self._thread)

    @QtCore.pyqtSlot()
    def on_started(self):
        """ slot будет вызван, когда начнется сбор данных """
        if self._worker.num == 100:
            self._worker.num = 0
            self.prg_bar.setValue(0)
            self._worker.running = False
        
        self.label_data.setText("Читаем данные")
        self.boton_iniciar.setText("Стоп")
        self.boton_iniciar.setEnabled(True)

    @QtCore.pyqtSlot()
    def on_finished(self):
        """ slot будет вызван, когда сбор данных закончится """
        self.label_data.setText("В ожидании")
        self.boton_iniciar.setText("Начать")
        self.boton_iniciar.setEnabled(True)
        print("_worker.running: finished", self.prg_bar.value())

    @QtCore.pyqtSlot()
    def read_data(self):
        """ Начать / остановить чтение при нажатии кнопки """
        print("_worker.running:", self._worker.running)
        self.on_started()
        
        if self._worker.running:
            self._worker.running = False
        else:
            self._worker.running = True
            QtCore.QTimer.singleShot(0, self._worker.read_data_from_sensor)
        self.boton_iniciar.setEnabled(False)

    @QtCore.pyqtSlot(int)  # str
    def update_label(self, data):
        """ Slot будет вызываться при появлении новых данных в метке """
        self.prg_bar.setValue(data)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ventana = App()
    ventana.show()
    sys.exit(app.exec_())

введите сюда описание изображения

Answered by S. Nick on December 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP