Создание невидимого слоя в PyQt, который покрывает весь Diaglo - PullRequest
0 голосов
/ 13 сентября 2018

Я хочу показать спиннер во время выполнения задачи в моем приложении pyqt5.Я нашел эту хорошую реализацию счетчика, поэтому я попробовал: https://github.com/z3ntu/QtWaitingSpinner

Демонстрация работает нормально, но в демонстрационной версии счетчик отображается в пустой области диалога.Я хотел бы, чтобы это было наложение, которое охватывает весь диалог.

Автор QtWaitingSpinner предполагает, что «В качестве альтернативного примера приведенный ниже код создаст спиннер, который (1) блокирует весь пользовательский ввод на основнойприложение, пока спиннер активен, (2) автоматически центрируется на родительском виджете каждый раз, когда вызывается «start», и (3) использует настройки формы, размера и цвета по умолчанию ».со следующим кодом:

spinner = QtWaitingSpinner(self, True, True, Qt.ApplicationModal)
spinner.start() # starts spinning

Но я попробовал эту реализацию в качестве примера, и она не сработала:

from PyQt5.QtWidgets import QApplication, QDialog, QTabWidget, QWidget, QGroupBox, QPushButton, QVBoxLayout
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
import requests
import urllib
from waitingspinnerwidget import QtWaitingSpinner

class DownloadDataDialog(QDialog):
    def __init__(self, parent=None):
        super(DownloadDataDialog, self).__init__(parent)

        self.spinner = QtWaitingSpinner(self, True, True, Qt.ApplicationModal)

        tabWidget = QTabWidget(self)
        tabWidget.addTab(MyTab(tabWidget), "MyTab")

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(tabWidget)
        self.setLayout(mainLayout)

        self.setWindowTitle("Download option chain data from web")

class MyTab(QWidget):
    def __init__(self, parent=None):
        super(MyTab, self).__init__(parent)

        dataGroup = QGroupBox('Data')

        getButton = QPushButton('Download')
        getButton.clicked.connect(self.download_data)

        dataLayout = QVBoxLayout()
        dataLayout.addWidget(getButton)
        dataGroup.setLayout(dataLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(dataGroup)
        mainLayout.addStretch(1)
        self.setLayout(mainLayout)

    def download_data(self):
        self.parent().parent().parent().spinner.start()
        url = 'http://www.meff.es/docs/Ficheros/Descarga/dRV/RV180912.zip'
        filepath = None
        try:
            filepath = self.download_data_file(url)
        except Exception as e:
            print(e)

        self.parent().parent().parent().spinner.stop()
        if filepath:
            #TODO doing stuff here
            self.parent().parent().parent().close()
        else:
            pass #TODO show error dialog

    def download_data_file(self, download_url):           
        # Build request URL and download the file
        destination = 'test.zip'
        urllib.request.urlretrieve(download_url, destination)
        return destination

if __name__ == '__main__':

    import sys

    app = QApplication(sys.argv)

    tabdialog = DownloadDataDialog()
    tabdialog.show()
    sys.exit(app.exec_())

Так что я собираюсь создать невидимый слой, установитьspinner как его единственный виджет и показывающий прозрачный слой по всему диалоговому окну.

Есть идеи, как мне это сделать?

1 Ответ

0 голосов
/ 13 сентября 2018

После того, как у меня тоже возникла эта проблема, я изменил библиотеку, сначала активируйте флаги: QtCore.Qt.Dialog | QtCore.Qt.FramelessWindowHint, а другое изменение необходимо выполнить в методе updatePosition ():

def updatePosition(self):
    if self.parentWidget() and self._centerOnParent:
        parentRect = QtCore.QRect(self.parentWidget().mapToGlobal(QtCore.QPoint(0, 0)), self.parentWidget().size())
        self.move(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, self.size(), parentRect).topLeft())

следующим образом:

waitingspinnerwidget.py

import math
from PyQt5 import QtCore, QtGui, QtWidgets


class QtWaitingSpinner(QtWidgets.QWidget):
    def __init__(self, parent=None, centerOnParent=True, disableParentWhenSpinning=False, modality=QtCore.Qt.NonModal):
        super().__init__(parent, flags=QtCore.Qt.Dialog | QtCore.Qt.FramelessWindowHint)
        self._centerOnParent = centerOnParent
        self._disableParentWhenSpinning = disableParentWhenSpinning

        # WAS IN initialize()
        self._color = QtGui.QColor(QtCore.Qt.black)
        self._roundness = 100.0
        self._minimumTrailOpacity = 3.14159265358979323846
        self._trailFadePercentage = 80.0
        self._revolutionsPerSecond = 1.57079632679489661923
        self._numberOfLines = 20
        self._lineLength = 10
        self._lineWidth = 2
        self._innerRadius = 10
        self._currentCounter = 0
        self._isSpinning = False

        self._timer = QtCore.QTimer(self)
        self._timer.timeout.connect(self.rotate)
        self.updateSize()
        self.updateTimer()
        self.hide()
        # END initialize()

        self.setWindowModality(modality)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

    def paintEvent(self, QPaintEvent):
        self.updatePosition()
        painter = QtGui.QPainter(self)
        painter.fillRect(self.rect(), QtCore.Qt.transparent)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)

        if self._currentCounter >= self._numberOfLines:
            self._currentCounter = 0

        painter.setPen(QtCore.Qt.NoPen)
        for i in range(0, self._numberOfLines):
            painter.save()
            painter.translate(self._innerRadius + self._lineLength, self._innerRadius + self._lineLength)
            rotateAngle = float(360 * i) / float(self._numberOfLines)
            painter.rotate(rotateAngle)
            painter.translate(self._innerRadius, 0)
            distance = self.lineCountDistanceFromPrimary(i, self._currentCounter, self._numberOfLines)
            color = self.currentLineColor(distance, self._numberOfLines, self._trailFadePercentage,
                                          self._minimumTrailOpacity, self._color)
            painter.setBrush(color)
            painter.drawRoundedRect(QtCore.QRect(0, -self._lineWidth / 2, self._lineLength, self._lineWidth), self._roundness,
                                    self._roundness, QtCore.Qt.RelativeSize)
            painter.restore()

    def start(self):
        self.updatePosition()
        self._isSpinning = True
        self.show()

        if self.parentWidget and self._disableParentWhenSpinning:
            self.parentWidget().setEnabled(False)

        if not self._timer.isActive():
            self._timer.start()
            self._currentCounter = 0

    def stop(self):
        self._isSpinning = False
        self.hide()

        if self.parentWidget() and self._disableParentWhenSpinning:
            self.parentWidget().setEnabled(True)

        if self._timer.isActive():
            self._timer.stop()
            self._currentCounter = 0

    def setNumberOfLines(self, lines):
        self._numberOfLines = lines
        self._currentCounter = 0
        self.updateTimer()

    def setLineLength(self, length):
        self._lineLength = length
        self.updateSize()

    def setLineWidth(self, width):
        self._lineWidth = width
        self.updateSize()

    def setInnerRadius(self, radius):
        self._innerRadius = radius
        self.updateSize()

    def color(self):
        return self._color

    def roundness(self):
        return self._roundness

    def minimumTrailOpacity(self):
        return self._minimumTrailOpacity

    def trailFadePercentage(self):
        return self._trailFadePercentage

    def revolutionsPersSecond(self):
        return self._revolutionsPerSecond

    def numberOfLines(self):
        return self._numberOfLines

    def lineLength(self):
        return self._lineLength

    def lineWidth(self):
        return self._lineWidth

    def innerRadius(self):
        return self._innerRadius

    def isSpinning(self):
        return self._isSpinning

    def setRoundness(self, roundness):
        self._roundness = max(0.0, min(100.0, roundness))

    def setColor(self, color=QtCore.Qt.black):
        self._color = QColor(color)

    def setRevolutionsPerSecond(self, revolutionsPerSecond):
        self._revolutionsPerSecond = revolutionsPerSecond
        self.updateTimer()

    def setTrailFadePercentage(self, trail):
        self._trailFadePercentage = trail

    def setMinimumTrailOpacity(self, minimumTrailOpacity):
        self._minimumTrailOpacity = minimumTrailOpacity

    def rotate(self):
        self._currentCounter += 1
        if self._currentCounter >= self._numberOfLines:
            self._currentCounter = 0
        self.update()

    def updateSize(self):
        size = (self._innerRadius + self._lineLength) * 2
        self.setFixedSize(size, size)

    def updateTimer(self):
        self._timer.setInterval(1000 / (self._numberOfLines * self._revolutionsPerSecond))

    def updatePosition(self):
        if self.parentWidget() and self._centerOnParent:
            parentRect = QtCore.QRect(self.parentWidget().mapToGlobal(QtCore.QPoint(0, 0)), self.parentWidget().size())
            self.move(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, self.size(), parentRect).topLeft())


    def lineCountDistanceFromPrimary(self, current, primary, totalNrOfLines):
        distance = primary - current
        if distance < 0:
            distance += totalNrOfLines
        return distance

    def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc, minOpacity, colorinput):
        color = QtGui.QColor(colorinput)
        if countDistance == 0:
            return color
        minAlphaF = minOpacity / 100.0
        distanceThreshold = int(math.ceil((totalNrOfLines - 1) * trailFadePerc / 100.0))
        if countDistance > distanceThreshold:
            color.setAlphaF(minAlphaF)
        else:
            alphaDiff = color.alphaF() - minAlphaF
            gradient = alphaDiff / float(distanceThreshold + 1)
            resultAlpha = color.alphaF() - gradient * countDistance
            # If alpha is out of bounds, clip it.
            resultAlpha = min(1.0, max(0.0, resultAlpha))
            color.setAlphaF(resultAlpha)
        return color

С учетом вышеизложенного мы решаем одну из этих проблем, другая проблема заключается в том, что urllib.request.urlretrieve() блокирует, поэтому это приведет кGUI, чтобы заморозить, поэтому решение состоит в том, чтобы переместить его в другой поток, используя предыдущий ответ, мы можем сделать это следующим образом:

from PyQt5 import QtCore, QtGui, QtWidgets
import urllib.request
from waitingspinnerwidget import QtWaitingSpinner


class RequestRunnable(QtCore.QRunnable):
    def __init__(self, url, destination, dialog):
        super(RequestRunnable, self).__init__()
        self._url = url
        self._destination = destination
        self.w = dialog

    def run(self):
        urllib.request.urlretrieve(self._url, self._destination)
        QMetaObject.invokeMethod(self.w, "FinishedDownload", QtCore.Qt.QueuedConnection)


class DownloadDataDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(DownloadDataDialog, self).__init__(parent)
        self.spinner = QtWaitingSpinner(self, True, True, QtCore.Qt.ApplicationModal)
        tabWidget = QtWidgets.QTabWidget(self)
        tabWidget.addTab(MyTab(), "MyTab")
        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.addWidget(tabWidget)
        self.setWindowTitle("Download option chain data from web")


class MyTab(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MyTab, self).__init__(parent)

        dataGroup = QtWidgets.QGroupBox('Data')

        getButton = QtWidgets.QPushButton('Download')
        getButton.clicked.connect(self.download_data)

        dataLayout = QtWidgets.QVBoxLayout(self)
        dataLayout.addWidget(getButton)

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.addWidget(dataGroup)
        mainLayout.addStretch(1)

    def download_data(self):
        self.parentWidget().window().spinner.start()
        url = 'http://www.meff.es/docs/Ficheros/Descarga/dRV/RV180912.zip'
        destination = 'test.zip'
        runnable = RequestRunnable(url, destination, self)
        QtCore.QThreadPool.globalInstance().start(runnable)

    @QtCore.pyqtSlot()
    def FinishedDownload(self):
        self.parentWidget().window().spinner.stop()


if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    tabdialog = DownloadDataDialog()
    tabdialog.show()
    sys.exit(app.exec_())

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...