Issue
I'm trying to make a project, and I wanted to put shadow behind my QSplashScreen. Is there a way to do it ?
I searched, and the QGraphicsDropShadowEffect didn't work.
Solution
Shadow effects are normally a responsibility of the OS.
For systems where those effects are not available you can create a "virtual" rasterized shadow, but there are some drawbacks:
- if the system does support shadow effects, the result will be ugly, as the system shadow will be drawn along with the custom one;
- the shadow effect is applied upon the appearance of the whole "root" window, so if some of the contents change while the splash is shown, those changes will not be reflected in the shadow effect;
- if the system does not support opaque move events (meaning that
moveEvent()
is only sent when a movement is completed), the shadow effect will show some artifacts;
This is a possible reimplementation of QSplashScreen that shows a shadow effect. The trick is to create a temporary QGraphicsScene and add a QGraphicsRectItem that has a QGraphicsDropShadowEffect applied on it.
class SplashTest(QtWidgets.QSplashScreen):
_shadowSize = 8
shadowCache = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowFlags(QtCore.Qt.SplashScreen)
self.desktopPixmap = QtWidgets.QApplication.primaryScreen().grabWindow(0)
@QtCore.pyqtProperty(int)
def shadowSize(self):
return self._shadowSize
@shadowSize.setter
def shadowSize(self, shadowSize):
if shadowSize == self._shadowSize:
return
self._shadowSize = shadowSize
if self.shadowCache is None:
self.shadowCache = {}
else:
self.shadowCache.clear()
self.repaint()
def setShadowSize(self, shadowSize):
self.shadowSize = shadowSize
def setPixmap(self, pixmap):
self.shadowCache.clear()
super().setPixmap(pixmap)
size = QtCore.QSize(pixmap.width() + self.shadowSize, pixmap.height() + self.shadowSize)
self.setFixedSize(size)
if self.isVisible():
self.repaint()
def drawShadow(self, event):
if not self.pixmap() or self.pixmap().isNull():
return
shadowCache = self.shadowCache.get((self.width(), self.height()))
if not shadowCache:
# create the shadow effect if it's not cached yet
self.shadowCache[(self.width(), self.height())] = shadowCache = QtGui.QPixmap(self.size())
shadowCache.fill(QtCore.Qt.transparent)
# create a virtual QGraphicsScene to paint onto
scene = QtWidgets.QGraphicsScene()
scene.setSceneRect(QtCore.QRectF(self.desktopPixmap.rect()))
# add a QGraphicsRectItem to the scene that will be used for the shadow
pmItem = scene.addRect(QtCore.QRectF(self.pixmap().rect()).translated(self.geometry().topLeft()))
pmItem.setPen(QtGui.QPen(QtCore.Qt.NoPen))
pmItem.setBrush(QtGui.QBrush(QtCore.Qt.white))
offset = QtCore.QPoint(self.shadowSize / 2, self.shadowSize / 2)
effect = QtWidgets.QGraphicsDropShadowEffect(blurRadius=self.shadowSize, offset=offset)
pmItem.setGraphicsEffect(effect)
pmPainter = QtGui.QPainter(shadowCache)
pmPainter.setRenderHints(pmPainter.Antialiasing)
scene.render(pmPainter, QtCore.QRectF(self.rect()), pmItem.sceneBoundingRect().adjusted(0, 0, self.shadowSize, self.shadowSize))
pmPainter.end()
qp = QtGui.QPainter(self)
# draw the background taken from the root window
qp.drawPixmap(event.rect(), self.desktopPixmap, event.rect().translated(self.pos()))
# draw the shadow
qp.drawPixmap(0, 0, shadowCache)
def event(self, event):
if event.type() == QtCore.QEvent.Paint:
# intercept the Paint event in order to draw the shadow
self.drawShadow(event)
return super().event(event)
def moveEvent(self, event):
self.repaint()
def updateSplash():
# remember: globals should *ALWAYS* be avoided unless you *really* know how to
# use them; this is just for the purpose of this example
global loadingStatus
loadingStatus += 1
splashTest.showMessage('Loading: {}%'.format(loadingStatus))
if loadingStatus >= 100:
timer.stop()
QtCore.QTimer.singleShot(1000, lambda: splashTest.showMessage('Completed'))
QtCore.QTimer.singleShot(5000, splashTest.close)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
splashTest = SplashTest(shadowSize=16)
splashTest.setPixmap(QtGui.QPixmap('splash.png'))
loadingStatus = 0
timer = QtCore.QTimer(interval=50, timeout=updateSplash)
timer.start()
splashTest.show()
sys.exit(app.exec_())
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.