PyQt5:QProgressBar 不会更新
PyQt5: QProgressBar will not update
我有一个应用程序,其功能之一是使用 pytube 下载 YouTube 视频。除了用下载进度更新 QProgressBar 之外,一切都完美无缺。请看下面的代码:
import pytube
import PyQt5.QtWidgets as QtWidgets
import PyQt5.QtCore as QtCore
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
progress = QtCore.pyqtSignal(str, int, int)
def __init__(self, url, save_path, fname):
super(Worker, self).__init__()
self.url = url
self.save_path = save_path
self.fname = fname
self.perc_dl = 0
self.download_video()
def run(self):
if self.perc_dl < 100:
self.progress.emit('Downloading: {}%'.format(str(self.perc_dl)), int(self.perc_dl), 100)
print(self.perc_dl)
else:
self.progress.emit('Done!', 100, 100)
self.finished.emit()
def download_video(self):
yt = pytube.YouTube(self.url, on_progress_callback=self.progress_function)
stream = yt.streams.filter(progressive=True, file_extension='mp4').get_highest_resolution()
stream.download(output_path=self.save_path, filename=self.fname)
def progress_function(self, stream, chunk, bytes_remaining):
curr = stream.filesize - bytes_remaining
per_downloaded = round((curr / stream.filesize) * 100, 1)
self.perc_dl = per_downloaded
self.run()
class DownloadFromYT(QtWidgets.QDialog):
def __init__(self, url, save_path, fname):
super(DownloadFromYT, self).__init__()
self.url = url
self.save_path = save_path
self.fname = fname
self.vLayoutMaster = QtWidgets.QVBoxLayout()
self.hLayout = QtWidgets.QHBoxLayout()
self.hLayout.setAlignment(QtCore.Qt.AlignRight)
self.label = QtWidgets.QLabel()
self.label.setText('Downloading video')
self.pBar = QtWidgets.QProgressBar()
self.pBar.setInvertedAppearance(True)
self.pBar.setTextVisible(True)
self.pBar.setFixedWidth(200)
self.pBar.setAlignment(QtCore.Qt.AlignCenter)
self.pBar.setMaximum(100)
self.pBar.setFormat('Downloading: ')
self.noButton = QtWidgets.QPushButton('No')
self.noButton.setFixedWidth(50)
self.noButton.hide()
self.yesButton = QtWidgets.QPushButton('Yes')
self.yesButton.setFixedWidth(50)
self.yesButton.hide()
self.vLayoutMaster.addWidget(self.label)
self.vLayoutMaster.addSpacing(10)
self.vLayoutMaster.addWidget(self.pBar)
self.vLayoutMaster.addSpacing(20)
self.hLayout.addWidget(self.noButton)
self.hLayout.addWidget(self.yesButton)
self.vLayoutMaster.addLayout(self.hLayout)
# Signals / slots
self.noButton.clicked.connect(self.reject)
self.yesButton.clicked.connect(self.accept)
# Widget
self.setLayout(self.vLayoutMaster)
self.setWindowTitle('Downloading')
self.setFixedSize(250, 100)
self.show()
# Init download
self.run_thread(url, save_path, fname)
def run_thread(self, url, save_path, fname):
self.thrd = QtCore.QThread()
self.worker = Worker(url, save_path, fname)
self.worker.moveToThread(self.thrd)
self.thrd.start()
self.thrd.started.connect(self.worker.run)
self.worker.finished.connect(self.thrd.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.progress.connect(self.show_progress)
self.thrd.finished.connect(self.thrd.deleteLater)
def show_progress(self, label, n, total):
self.pBar.setFormat(label)
self.pBar.setMaximum(total)
self.pBar.setValue(n)
QtCore.QCoreApplication.processEvents()
if label == 'Done!':
self.thrd.quit()
self.noButton.show()
self.yesButton.show()
self.label.setText('Video has been downloaded. Would you like to set the chosen\n'
'file path as the "Local file" path?')
我在我的应用程序的其他部分使用了这个基本结构来显示和更新进度条,没有任何问题,但是由于某些原因,self.pBar
在 progress
信号时拒绝更新从 Worker
class 发出。如您所见,我在 run
方法中有一个 print()
语句来检查代码是否确实进入了 if
块并定期将下载进度打印到控制台,它是。应用程序在线程完成时更新 运行,所以我得到了最终的、完全填充的进度条,但在开始和结束之间,进度条没有按预期更新。
如有任何帮助,我们将不胜感激!
问题似乎是,尽管您付出了所有努力来正确连接所有 thread-worker 组件,但您仍然 运行ning self.download_video()
在主要(UI) 线。因此它阻止了 UI 的刷新。如果你想 运行 在辅助线程中,你必须在 Worker.run()
函数中启动它,而不是在 Worker
构造函数中。如果您正确连接了所有线程,那么您根本不需要调用 processEvents()
来刷新 UI.
如果您不确定下载函数 运行 所在的线程,请在打印函数中打印线程 ID,并将其与主 UI 线程的线程 ID 进行比较。看看https://doc.qt.io/qt-5/qthread.html#currentThread
我有一个应用程序,其功能之一是使用 pytube 下载 YouTube 视频。除了用下载进度更新 QProgressBar 之外,一切都完美无缺。请看下面的代码:
import pytube
import PyQt5.QtWidgets as QtWidgets
import PyQt5.QtCore as QtCore
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
progress = QtCore.pyqtSignal(str, int, int)
def __init__(self, url, save_path, fname):
super(Worker, self).__init__()
self.url = url
self.save_path = save_path
self.fname = fname
self.perc_dl = 0
self.download_video()
def run(self):
if self.perc_dl < 100:
self.progress.emit('Downloading: {}%'.format(str(self.perc_dl)), int(self.perc_dl), 100)
print(self.perc_dl)
else:
self.progress.emit('Done!', 100, 100)
self.finished.emit()
def download_video(self):
yt = pytube.YouTube(self.url, on_progress_callback=self.progress_function)
stream = yt.streams.filter(progressive=True, file_extension='mp4').get_highest_resolution()
stream.download(output_path=self.save_path, filename=self.fname)
def progress_function(self, stream, chunk, bytes_remaining):
curr = stream.filesize - bytes_remaining
per_downloaded = round((curr / stream.filesize) * 100, 1)
self.perc_dl = per_downloaded
self.run()
class DownloadFromYT(QtWidgets.QDialog):
def __init__(self, url, save_path, fname):
super(DownloadFromYT, self).__init__()
self.url = url
self.save_path = save_path
self.fname = fname
self.vLayoutMaster = QtWidgets.QVBoxLayout()
self.hLayout = QtWidgets.QHBoxLayout()
self.hLayout.setAlignment(QtCore.Qt.AlignRight)
self.label = QtWidgets.QLabel()
self.label.setText('Downloading video')
self.pBar = QtWidgets.QProgressBar()
self.pBar.setInvertedAppearance(True)
self.pBar.setTextVisible(True)
self.pBar.setFixedWidth(200)
self.pBar.setAlignment(QtCore.Qt.AlignCenter)
self.pBar.setMaximum(100)
self.pBar.setFormat('Downloading: ')
self.noButton = QtWidgets.QPushButton('No')
self.noButton.setFixedWidth(50)
self.noButton.hide()
self.yesButton = QtWidgets.QPushButton('Yes')
self.yesButton.setFixedWidth(50)
self.yesButton.hide()
self.vLayoutMaster.addWidget(self.label)
self.vLayoutMaster.addSpacing(10)
self.vLayoutMaster.addWidget(self.pBar)
self.vLayoutMaster.addSpacing(20)
self.hLayout.addWidget(self.noButton)
self.hLayout.addWidget(self.yesButton)
self.vLayoutMaster.addLayout(self.hLayout)
# Signals / slots
self.noButton.clicked.connect(self.reject)
self.yesButton.clicked.connect(self.accept)
# Widget
self.setLayout(self.vLayoutMaster)
self.setWindowTitle('Downloading')
self.setFixedSize(250, 100)
self.show()
# Init download
self.run_thread(url, save_path, fname)
def run_thread(self, url, save_path, fname):
self.thrd = QtCore.QThread()
self.worker = Worker(url, save_path, fname)
self.worker.moveToThread(self.thrd)
self.thrd.start()
self.thrd.started.connect(self.worker.run)
self.worker.finished.connect(self.thrd.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.progress.connect(self.show_progress)
self.thrd.finished.connect(self.thrd.deleteLater)
def show_progress(self, label, n, total):
self.pBar.setFormat(label)
self.pBar.setMaximum(total)
self.pBar.setValue(n)
QtCore.QCoreApplication.processEvents()
if label == 'Done!':
self.thrd.quit()
self.noButton.show()
self.yesButton.show()
self.label.setText('Video has been downloaded. Would you like to set the chosen\n'
'file path as the "Local file" path?')
我在我的应用程序的其他部分使用了这个基本结构来显示和更新进度条,没有任何问题,但是由于某些原因,self.pBar
在 progress
信号时拒绝更新从 Worker
class 发出。如您所见,我在 run
方法中有一个 print()
语句来检查代码是否确实进入了 if
块并定期将下载进度打印到控制台,它是。应用程序在线程完成时更新 运行,所以我得到了最终的、完全填充的进度条,但在开始和结束之间,进度条没有按预期更新。
如有任何帮助,我们将不胜感激!
问题似乎是,尽管您付出了所有努力来正确连接所有 thread-worker 组件,但您仍然 运行ning self.download_video()
在主要(UI) 线。因此它阻止了 UI 的刷新。如果你想 运行 在辅助线程中,你必须在 Worker.run()
函数中启动它,而不是在 Worker
构造函数中。如果您正确连接了所有线程,那么您根本不需要调用 processEvents()
来刷新 UI.
如果您不确定下载函数 运行 所在的线程,请在打印函数中打印线程 ID,并将其与主 UI 线程的线程 ID 进行比较。看看https://doc.qt.io/qt-5/qthread.html#currentThread