Реализация QProgressBar в потоке

Задача: из первого приложения (модуль m1.py) по кнопке вызывается второе приложение (модуль otv.py), в процессе выполнения которого, в statusBarпервого приложения (m1.py), отображается шкала ProgressBar.

Функционал в принципе работает, но криво и через одно место (третье приложение модуль b1.py). Кажется мне, в дальнейшем это аукнется, хотелось бы разобраться.

Трудности:

  1. signaller.progress_chek.connect(self.signal)otv.py) – не работает, на мой взгляд, если в основном скрипте присутствует time.sleep
  2. т.к. п.1 не работает, обновляем ProgressBar через Example.signal(self, value=il)
  3. для работы п.2 тянем из (m1.py) в (otv.py) selfчерез (b1.py)
  4. т.к. Example.signal подвисает после 40% городим задержку и отслеживание окончания потока через result_a = threading.Event() / result_a.wait() / result_a.set(), хотя кажется это можно реализовать через finished = pyqtSignal()

P.S. в def while_x() проходит работа с .xlsx файлом, для краткости и наглядности приведен цикл.

m1.py


import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql

class Example(QtWidgets.QMainWindow):
    def __init__(self):
        super(Example, self).__init__()
        QtWidgets.QMainWindow.__init__(self)

        self.initUI(self)

    def initUI(self, QProgressBar):

        btn_dir = QtWidgets.QPushButton('Запуск')
        btn_dir.clicked.connect(self.ot)

        self.progress = QtWidgets.QProgressBar(self, minimum=0, maximum=100)
        self.progress.setObjectName("progress")
        self.progress.setStyleSheet('text-align: center; '                                     
                       'min-height: 15px; max-height: 15px;')

        # self.timeLine = QtCore.QTimeLine(1000 * 10) # проверка
        # self.timeLine.setFrameRange(0, 100)
        # self.timeLine.frameChanged.connect(self.signal)
        # self.timeLine.start()

        self.progressbar_self()
        self.statusBar().insertPermanentWidget(1, self.progress)
        self.statusBar().addPermanentWidget(btn_dir)
        self.show()

    def ot(self):
        import otv

    def progressbar_self(self):
        import b1
        b1.self = self

    def progressbar_reset(self):
        self.progress.reset()

    def signal(self, value):
        print(f'прогресс процесса {value} %')
        self.progress.setValue(value)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec())

otv.py


from PyQt5.QtCore import (QDate, QObject, QEvent, pyqtSlot, pyqtSignal, QThread,
                          QTimeLine, QTimer, QSize, QSettings, QBasicTimer)
import os, sys
import time
import threading

from m1 import Example
from b1 import self

class Signaller(QObject):
    progress_chek = pyqtSignal(int)
    finished = pyqtSignal()

def func(signaller):
    il = 0
    signaller.progress_chek.emit(il)
    # signaller.progress_chek.connect(self.signal)
    Example.signal(self, value=il)
    for il in range(1, 101):
        if il % 10 == 0:
            # signaller.progress_chek.emit(il)
            # signaller.progress_chek.connect(self.signal)
            Example.signal(self, value=il)
        if il > 70:
            time.sleep(1.0)
            # signaller.progress_chek.emit(il)
            # signaller.progress_chek.connect(self.signal)
            Example.signal(self, value=il)
            il = il + 1
        if result_a.wait(timeout=0.1):
            print()
            print('\r ... на {}% закрыли поток ...'.format(il))
            time.sleep(0.1)
            # signaller.progress_chek.emit(100)
            # signaller.progress_chek.connect(self.signal)
            Example.signal(self, value=100)
            break

signaller = Signaller()
result_a = threading.Event()

try:
    # signaller.progress_chek.connect(self.signal)
    # signaller.finished.connect(self.progressbar_reset)
    thread = threading.Thread(target=func, args=(signaller,), daemon=True)
    thread.start()
except Exception as exc:
    print('ProgressBarа ! = ', exc)

print()
print('_______________________________________________________')
print()

def widg_gotov():
    time.sleep(5.0)
    print('обнулить ProgressBarа')
    Example.progressbar_reset(self)

def while_x(x):
    print('x1 =', x)
    while x < 200:
        x = x+10
        time.sleep(0.4)
        print('x2 =', x)

x = 0
while_x(x)
print()
print('1: thread.isAlive =', thread.isAlive())
if thread.isAlive():
    result_a.set()
    time.sleep(1.0)
    print('2: thread.isAlive =', thread.isAlive())

widg_gotov()
print('_______________________________________________________')

b1.py

self = []

if __name__ == '__main__':
    print()

Ответы (1 шт):

Автор решения: S. Nick

Sorry, я ничего не понял в вашей солянке. Если я вас правильно понял, это может выглядеть примерно так. Если это то что вы хотите и вам что-то не понятно - спросите.

import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
from PyQt5.Qt import *


class Thread(QtCore.QThread):   
    progress_chek = pyqtSignal(int)
    finished = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.x = 0
        
    def run(self):
        while self.x <= 200:
            self.progress_chek.emit(self.x)
            self.x += 10
            self.msleep(400)            
            #print('x2 =', self.x)
        self.finished.emit()


class Example(QtWidgets.QMainWindow):
    def __init__(self):
        super(Example, self).__init__()

        self.centralWidget = QtWidgets.QWidget()
        self.setCentralWidget(self.centralWidget)

        self.frame = QtWidgets.QFrame()
        self.frame.setStyleSheet("background-color: rgb(146, 43, 63);")
        self.label = QLabel()
        self.label.setStyleSheet("color: #FBFFE2; font-size: 22px;")
        layoutH = QtWidgets.QHBoxLayout(self.frame)
        layoutH.addWidget(self.label, alignment = Qt.AlignCenter)
        
        self.btn_dir = QtWidgets.QPushButton('Запуск')
        self.btn_dir.clicked.connect(self.ot)

        self.progress = QtWidgets.QProgressBar(self, minimum=0, maximum=200)
        self.progress.setObjectName("progress")
        self.progress.setStyleSheet('text-align: center; '                                     
                       'min-height: 20px; max-height: 20px;')
        
        layout = QtWidgets.QGridLayout(self.centralWidget) 
        layout.addWidget(self.frame, 0, 0, 1, 2)
        layout.addWidget(self.progress, 1, 0)
        layout.addWidget(self.btn_dir, 1, 1)

        self.thread = Thread() 
        self.thread.progress_chek.connect(self.update_progressbar)
        self.thread.finished.connect(self.finished_progressbar)

    def update_progressbar(self, val):
        self.progress.setValue(val)
        if val*0.1 % 2:
            self.label.setText("Выполняется процесс, ожидайте ...")
        else:
            self.label.setText("Выполняется процесс, ожидайте    ")

    def finished_progressbar(self):
        self.btn_dir.setEnabled(True)
        self.label.clear()
        msg = QtWidgets.QMessageBox.information(
            self, 
            'ВНИМАНИЕ', 
            'Процесс завершен - можете закрывать окно!'
        )
        self.progress.reset()
            

    def ot(self):
        self.thread.x = 0
        self.progress.reset()
        self.progress.setValue(0)
        
        self.thread.start()
        self.btn_dir.setEnabled(False)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = Example()
    ex.resize(500, 300)
    ex.show()
    sys.exit(app.exec())

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

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

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

→ Ссылка