Posts: 50
Threads: 23
Joined: Apr 2020
Jul-12-2022, 01:45 PM
(This post was last modified: Jul-12-2022, 01:45 PM by maiya.)
Hi All,
I want to popup a progress bar, while executing a task parallelly. Progress bar should display (as a popup) while clicking some button (from another widget) internally this button leads to execute some task. While executing this task parallelly progress bar popup should display the progress of this task and once task done (executed) and then progress bar pop up should disappear automatically.
I have got some thing from the google, but progress bar itself taking some time and once after disappear this progress bar and then only actual task started executing. Which I really do not want.
Any help/suggestion on this would really help.
Regards,
maiya
Posts: 1,027
Threads: 16
Joined: Dec 2016
Jul-12-2022, 02:27 PM
(This post was last modified: Jul-12-2022, 02:27 PM by Axel_Erfurt.)
It might be better to show the progress bar when needed, e.g. in the status bar. Here's an example
import sys
from PyQt5 import QtGui, QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 500, 100)
self.setWindowTitle("ProgressBar")
self.setWindowIcon(QtGui.QIcon.fromTheme('python3'))
self.statusBar().showMessage("Ready", 0)
self.btn = QtWidgets.QPushButton("Download",self)
self.btn.move(10,10)
self.btn.clicked.connect(self.download)
self.progress_bar = QtWidgets.QProgressBar()
self.statusBar().addPermanentWidget(self.progress_bar)
self.progress_bar.hide()
def download(self):
self.completed = 0
self.progress_bar.setFixedSize(self.geometry().width() - 120, 16)
self.progress_bar.show()
self.statusBar().showMessage("downloading ...", 0)
while self.completed < 100:
self.completed += 0.00005
self.progress_bar.setValue(int(self.completed))
if self.progress_bar.value() == 100:
self.statusBar().showMessage("completed", 0)
self.progress_bar.hide()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
Posts: 50
Threads: 23
Joined: Apr 2020
Jul-12-2022, 08:54 PM
(This post was last modified: Jul-12-2022, 08:54 PM by maiya.)
(Jul-12-2022, 02:27 PM)Axel_Erfurt Wrote: It might be better to show the progress bar when needed, e.g. in the status bar. Here's an example
import sys
from PyQt5 import QtGui, QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 500, 100)
self.setWindowTitle("ProgressBar")
self.setWindowIcon(QtGui.QIcon.fromTheme('python3'))
self.statusBar().showMessage("Ready", 0)
self.btn = QtWidgets.QPushButton("Download",self)
self.btn.move(10,10)
self.btn.clicked.connect(self.download)
self.progress_bar = QtWidgets.QProgressBar()
self.statusBar().addPermanentWidget(self.progress_bar)
self.progress_bar.hide()
def download(self):
self.completed = 0
self.progress_bar.setFixedSize(self.geometry().width() - 120, 16)
self.progress_bar.show()
self.statusBar().showMessage("downloading ...", 0)
while self.completed < 100:
self.completed += 0.00005
self.progress_bar.setValue(int(self.completed))
if self.progress_bar.value() == 100:
self.statusBar().showMessage("completed", 0)
self.progress_bar.hide()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_()) I do not need this download button here on this widget, but from the another widget. As soon as I press this download button from the another widget, it should start download a file, however at the same time it should popup a progress bar as an indication to the user that screen is not hang, but it is downloading..
Here in this example, what are you downloading?, which I could not see (or probably it is missing here). It is just a download button, but actually no downloading happening..
I want the real downloading and parallelly the progress bar too.
Any help? Thanks.
Regards,
maiya
Posts: 1,027
Threads: 16
Joined: Dec 2016
(Jul-12-2022, 08:54 PM)maiya Wrote: Here in this example, what are you downloading?
Nothing, it's just an example to show progressbar.
Here's an old example to make a real download with progressbar.
PyQt5_Downloader.py
If you want an extra popup for the progress bar you will have to put in a lot of effort to make it work.
Posts: 8
Threads: 1
Joined: Jun 2019
Aug-08-2022, 05:39 PM
(This post was last modified: Aug-08-2022, 05:39 PM by mfitzp.)
(Jul-12-2022, 02:27 PM)Axel_Erfurt Wrote: It might be better to show the progress bar when needed, e.g. in the status bar. Here's an example
def download(self):
self.completed = 0
self.progress_bar.setFixedSize(self.geometry().width() - 120, 16)
self.progress_bar.show()
self.statusBar().showMessage("downloading ...", 0)
while self.completed < 100:
self.completed += 0.00005
self.progress_bar.setValue(int(self.completed))
if self.progress_bar.value() == 100:
self.statusBar().showMessage("completed", 0)
self.progress_bar.hide()
# nothing will happen until we reach here
This example won't work unfortunately. In PyQt updates (adding widgets, changing things) only happen when you return to the event loop (by returning from your Python code). If you run the example, you'll notice that after pressing the button nothing happens, then *everything* happens at once: once the loop finishes, Qt gets to processing all the events you've created in one burst.
You can fudge this by calling QApplication.processEvents() in your loop -- although this wouldn't help for the real case with downloading a file as that's a single continuous process. The correct way to do this would be [using a separate thread, probably using QThreadPool]( https://www.pythonguis.com/tutorials/mul...hreadpool/) that leaves the UI free to update. You can emit the progress from the worker using signals and then send that to any progressbar you want (popup or in the statusbar).
Posts: 6,779
Threads: 20
Joined: Feb 2020
Or you could use a QTimer to periodically call a function to update the progress bar.
If you can't measure progress and are using a progress bar as a busy indicator set the minimum and maximum value to zero.
Posts: 1,027
Threads: 16
Joined: Dec 2016
import sys
from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit, QProgressBar, QApplication,
QVBoxLayout, QHBoxLayout, QLabel, QFileDialog)
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QThread, pyqtSignal, QSettings, QStandardPaths
import requests
import subprocess
################################################
################################################
class Downloader(QWidget):
def __init__(self, *args, **kwargs):
super(Downloader, self).__init__(*args, **kwargs)
self.settings = QSettings("MyDownloader", "MyDownloader")
self.setWindowTitle("Downloader")
self.setWindowIcon(QIcon.fromTheme("download"))
layout = QVBoxLayout(self)
hlayout = QHBoxLayout()
self.dpath = ""
print(self.dpath)
self.readSettings()
self.url = ""
self.fname = ""
self.setFixedSize(600, 170)
# Download Button
self.pushButton = QPushButton(self, maximumWidth=100)
self.pushButton.setToolTip('<b>Download</b>')
self.pushButton.setText("Download")
self.pushButton.setIcon(QIcon.fromTheme("download"))
hlayout.addWidget(self.pushButton)
self.pushButton.clicked.connect(self.on_pushButton_clicked)
# Cancel Button
self.cancelButton = QPushButton(self, maximumWidth=100)
self.cancelButton.setText("Cancel")
self.cancelButton.setIcon(QIcon.fromTheme("cancel"))
hlayout.addWidget(self.cancelButton)
self.cancelButton.setEnabled(False)
self.cancelButton.clicked.connect(self.on_cancelButton_clicked)
# Settings Button
self.settingsButton = QPushButton(self, maximumWidth=32)
self.settingsButton.setToolTip("choose Download Folder")
self.settingsButton.setIcon(QIcon.fromTheme("folder"))
hlayout.addWidget(self.settingsButton)
self.settingsButton.clicked.connect(self.on_settingsButton_clicked)
# Space to align Buttons left
empty = QWidget()
hlayout.addWidget(empty)
# Bar
self.progressBar = QProgressBar(self, minimumWidth=300)
self.progressBar.setFixedHeight(14)
self.progressBar.setValue(0)
self.progressBar.hide()
# Url Field
self.urlfield = QLineEdit()
self.urlfield.setPlaceholderText("URL")
self.urlfield.textChanged.connect(self.extractFilename)
layout.addWidget(self.urlfield)
# Name Field
self.namefield = QLineEdit()
self.namefield.setPlaceholderText("Filename")
layout.addWidget(self.namefield)
layout.addWidget(self.progressBar)
# StatusBar
self.lbl = QLabel("status")
layout.addLayout(hlayout)
layout.addWidget(self.lbl)
self.lbl.setText(f"Ready - Download Path: {self.dpath}")
self.clip = QApplication.clipboard()
if self.clip.text().startswith("http"):
self.urlfield.setText(self.clip.text())
def on_settingsButton_clicked(self):
path = QFileDialog.getExistingDirectory(self, "Select Folder", self.dpath)
if path:
self.dpath = path
self.settings.setValue("folder", self.dpath)
self.lbl.setText(f"changed Download Path to: {self.dpath}")
else:
return
def writeSettings(self):
self.settings.setValue("folder", self.dpath)
def readSettings(self):
if self.settings.contains("folder"):
self.dpath = self.settings.value("folder")
else:
self.dpath = QStandardPaths.standardLocations(QStandardPaths.MoviesLocation)[0]
def extractFilename(self):
t = self.urlfield.text().split('/')[-1]
self.namefield.setText(f"{self.dpath}/{t}")
def on_pushButton_clicked(self):
if self.urlfield.text().startswith("http") or self.urlfield.text().startswith("ftp"):
the_url = self.urlfield.text()
the_filesize = requests.get(the_url, stream=True).headers['Content-Length']
the_filepath = self.namefield.text()
the_fileobj = open(the_filepath, 'wb')
#### Create a download thread
self.downloadThread = downloadThread(the_url, the_filesize, the_fileobj, buffer=10240)
self.downloadThread.download_proess_signal.connect(self.set_progressbar_value)
self.downloadThread.start()
self.lbl.setText("Download started ...")
self.progressBar.show()
self.cancelButton.setEnabled(True)
# Setting progress bar
def set_progressbar_value(self, value):
self.progressBar.setValue(value)
if value == 100:
self.lbl.setText("Download success!")
self.sendMessage()
self.progressBar.hide()
def on_cancelButton_clicked(self):
self.downloadThread.terminate()
self.lbl.setText("Download cancelled")
self.cancelButton.setEnabled(False)
def sendMessage(self):
title = 'Downloader'
message = 'Download success!'
icon = "info"
timeout = 5000
msg = ["notify-send", "-i", icon, title, message, '-t', str(timeout)]
subprocess.Popen(msg)
##################################################################
#Download thread
##################################################################
class downloadThread(QThread):
download_proess_signal = pyqtSignal(int) #Create signal
def __init__(self, url, filesize, fileobj, buffer):
super(downloadThread, self).__init__()
self.url = url
self.filesize = filesize
self.fileobj = fileobj
self.buffer = buffer
def run(self):
try:
rsp = requests.get(self.url, stream=True) #Streaming download mode
offset = 0
for chunk in rsp.iter_content(chunk_size=self.buffer):
if not chunk: break
self.fileobj.seek(offset) #Setting Pointer Position
self.fileobj.write(chunk) #write file
offset = offset + len(chunk)
proess = offset / int(self.filesize) * 100
self.download_proess_signal.emit(int(proess)) #Sending signal
#######################################################################
self.fileobj.close() #Close file
self.exit(0) #Close thread
except Exception as e:
print(e)
####################################
#Program entry
####################################
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Downloader()
w.show()
sys.exit(app.exec_())
Posts: 8
Threads: 1
Joined: Jun 2019
(Aug-08-2022, 06:38 PM)deanhystad Wrote: Or you could use a QTimer to periodically call a function to update the progress bar.
If you can't measure progress and are using a progress bar as a busy indicator set the minimum and maximum value to zero.
QTimer has the same issue actually, if you're downloading in the GUI thread, the timer events will be blocked (and won't occur) until your method returns, e.g.
from PyQt5.QtWidgets import QApplication, QPushButton
from PyQt5.QtCore import QTimer
import time
STATE_A = "State A"
STATE_B = "STATE b"
class Window(QPushButton):
def __init__(self):
super().__init__()
self.setText("State A")
self.clicked.connect(self.thread_blocking_task)
self.timer = QTimer()
self.timer.timeout.connect(self.toggle_button_text)
self.timer.start(100) # 100 msec intervals
def toggle_button_text(self):
self.setText(STATE_B if self.text() == STATE_A else STATE_A)
def thread_blocking_task(self):
time.sleep(5) # Sleep for 5 seconds.
app = QApplication([])
w = Window()
w.show()
app.exec() The example by @ Axel_Erfurt is the way to do it: moving the download into a separate thread so the GUI is not blocked.
|