Qt-在所有小部件上方显示小部件或标签

时间:2020-08-01 02:37:01

标签: python qt loading pyside2

我想在用户每次按下按钮时显示一个加载屏幕(运行过程需要几秒钟)。

我想要这样的东西

QSplashScreen对我没有帮助,因为它仅在打开应用程序之前使用,而QDialog对我没有用,因为我希望通过拖动窗口来使应用程序随消息一起移动...

我必须使用什么?

3 个答案:

答案 0 :(得分:0)

请检查Qt :: WindowFlags。 Qt :: SplashScreen标志将在不使用QSplashScreen的情况下为您提供闪屏体验(您可以将其与所有小部件一起使用,如图所示),或者更好地,将QDialog与该标志一起使用。 对于移动,可能没有好的解决方案,但是您可以仅使用父级moveEvent发出信号。例如: 主视窗: moveEvent->信号已移动 对话: 信号移动->重新居中窗口。 它看起来并不难。

顺便说一句,我认为在应用程序运行期间阻止所有GUI并不是最好的解决方案。您认为使用QProgressBar吗?

答案 1 :(得分:0)

您可以使用以下插槽void QWidget::raise()。 将这个小部件提升到父小部件堆栈的顶部。 调用后,小部件将在任何重叠的同级小部件之前出现在视觉上

答案 2 :(得分:0)

实现此目的的唯一(安全)方法是添加子窗口小部件,而无需将其添加到任何布局管理器。

您唯一需要关心的是,小部件始终在显示后始终https://www.youtube.com/watch?v=KcC8KZ_Ga2M,并且几何图形始终会更新为父小部件(或者最好是顶层窗口)。

这是一个稍微高级的示例,但是它的好处是您可以对 any 小部件进行子类化,将LoadingWidget类添加到基类中以实现加载机制。

raised

from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets

class Loader(QtWidgets.QWidget):
    def __init__(self, parent):
        super().__init__(parent)

        self.gradient = QtGui.QConicalGradient(.5, .5, 0)
        self.gradient.setCoordinateMode(self.gradient.ObjectBoundingMode)
        self.gradient.setColorAt(.25, QtCore.Qt.transparent)
        self.gradient.setColorAt(.75, QtCore.Qt.transparent)

        self.animation = QtCore.QVariantAnimation(
            startValue=0., endValue=1., 
            duration=1000, loopCount=-1, 
            valueChanged=self.updateGradient
            )

        self.stopTimer = QtCore.QTimer(singleShot=True, timeout=self.stop)

        self.focusWidget = None
        self.hide()
        parent.installEventFilter(self)

    def start(self, timeout=None):
        self.show()
        self.raise_()
        self.focusWidget = QtWidgets.QApplication.focusWidget()
        self.setFocus()
        if timeout:
            self.stopTimer.start(timeout)
        else:
            self.stopTimer.setInterval(0)

    def stop(self):
        self.hide()
        self.stopTimer.stop()
        if self.focusWidget:
            self.focusWidget.setFocus()
            self.focusWidget = None

    def updateGradient(self, value):
        self.gradient.setAngle(-value * 360)
        self.update()

    def eventFilter(self, source, event):
        # ensure that we always cover the whole parent area
        if event.type() == QtCore.QEvent.Resize:
            self.setGeometry(source.rect())
        return super().eventFilter(source, event)

    def showEvent(self, event):
        self.setGeometry(self.parent().rect())
        self.animation.start()

    def hideEvent(self, event):
        # stop the animation when hidden, just for performance
        self.animation.stop()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        color = self.palette().window().color()
        color.setAlpha(max(color.alpha() * .5, 128))
        qp.fillRect(self.rect(), color)

        text = 'Loading...'
        interval = self.stopTimer.interval()
        if interval:
            remaining = int(max(0, interval - self.stopTimer.remainingTime()) / interval * 100)
            textWidth = self.fontMetrics().width(text + ' 000%')
            text += ' {}%'.format(remaining)
        else:
            textWidth = self.fontMetrics().width(text)
        textHeight = self.fontMetrics().height()
        # ensure that there's enough space for the text
        if textWidth > self.width() or textHeight * 3 > self.height():
            drawText = False
            size = max(0, min(self.width(), self.height()) - textHeight * 2)
        else:
            size = size = min(self.height() / 3, max(textWidth, textHeight))
            drawText = True

        circleRect = QtCore.QRect(0, 0, size, size)
        circleRect.moveCenter(self.rect().center())

        if drawText:
            # text is going to be drawn, move the circle rect higher
            circleRect.moveTop(circleRect.top() - textHeight)
            middle = circleRect.center().x()
            qp.drawText(
                middle - textWidth / 2, circleRect.bottom() + textHeight, 
                textWidth, textHeight, 
                QtCore.Qt.AlignCenter, text)

        self.gradient.setColorAt(.5, self.palette().windowText().color())
        qp.setPen(QtGui.QPen(self.gradient, textHeight))
        qp.drawEllipse(circleRect)


class LoadingExtension(object):
    # a base class to extend any QWidget subclass's top level window with a loader
    def startLoading(self, timeout=0):
        window = self.window()
        if not hasattr(window, '_loader'):
            window._loader = Loader(window)
        window._loader.start(timeout)

        # this is just for testing purposes
        if not timeout:
            QtCore.QTimer.singleShot(randrange(1000, 5000), window._loader.stop)

    def loadingFinished(self):
        if hasattr(self.window(), '_loader'):
            self.window()._loader.stop()


class Test(QtWidgets.QWidget, LoadingExtension):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QGridLayout(self)

        # just a test widget
        textEdit = QtWidgets.QTextEdit()
        layout.addWidget(textEdit, 0, 0, 1, 2)
        textEdit.setMinimumHeight(20)

        layout.addWidget(QtWidgets.QLabel('Timeout:'))
        self.timeoutSpin = QtWidgets.QSpinBox(maximum=5000, singleStep=250, specialValueText='Random')
        layout.addWidget(self.timeoutSpin, 1, 1)
        self.timeoutSpin.setValue(2000)

        btn = QtWidgets.QPushButton('Start loading...')
        layout.addWidget(btn, 2, 0, 1, 2)
        btn.clicked.connect(lambda: self.startLoading(self.timeoutSpin.value()))


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    test = Test()
    test.show()
    sys.exit(app.exec_())