pyqt5:qprogressbar不会更新
我有一个应用程序,其中其中一个功能是使用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
当<<<代码>进度信号是从worker
类发出的。如您所见,我在运行
方法中有一个print()
语句,以检查代码实际上是否输入该,如果 block block和定期进行。将下载进度打印到控制台,这是。该应用程序完成运行后的应用程序会更新,因此我将获得最终的,完全填充的进度栏,但是在开始和结束之间,进度栏并未按预期进行更新。
任何帮助将不胜感激!
I have an application where one of its functions is to use pytube to download YouTube videos. Everything works perfectly except for updating the QProgressBar with the download progress. Please see below code:
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?')
I have used this basic structure in other parts of my application to display and update a progress bar with no issues, however for some reason here, self.pBar
refuses to update when the progress
signal is emitted from the Worker
class. As you can see, I have a print()
statement in the run
method to check that the code is actually entering that if
block and periodically printing out the download progress to the console, which it is. The application updates when the thread has finished running, so I get the final, completely filled progress bar, but in between the start and end the progress bar does not update as expected.
Any help would be greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题似乎是,尽管您努力正确地将所有这些线程工具组件连接起来,但您仍在主(UI)线程中运行
self.download_video()
。因此,它阻止了UI的刷新。如果要在辅助线程中运行,则必须在worker.run()
函数中启动它,而不是worker
构造函数。如果正确连接所有线程,则无需调用ProcessEvents()
以刷新UI。如果不确定下载功能运行的线程,请在打印功能中打印线程ID,然后将其与主UI线程的线程ID进行比较。看看 https://doc.qt.io/qt.io/qt-5/ qthread.html#CurrentThread
The problem seems to be that despite all your effort to wire up all that thread-worker components correctly, you are still running
self.download_video()
in the main (UI) thread. And therefore it is blocking refreshing of the UI. If you want to run in in the secondary thread, you must start it inWorker.run()
function and not in theWorker
constructor. If you wire up all threading correctly, then you should not need to callprocessEvents()
at all to refresh UI.If you are unsure about the thread in which the download function runs, print the thread ID in your print function and compare it to thread id of the main UI thread. Have a look at https://doc.qt.io/qt-5/qthread.html#currentThread