I am trying to build an application using PyQt. A part of the application runs a thread which takes some time to complete. How can I add a waiting indicator (preferably circular) to indicate running of the process?
I know I can go with a progress bar, or perhaps a splash screen. I have looked into both but I am trying to opt for one of them only as a last resort. Can someone please help me with this?
Thank You
I have converted the code from the QtWaitingSpinner from C ++ to PyQt4/PyQt5
from math import ceil
"""from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *"""
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class QtWaitingSpinner(QWidget):
mColor = QColor(Qt.gray)
mRoundness = 100.0
mMinimumTrailOpacity = 31.4159265358979323846
mTrailFadePercentage = 50.0
mRevolutionsPerSecond = 1.57079632679489661923
mNumberOfLines = 20
mLineLength = 10
mLineWidth = 2
mInnerRadius = 20
mCurrentCounter = 0
mIsSpinning = False
def __init__(self, centerOnParent=True, disableParentWhenSpinning=True, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.mCenterOnParent = centerOnParent
self.mDisableParentWhenSpinning = disableParentWhenSpinning
self.initialize()
def initialize(self):
self.timer = QTimer(self)
self.timer.timeout.connect(self.rotate)
self.updateSize()
self.updateTimer()
self.hide()
@pyqtSlot()
def rotate(self):
self.mCurrentCounter += 1
if self.mCurrentCounter > self.numberOfLines():
self.mCurrentCounter = 0
self.update()
def updateSize(self):
size = (self.mInnerRadius + self.mLineLength) * 2
self.setFixedSize(size, size)
def updateTimer(self):
self.timer.setInterval(1000 / (self.mNumberOfLines * self.mRevolutionsPerSecond))
def updatePosition(self):
if self.parentWidget() and self.mCenterOnParent:
self.move(self.parentWidget().width() / 2 - self.width() / 2,
self.parentWidget().height() / 2 - self.height() / 2)
def lineCountDistanceFromPrimary(self, current, primary, totalNrOfLines):
distance = primary - current
if distance < 0:
distance += totalNrOfLines
return distance
def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc, minOpacity, color):
if countDistance == 0:
return color
minAlphaF = minOpacity / 100.0
distanceThreshold = ceil((totalNrOfLines - 1) * trailFadePerc / 100.0)
if countDistance > distanceThreshold:
color.setAlphaF(minAlphaF)
else:
alphaDiff = self.mColor.alphaF() - minAlphaF
gradient = alphaDiff / distanceThreshold + 1.0
resultAlpha = color.alphaF() - gradient * countDistance
resultAlpha = min(1.0, max(0.0, resultAlpha))
color.setAlphaF(resultAlpha)
return color
def paintEvent(self, event):
self.updatePosition()
painter = QPainter(self)
painter.fillRect(self.rect(), Qt.transparent)
painter.setRenderHint(QPainter.Antialiasing, True)
if self.mCurrentCounter > self.mNumberOfLines:
self.mCurrentCounter = 0
painter.setPen(Qt.NoPen)
for i in range(self.mNumberOfLines):
painter.save()
painter.translate(self.mInnerRadius + self.mLineLength,
self.mInnerRadius + self.mLineLength)
rotateAngle = 360.0 * i / self.mNumberOfLines
painter.rotate(rotateAngle)
painter.translate(self.mInnerRadius, 0)
distance = self.lineCountDistanceFromPrimary(i, self.mCurrentCounter,
self.mNumberOfLines)
color = self.currentLineColor(distance, self.mNumberOfLines,
self.mTrailFadePercentage, self.mMinimumTrailOpacity, self.mColor)
painter.setBrush(color)
painter.drawRoundedRect(QRect(0, -self.mLineWidth // 2, self.mLineLength, self.mLineLength),
self.mRoundness, Qt.RelativeSize)
painter.restore()
def start(self):
self.updatePosition()
self.mIsSpinning = True
self.show()
if self.parentWidget() and self.mDisableParentWhenSpinning:
self.parentWidget().setEnabled(False)
if not self.timer.isActive():
self.timer.start()
self.mCurrentCounter = 0
def stop(self):
self.mIsSpinning = False
self.hide()
if self.parentWidget() and self.mDisableParentWhenSpinning:
self.parentWidget().setEnabled(True)
if self.timer.isActive():
self.timer.stop()
self.mCurrentCounter = 0
def setNumberOfLines(self, lines):
self.mNumberOfLines = lines
self.updateTimer()
def setLineLength(self, length):
self.mLineLength = length
self.updateSize()
def setLineWidth(self, width):
self.mLineWidth = width
self.updateSize()
def setInnerRadius(self, radius):
self.mInnerRadius = radius
self.updateSize()
def color(self):
return self.mColor
def roundness(self):
return self.mRoundness
def minimumTrailOpacity(self):
return self.mMinimumTrailOpacity
def trailFadePercentage(self):
return self.mTrailFadePercentage
def revolutionsPersSecond(self):
return self.mRevolutionsPerSecond
def numberOfLines(self):
return self.mNumberOfLines
def lineLength(self):
return self.mLineLength
def lineWidth(self):
return self.mLineWidth
def innerRadius(self):
return self.mInnerRadius
def isSpinning(self):
return self.mIsSpinning
def setRoundness(self, roundness):
self.mRoundness = min(0.0, max(100, roundness))
def setColor(self, color):
self.mColor = color
def setRevolutionsPerSecond(self, revolutionsPerSecond):
self.mRevolutionsPerSecond = revolutionsPerSecond
self.updateTimer()
def setTrailFadePercentage(self, trail):
self.mTrailFadePercentage = trail
def setMinimumTrailOpacity(self, minimumTrailOpacity):
self.mMinimumTrailOpacity = minimumTrailOpacity
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dial = QDialog()
w = QtWaitingSpinner(dial)
dial.show()
w.start()
QTimer.singleShot(1000, w.stop)
sys.exit(app.exec_())
In the following link you can find a complete example
In addition to the above response...
If you are using Qt Designer you'll need to create a QWidget and promote it to a QtWaitingSpinner. You can follow this for promoting QWidgets. In this case, your Promoted Class name is QtWaitingSpinner and Header file is the file where the QtWaitingSpinner class is located.
Once your GUI is set you can call
your_QtWaitingSpinnerObject.start()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With