PyQt5:进度指标作为叠加

时间:2017-01-25 23:12:52

标签: multithreading python-3.x pyqt pyqt5 python-multithreading

我再次使用PyQt5,这次我遇到了如何在执行方法需要更长时间时向我的gui添加类似"circular progress indicator"的东西。

为了做到这一点,我有一个(超级)简单的gui只有一个按钮。单击该按钮后,应用程序将打印15秒钟" hello world!"到控制台(这只是为了看到它实际上在做什么)。

我现在的问题是:我怎样才能覆盖"当一个方法的执行需要超过例如4秒(直到方法结束,然后进度指示器应该再次消失)?我有一个来自here的进度指示器的示例脚本。但不幸的是,当我把它添加到我的玩具示例中时,我迷失了方向。这里的任何帮助将非常感谢。

我正在使用:PyQt5.6.0Python3.5.2

我的代码:

design.py (gui文件)

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'design.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(300, 300)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(105, 140, 113, 32))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))

main.py :(主要脚本):

from PyQt5 import QtWidgets
import time
from design import Ui_MainWindow


class ToyEx(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.gorepeat)

    def gorepeat(self):
        """
        print sth for 15 seconds
        """
        end_time = time.time() + 15 * 1
        print(end_time)
        while time.time() < end_time:
            print('hello world!')

if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    form = ToyEx()
    form.show()
    app.exec_()

progress_indicator.py :(进度指示器的代码,改编自here

"""
adapted to PyQt5 from:

  Author: Jared P. Sutton <jpsutton@gmail.com>
  License: LGPL
  Note: I've licensed this code as LGPL because it was a complete translation of the code found here...
    https://github.com/mojocorp/QProgressIndicator

  sourcecode from here: https://github.com/mojocorp/QProgressIndicator

"""

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import time


class QProgressIndicator(QWidget):
    m_angle = None
    m_timerId = None
    m_delay = None
    m_displayedWhenStopped = None
    m_color = None

    def __init__(self, parent):
        # Call parent class constructor first
        super(QProgressIndicator, self).__init__(parent)

        # Initialize Qt Properties
        self.setProperties()

        # Intialize instance variables
        self.m_angle = 0
        self.m_timerId = -1
        self.m_delay = 40
        self.m_displayedWhenStopped = False
        self.m_color = Qt.black

        # Set size and focus policy
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.setFocusPolicy(Qt.NoFocus)

        # Show the widget
        self.show()

    def animationDelay(self):
        return self.delay

    def isAnimated(self):
        return (self.m_timerId != -1)

    def isDisplayedWhenStopped(self):
        return self.displayedWhenStopped

    def getColor(self):
        return self.color

    def sizeHint(self):
        return QSize(20, 20)

    def startAnimation(self):
        self.m_angle = 0

        if self.m_timerId == -1:
            self.m_timerId = self.startTimer(self.m_delay)

    def stopAnimation(self):
        if self.m_timerId != -1:
            self.killTimer(self.m_timerId)

        self.m_timerId = -1
        self.update()

    def setAnimationDelay(self, delay):
        if self.m_timerId != -1:
            self.killTimer(self.m_timerId)

        self.m_delay = delay

        if self.m_timerId != -1:
            self.m_timerId = self.startTimer(self.m_delay)

    def setDisplayedWhenStopped(self, state):
        self.displayedWhenStopped = state
        self.update()

    def setColor(self, color):
        self.m_color = color
        self.update()

    def timerEvent(self, event):
        self.m_angle = (self.m_angle + 30) % 360
        self.update()

    def paintEvent(self, event):
        if (not self.m_displayedWhenStopped) and (not self.isAnimated()):
            return

        width = min(self.width(), self.height())

        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        outerRadius = (width - 1) * 0.5
        innerRadius = (width - 1) * 0.5 * 0.48

        capsuleHeight = outerRadius - innerRadius
        capsuleWidth = capsuleHeight * .23 if (width > 32) else capsuleHeight * .35
        capsuleRadius = capsuleWidth / 2

        for i in range(0, 12):
            color = QColor(self.m_color)

            if self.isAnimated():
                color.setAlphaF(1.0 - (i / 12.0))
            else:
                color.setAlphaF(0.2)

            painter.setPen(Qt.NoPen)
            painter.setBrush(color)
            painter.save()
            painter.translate(self.rect().center())
            painter.rotate(self.m_angle - (i * 30.0))
            painter.drawRoundedRect(capsuleWidth * -0.5, (innerRadius + capsuleHeight) * -1, capsuleWidth,
                                    capsuleHeight, capsuleRadius, capsuleRadius)
            painter.restore()

    def setProperties(self):
        self.delay = pyqtProperty(int, self.animationDelay, self.setAnimationDelay)
        self.displayedWhenStopped = pyqtProperty(bool, self.isDisplayedWhenStopped, self.setDisplayedWhenStopped)
        self.color = pyqtProperty(QColor, self.getColor, self.setColor)


def TestProgressIndicator():
    app = QApplication(sys.argv)
    t_end = time.time() + 10 *1
    progress = QProgressIndicator(None)
    progress.setAnimationDelay(70)
    progress.startAnimation()

    # Execute the application
    sys.exit(app.exec_())


if __name__ == "__main__":
    TestProgressIndicator()

修改: 在包含进度指示器时,我使用threading模块再次尝试了一些东西。以下是更新后的代码:

design.py (gui文件,包含进度指示器):

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'design.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(300, 300)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(105, 140, 113, 32))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))


class QProgressIndicator(QtWidgets.QWidget):
    m_angle = None
    m_timerId = None
    m_delay = None
    m_displayedWhenStopped = None
    m_color = None

    def __init__(self, parent):
        # Call parent class constructor first
        super(QProgressIndicator, self).__init__(parent)
        # palette = QPalette(self.palette())
        # palette.setColor(palette.Background, Qt.transparent)
        # self.setPalette(palette)

        # Initialize Qt Properties
        self.setProperties()

        # Intialize instance variables
        self.m_angle = 0
        self.m_timerId = -1
        self.m_delay = 40
        self.m_displayedWhenStopped = False
        #self.m_color = Qt.black
        self.m_color = QtCore.Qt.transparent

        # Set size and focus policy
        self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        self.setFocusPolicy(QtCore.Qt.NoFocus)

        # Show the widget
        self.show()

    def animationDelay(self):
        return self.delay

    def isAnimated(self):
        return (self.m_timerId != -1)

    def isDisplayedWhenStopped(self):
        return self.displayedWhenStopped

    def getColor(self):
        return self.color

    def sizeHint(self):
        return QtCore.QSize(50, 50)

    def startAnimation(self):
        self.m_angle = 0

        if self.m_timerId == -1:
            self.m_timerId = self.startTimer(self.m_delay)

    def stopAnimation(self):
        if self.m_timerId != -1:
            self.killTimer(self.m_timerId)

        self.m_timerId = -1
        self.update()

    def setAnimationDelay(self, delay):
        if self.m_timerId != -1:
            self.killTimer(self.m_timerId)

        self.m_delay = delay

        if self.m_timerId != -1:
            self.m_timerId = self.startTimer(self.m_delay)

    def setDisplayedWhenStopped(self, state):
        self.displayedWhenStopped = state
        self.update()

    def setColor(self, color):
        self.m_color = color
        self.update()

    def timerEvent(self, event):
        self.m_angle = (self.m_angle + 30) % 360
        self.update()

    def paintEvent(self, event):
        if (not self.m_displayedWhenStopped) and (not self.isAnimated()):
            return

        width = min(self.width(), self.height())

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        outerRadius = (width - 1) * 0.5
        innerRadius = (width - 1) * 0.5 * 0.48

        capsuleHeight = outerRadius - innerRadius
        capsuleWidth = capsuleHeight * .23 if (width > 32) else capsuleHeight * .35
        capsuleRadius = capsuleWidth / 2

        for i in range(0, 12):
            color = QtGui.QColor(self.m_color)

            if self.isAnimated():
                color.setAlphaF(1.0 - (i / 12.0))
            else:
                color.setAlphaF(0.2)

            painter.setPen(QtCore.Qt.NoPen)
            painter.setBrush(color)
            painter.save()
            painter.translate(self.rect().center())
            painter.rotate(self.m_angle - (i * 30.0))
            painter.drawRoundedRect(capsuleWidth * -0.5, (innerRadius + capsuleHeight) * -1, capsuleWidth,
                                    capsuleHeight, capsuleRadius, capsuleRadius)
            painter.restore()

    def setProperties(self):
        self.delay = QtCore.pyqtProperty(int, self.animationDelay, self.setAnimationDelay)
        self.displayedWhenStopped = QtCore.pyqtProperty(bool, self.isDisplayedWhenStopped, self.setDisplayedWhenStopped)
        self.color = QtCore.pyqtProperty(QtGui.QColor, self.getColor, self.setColor)

main.py (已更新):

from PyQt5 import QtWidgets
from threading import Thread, Timer
from design import Ui_MainWindow, QProgressIndicator
import time


class ToyEx(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.threadfuncts)

    def threadfuncts(self):
        Thread(target=gorepeat).start()
        Timer(4, Thread(target=test_progressindicator).start())  # <-- THIS DOES NOT WORK!


def gorepeat():
    """
    print sth for 15 seconds
    """
    end_time = time.time() + 15 * 1
    while time.time() < end_time:
        print('hello world!')


def test_progressindicator():
    progress = QProgressIndicator(None)
    progress.setAnimationDelay(70)
    progress.startAnimation()


if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    form = ToyEx()
    form.show()
    app.exec_()

但是当我运行此代码时,它已经在一两秒后崩溃了。方法threadfuncts()不应该以第一个函数(gorepeat())被调用的方式处理线程,4秒后第二个函数(test_progressindicator())被调用(然后他们并行运行)?如果我将test_progressindicator()定义为另一个简单地打印到控制台的函数,那么线程&#34;类型为&#34;工作(4秒的延迟不起作用 - 这是另一点,我不明白为什么它不起作用。)

再说一遍,如果有人能在这里帮助我,我非常感激......

0 个答案:

没有答案