Обновление хода PyQt из другого потока, выполняющего загрузку по FTP

Я хочу получить доступ к индикатору выполнения (который находится в Ui_MainWindow() классе) setMaximum() из другого класса / потока (DownloadThread() класс).

Я пробовал сделать DownloadThread() класс наследуемым от Ui_MainWindow: DownloadThread(Ui_MainWindow). Но когда я пытаюсь установить максимальное значение индикатора выполнения:

Ui_MainWindow.progressBar.setMaximum(100)

Я получаю такую ​​ошибку:

AttributeError: объект типа 'Ui_MainWindow' не имеет атрибута 'progressBar'

Мой код:

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # ...
        self.updateButton = QtGui.QPushButton(self.centralwidget)
        self.progressBar = QtGui.QProgressBar(self.centralwidget)
        self.updateStatusText = QtGui.QLabel(self.centralwidget)
        # ...
        self.updateButton.clicked.connect(self.download_file)
        # ...

    def download_file(self):
        self.thread = DownloadThread()
        self.thread.data_downloaded.connect(self.on_data_ready)
        self.thread.start()

    def on_data_ready(self, data):
        self.updateStatusText.setText(str(data))


class DownloadThread(QtCore.QThread, Ui_MainWindow):

    data_downloaded = QtCore.pyqtSignal(object)

    def run(self):
        self.data_downloaded.emit('Status: Connecting...')

        ftp = FTP('example.com')
        ftp.login(user='user', passwd='pass')

        ftp.cwd('/some_directory/')

        filename = '100MB.bin'
        totalsize = ftp.size(filename)
        print(totalsize)

        # SET THE MAXIMUM VALUE OF THE PROGRESS BAR
        Ui_MainWindow.progressBar.setMaximum(totalsize)          

        self.data_downloaded.emit('Status: Downloading...')

        global localfile
        with open(filename, 'wb') as localfile:
            ftp.retrbinary('RETR ' + filename, self.file_write)

        ftp.quit()
        localfile.close()

        self.data_downloaded.emit('Status: Updated!')

    def file_write(self, data):
        global localfile
        localfile.write(data)
        print(len(data))

person Qiasm    schedule 22.08.2019    source источник
comment
Опубликуйте минимально воспроизводимый пример.   -  person Heike    schedule 22.08.2019
comment
Я полагаю, что это класс потока, который вы вызываете, поэтому вам понадобится сигнал, чтобы сделать это и получить доступ к индикатору выполнения из главного окна   -  person Ευάγγελος Γρηγορόπουλος    schedule 22.08.2019
comment
@ ΕυάγγελοςΓρηγορόπουλος Как подать сигнал?   -  person Qiasm    schedule 22.08.2019
comment
вот код: hastebin.com/fequboleho.rb   -  person Qiasm    schedule 22.08.2019
comment
Ui_MainWindow.progressBar.setMaximum(100) должно быть self.progressBar.setMaximum(100).   -  person Heike    schedule 22.08.2019
comment
@Heike Хотя это правда, вы не можете получить доступ к виджетам PyQt из других потоков.   -  person Martin Prikryl    schedule 22.08.2019
comment
В вашем коде нет file_write.   -  person Martin Prikryl    schedule 22.08.2019
comment
@MartinPrikryl Верно, но OP пытается сделать не это.   -  person Heike    schedule 22.08.2019
comment
@Heike OP пытается позвонить progressBar.setMaximum из DownloadThread. Так что простое self.progressBar.setMaximum(100) не поможет (хотя это действительно улучшение).   -  person Martin Prikryl    schedule 22.08.2019


Ответы (2)


Непосредственная проблема заключается в том, что Ui_MainWindow - это класс, а не его экземпляр. Вам придется передать свое "окно" self DownloadThread. Но в любом случае это неправильное решение. Вы не можете получить доступ к виджетам PyQt из другого потока. Вместо этого используйте тот же метод, что и вы, для обновления текста статуса (FTP-загрузка с текстовой меткой, показывающей текущий статус загрузка).

class Ui_MainWindow(object):
    def download_file(self):
        self.thread = DownloadThread()
        self.thread.data_downloaded.connect(self.on_data_ready)
        self.thread.data_progress.connect(self.on_progress_ready)
        self.progress_initialized = False
        self.thread.start()

    def on_progress_ready(self, data):
        # The first signal sets the maximum, the other signals increase a progress
        if self.progress_initialized:
            self.progressBar.setValue(self.progressBar.value() + int(data))
        else:
            self.progressBar.setMaximum(int(data))
            self.progress_initialized = True

class DownloadThread(QtCore.QThread):

    data_downloaded = QtCore.pyqtSignal(object)
    data_progress = QtCore.pyqtSignal(object)

    def run(self):
        self.data_downloaded.emit('Status: Connecting...')

        with FTP('example.com') as ftp:
            ftp.login(user='user', passwd='pass')

            ftp.cwd('/some_directory/')

            filename = '100MB.bin'
            totalsize = ftp.size(filename)
            print(totalsize)

            # The first signal sets the maximum
            self.data_progress.emit(str(totalsize))

            self.data_downloaded.emit('Status: Downloading...')

            with open(filename, 'wb') as self.localfile:
                ftp.retrbinary('RETR ' + filename, self.file_write)

        self.data_downloaded.emit('Status: Updated!')

    def file_write(self, data):
        self.localfile.write(data)
        # The other signals increase a progress
        self.data_progress.emit(str(len(data)))

Другие изменения в вашем коде:

  • global localfile - плохая практика. Вместо этого используйте self.localfile.
  • В localfile.close() нет необходимости, with об этом позаботится.
  • Аналогично ftp.quit() следует заменить на with.
  • DownloadThread не нужно наследовать от Ui_MainWindow.
person Martin Prikryl    schedule 22.08.2019
comment
@Aspect Кстати, global localfile - плохая практика. Вместо этого используйте self.localfile. + Нет необходимости в self.localfile.close(), with позаботится об этом. + DownloadThread не нужно наследовать от Ui_MainWindow. --- Я тоже исправил это в своем ответе. - person Martin Prikryl; 22.08.2019

класс потока:

from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport,QtWebEngineWidgets
from PyQt5.QtWidgets import QDialog,QWidget,QApplication, QInputDialog, QLineEdit, QFileDialog,QProgressDialog, QMainWindow, QFrame,QSplashScreen
from PyQt5.QtCore import QThread , pyqtSignal,Qt
from PyQt5.QtGui import QIcon,QPainter,QPixmap

class threaded_class(QThread):

    signal_to_send_at_progress_bar = pyqtSignal()
    def __init__(self,parent=None):
        QThread.__init__(self, parent=parent)
    def run(self):
        while self.isRunning:
            ##do the stuf you want here and when you want to change the progress bar
            self.signal_to_send_at_progress_bar.emit()

в главном окне:

class mainProgram(QtWidgets.QMainWindow, Ui_MainWindow):                   #main window


    def __init__(self, parent=None):

        super(mainProgram, self).__init__(parent)
        self.setupUi(self)
        ###...........#####
        self.thread_class_in_main_window = threaded_class()
        self.thread_class_in_main_window .start()
        self.thread_db.signal_to_send_at_progress_bar.connect(progressBar.setMaximum(100))

Вы также можете выдавать строку и число с сигналами.

person Ευάγγελος Γρηγορόπουλος    schedule 22.08.2019