Exemplo n.º 1
0
class CImprovedButton(QToolButton):
    def __init__(self, parent=None):
        QToolButton.__init__(self, parent)

        #TESTO ALTERNATIVO
        #Spesso se il pulsante ha icona troppo grossa e quando per ragioni di spazio o altro non si può spostare
        #o ridimensionare il pulsante stesso, la label ha posizioni assurde e schifose. Aggiungerne una "+ controllabile"
        #è l'unico modo..
        self.__fixed_label = QLabel("alternative label", self)
        self.__fixed_label.move(0, self.geometry().height() - 35)
        self.__fixed_label.resize(self.geometry().width(), self.__fixed_label.geometry().height())
        self.__fixed_label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.__font = QtGui.QFont("Arial", 10)
        self.__fixed_label.setFont(self.__font)
        self.__fixed_label.show()

        #INDICATORE STILE iOS
        self.__indicator = QLabel("0", self)
        self.__indicator.setStyleSheet("border-image: url(':/images/backgrounds/indicator.png'); padding-right:1px; color: white;")
        self.__indicator.geometry().setWidth(25)
        self.__indicator.geometry().setHeight(20)
        self.__indicator.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.__indicator.setVisible(False)
        self.setIndicatorPos(QPoint(self.width() - self.__indicator.width(), 0)) #default top-right corner
        #Quando il pulsante viene ridimensionato (designer o meno) devo anche sistemare la label di conseguenza
        self.resizeEvent = self.__onResize
        self.__indicator.resizeEvent = self.__on_indicator_Resize

        self.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)

        self.clicked.connect(self.stopAllAnimations)

        #BLINK
        self.__blink_timer = QTimer(parent)
        self.__blink_timer.timeout.connect(self.__on_blink_timer)
        self.__blink_timer_interval = 1000

        #FADING
        self.__opacity_effect = QGraphicsOpacityEffect()
        self.__fading_timer = QTimer(parent)
        self.__fading_timer.timeout.connect(self.__on_fading_timer)
        self.__FADE_TYPE = Enum("IN", "OUT")
        self.__fade_time = 20
        self.__opacity = 1.0
        self.__opacity_fading_coefficient = 0.02
        self.__selected_fade_type = self.__FADE_TYPE.IN

        # ANIMAZIONI GROW
        self.__animationGrow = QPropertyAnimation(self, "iconSize", self)
        self.__animationGrow.setDuration(1000)
        self.__animationGrow.setEasingCurve(QEasingCurve.Linear)
        self.__animationGrow.finished.connect(self.__on_growed)

        self.__animationShrink = QPropertyAnimation(self, "iconSize", self)
        self.__animationShrink.setDuration(1000)
        self.__animationShrink.setEasingCurve(QEasingCurve.Linear)
        self.__animationShrink.finished.connect(self.__on_shrinked)

        self.__defaultIconDimension = 60
        self.__iconGrowsBy = 40
        self.__growing = False

        # ANIMAZIONI BOUNCE
        self.__animationUp = QPropertyAnimation(self, "pos", self)
        self.__animationUp.setDuration(200)
        self.__animationUp.setEasingCurve(QEasingCurve.Linear)
        self.__animationUp.finished.connect(self.__on_top_reached)

        self.__animationBounce = QPropertyAnimation(self, "pos", self)
        self.__animationBounce.setDuration(1000)
        self.__animationBounce.setEasingCurve(QEasingCurve.OutBounce)
        self.__animationBounce.finished.connect(self.__on_bounce_finished)

        self.__bouncing = False
        self.__startPos = QPoint(self.pos().x(), self.pos().y())

        #PIXMAP & MASCHERA
        self.__pmap = QPixmap(self.size())
        self.__pmap_fname = ""
        self.__show_mask_preview = False

    def setDefaultIconSize(self, value):
        """ Sets default icon size when growing stops.
        @param value: size (both width and height)
        @type value: int
        """
        self.__defaultIconDimension = value

    def getDefaultIconSize(self):
        return self.__defaultIconDimension

    defaultIconSize = QtCore.pyqtProperty("int", getDefaultIconSize, setDefaultIconSize)

    def setFixetTextVisibility(self, bool):
        """ Sets if fixed text is visible or not.
        @param bool: visible or not
        @type bool: bool
        """
        self.__fixed_label.setVisible(bool)

    def getFixetTextVisibility(self):
        return self.__fixed_label.isVisible()

    fixetTextVisibility = QtCore.pyqtProperty("bool", fget=getFixetTextVisibility, fset=setFixetTextVisibility)

    def setFixedText(self, txt):
        """ Sets text on the button.
        @param txt: text
        @type txt: string
        """
        self.__fixed_label.setText(txt)

    def getFixedText(self):
        return self.__fixed_label.text()

    fixedText = QtCore.pyqtProperty("QString", getFixedText, setFixedText)

    def setFixedTextPos(self, qpoint):
        """ Sets text position in the button.
        @param qpoint: Position RELATIVE. 0,0 is top left corner of the button.
        @type qpoint: QPoint
        """
        self.__fixed_label.move(qpoint)

    def getFixedTextPos(self):
        return self.__fixed_label.pos()

    fixedTextPos = QtCore.pyqtProperty("QPoint", getFixedTextPos, setFixedTextPos)

    def setFixedTextFont(self, font):
        """ Sets text font.
        @param font: Font for fixed text.
        @type font: QFont
        """
        self.__font = font
        self.__fixed_label.setFont(self.__font)

    def getFixedTextFont(self):
        return self.__font

    fixedTextFont = QtCore.pyqtProperty("QFont", getFixedTextFont, setFixedTextFont)

    #FUNZIONI INDICATORE
    def setIndicatorVisibility(self, bool):
        """ Sets if indicator is visible or not.
        @param bool: visible or not
        @type bool: bool
        """
        self.__indicator.setVisible(bool)

    def getIndicatorVisibility(self):
        return self.__indicator.isVisible()

    indicatorVisibility = QtCore.pyqtProperty("bool", fget=getIndicatorVisibility, fset=setIndicatorVisibility)

    def setIndicatorPos(self, qpoint):
        """ Sets indicator position in the button.
        @param qpoint: Position RELATIVE. 0,0 is top left corner of the button.
        @type qpoint: QPoint
        """
        self.__indicator.move(qpoint)

    def getIndicatorPos(self):
        return self.__indicator.pos()

    indicatorPos = QtCore.pyqtProperty("QPoint", getIndicatorPos, setIndicatorPos)

    def setIndicatorSize(self, size):
        """ Sets indicator size.
        @param size: Size
        @type size: QSize
        """
        self.__indicator.resize(size)

    def getIndicatorSize(self):
        return self.__indicator.size()

    indicatorSize = QtCore.pyqtProperty("QSize", getIndicatorSize, setIndicatorSize)

    def setIndicatorFont(self, font):
        """ Sets indicator text font.
        @param font: Font for indicator text.
        @type font: QFont
        """
        self.__indicator.setFont(font)

    def getIndicatorFont(self):
        return self.__indicator.font()

    indicatorFont = QtCore.pyqtProperty("QFont", getIndicatorFont, setIndicatorFont)


    ## FUNZIONI PER BLINK
    def __on_blink_timer(self):
        self.setVisible(not (self.isVisible()))

    def setBlinking(self, blink):
        """ Sets if the button have to blink or not.
        @param blink: blinking or not
        @type blink: bool
        """
        if blink:
            self.__blink_timer.setInterval(self.__blink_timer_interval)
            self.__blink_timer.start()
        else:
            self.__blink_timer.stop()
            self.setVisible(True)

    def setBlinkInterval(self, value):
        """ Sets blink interval.
        @param blink: blink interval (msec)
        @type blink: int
        """
        self.__blink_timer_interval = value

    def getBlinkInterval(self):
        return self.__blink_timer_interval

    blinkInterval = QtCore.pyqtProperty("int", getBlinkInterval, setBlinkInterval)


    ##FUNZIONI PER FADING
    def fadeIn(self):
        """
         Button fades in from completely invisible to completely visible.
        """
        self.__opacity = 0.0
        self.__selected_fade_type = self.__FADE_TYPE.IN
        self.__fading_timer.start(self.__fade_time)

    def fadeOut(self):
        """
         Button fades out from completely visible to completely invisible.
        """
        self.__selected_fade_type = self.__FADE_TYPE.OUT
        self.__fading_timer.start(self.__fade_time)

    def setFadeTime(self, value):
        """ Sets fading time. Everytime interval is reached, alpha is increased (or decreased) by __opacity_fading_coefficient.
        @param value: fade time (msec)
        @type value: int
        """
        self.__fade_time = value

    def getFadeTime(self):
        return self.__fade_time

    fadeInterval = QtCore.pyqtProperty("int", getFadeTime, setFadeTime)

    def setFadeCoefficient(self, value):
        """ Sets fading coefficient. Alpha is increased (or decreased) by this value.
        @param value: coefficient (min 0.0 - max 1.0)
        @type value: float
        """
        self.__opacity_fading_coefficient = value

    def getFadeCoefficient(self):
        return self.__opacity_fading_coefficient

    fadeCoefficient = QtCore.pyqtProperty("double", getFadeCoefficient, setFadeCoefficient)

    def __on_fading_timer(self):
        if self.__selected_fade_type == self.__FADE_TYPE.OUT:
            if self.__opacity > 0:
                self.__opacity -= self.__opacity_fading_coefficient
                self.__opacity_effect.setOpacity(self.__opacity)
                self.setGraphicsEffect(self.__opacity_effect)
            else:
                self.__fading_timer.stop()

        if self.__selected_fade_type == self.__FADE_TYPE.IN:
            if self.__opacity <= 1.0:
                self.__opacity += self.__opacity_fading_coefficient
                self.__opacity_effect.setOpacity(self.__opacity)
                self.setGraphicsEffect(self.__opacity_effect)
            else:
                self.__fading_timer.stop()

    # FUNZIONI PER GROW\SHRINK
    def __on_growed(self):
        self.__animationShrink.setStartValue(QSize(self.iconSize().width(), self.iconSize().height()))
        self.__animationShrink.setEndValue(
            QSize(self.iconSize().width() - self.__iconGrowsBy, self.iconSize().height() - self.__iconGrowsBy))
        self.__animationShrink.start()

    def __on_shrinked(self):
        self.__animationGrow.setStartValue(QSize(self.iconSize().width(), self.iconSize().height()))
        self.__animationGrow.setEndValue(
            QSize(self.iconSize().width() + self.__iconGrowsBy, self.iconSize().height() + self.__iconGrowsBy))
        self.__animationGrow.start()

    def startGrow(self):
        """
         Button ICON starts to grow and shrink to standard value when maximum size (configured) is reached
        """
        if self.__growing:
            return
        self.__animationGrow.setStartValue(QSize(self.iconSize().width(), self.iconSize().height()))
        self.__animationGrow.setEndValue(
            QSize(self.iconSize().width() + self.__iconGrowsBy, self.iconSize().height() + self.__iconGrowsBy))
        self.__animationGrow.start()
        self.__growing = True

    def stopGrow(self):
        if self.__animationGrow.startValue().toSize() != QSize(0,
                                                               0) and self.__animationShrink.startValue().toSize() != QPoint(
                0, 0):
            self.__animationGrow.stop()
            self.__animationShrink.stop()
            self.setIconSize(QSize(self.__defaultIconDimension, self.__defaultIconDimension))
            self.__growing = False
            
    #FUNZIONI PER BOUNCE
    def startBounce(self):
        """
         Button starts to bounce requiring attention.
        """
        if self.__bouncing:
            return
        self.__startPos = QPoint(self.pos().x(), self.pos().y())
        self.__animationUp.setStartValue(QPoint(self.__startPos.x(), self.__startPos.y()))
        self.__animationUp.setEndValue(QPoint(self.__startPos.x(), self.__startPos.y() - self.geometry().height()))
        self.__animationUp.start()
        self.__bouncing = True
        
    def stopBounce(self):
        if self.__animationUp.startValue().toPoint() != QPoint(0,0) and self.__animationBounce.startValue().toPoint() != QPoint(0,0):
            self.__animationBounce.stop()
            self.__animationUp.stop()
            self.setGeometry(self.__startPos.x(), self.__startPos.y(), self.geometry().width(), self.geometry().height())
            self.__bouncing = False

    def __on_top_reached(self):
        self.__animationBounce.setStartValue(QPoint(self.pos().x(), self.pos().y()))
        self.__animationBounce.setEndValue(QPoint(self.__startPos.x(), self.__startPos.y()))
        self.__animationBounce.start()

    def __on_bounce_finished(self):
        self.__animationUp.start()

    def stopAllAnimations(self):
        self.stopBounce()
        self.stopGrow()

    #FUNZIONI PER PIXMAP & MASCHERA
    def setPixmapFile(self, image_file):
        self.__pmap = image_file
        self.__pmap.scaled(self.size())
        #NB: Il pixmap deve essere BIANCO e NERO. Con heuristicMask il primo pixel in alto a sinistra (0,0) viene
        #usato per decidere il colore trasparente, tutto il resto è visibile.

    def getPixmapFile(self):
        return self.__pmap

    pixmapFile = QtCore.pyqtProperty("QPixmap", getPixmapFile, setPixmapFile)

    def applyMask(self, bool):
        self.__show_mask_preview = bool
        if bool:
            self.setMask(QBitmap(self.__pmap.createHeuristicMask().scaled(self.size())))
        else:
            self.setMask(QBitmap())

    def getMask(self):
        return self.__show_mask_preview

    appliedMask = QtCore.pyqtProperty("bool", fget=getMask, fset=applyMask)

    def __onResize(self, event):
        self.__fixed_label.move(0, self.geometry().height() - 35)
        self.__fixed_label.resize(self.geometry().width(), self.__fixed_label.geometry().height())
        self.setIndicatorPos(QPoint(self.width() - self.__indicator.width(), 0))
        self.__pmap.scaled(self.size())
        if self.__show_mask_preview:
            self.setMask(QBitmap(self.__pmap.createHeuristicMask().scaled(self.size())))

    def __on_indicator_Resize(self, event):
        self.setIndicatorPos(QPoint(self.width() - self.__indicator.width(), 0))
Exemplo n.º 2
0
class CImprovedButton(QToolButton):
    def __init__(self, parent=None):
        QToolButton.__init__(self, parent)

        #TESTO ALTERNATIVO
        #Spesso se il pulsante ha icona troppo grossa e quando per ragioni di spazio o altro non si può spostare
        #o ridimensionare il pulsante stesso, la label ha posizioni assurde e schifose. Aggiungerne una "+ controllabile"
        #è l'unico modo..
        self.__fixed_label = QLabel("alternative label", self)
        self.__fixed_label.move(0, self.geometry().height() - 35)
        self.__fixed_label.resize(self.geometry().width(),
                                  self.__fixed_label.geometry().height())
        self.__fixed_label.setAlignment(QtCore.Qt.AlignHCenter
                                        | QtCore.Qt.AlignVCenter)
        self.__font = QtGui.QFont("Arial", 10)
        self.__fixed_label.setFont(self.__font)
        self.__fixed_label.show()

        #INDICATORE STILE iOS
        self.__indicator = QLabel("0", self)
        self.__indicator.setStyleSheet(
            "border-image: url(':/images/backgrounds/indicator.png'); padding-right:1px; color: white;"
        )
        self.__indicator.geometry().setWidth(25)
        self.__indicator.geometry().setHeight(20)
        self.__indicator.setAlignment(QtCore.Qt.AlignHCenter
                                      | QtCore.Qt.AlignVCenter)
        self.__indicator.setVisible(False)
        self.setIndicatorPos(QPoint(self.width() - self.__indicator.width(),
                                    0))  #default top-right corner
        #Quando il pulsante viene ridimensionato (designer o meno) devo anche sistemare la label di conseguenza
        self.resizeEvent = self.__onResize
        self.__indicator.resizeEvent = self.__on_indicator_Resize

        self.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)

        self.clicked.connect(self.stopAllAnimations)

        #BLINK
        self.__blink_timer = QTimer(parent)
        self.__blink_timer.timeout.connect(self.__on_blink_timer)
        self.__blink_timer_interval = 1000

        #FADING
        self.__opacity_effect = QGraphicsOpacityEffect()
        self.__fading_timer = QTimer(parent)
        self.__fading_timer.timeout.connect(self.__on_fading_timer)
        self.__FADE_TYPE = Enum("IN", "OUT")
        self.__fade_time = 20
        self.__opacity = 1.0
        self.__opacity_fading_coefficient = 0.02
        self.__selected_fade_type = self.__FADE_TYPE.IN

        # ANIMAZIONI GROW
        self.__animationGrow = QPropertyAnimation(self, "iconSize", self)
        self.__animationGrow.setDuration(1000)
        self.__animationGrow.setEasingCurve(QEasingCurve.Linear)
        self.__animationGrow.finished.connect(self.__on_growed)

        self.__animationShrink = QPropertyAnimation(self, "iconSize", self)
        self.__animationShrink.setDuration(1000)
        self.__animationShrink.setEasingCurve(QEasingCurve.Linear)
        self.__animationShrink.finished.connect(self.__on_shrinked)

        self.__defaultIconDimension = 60
        self.__iconGrowsBy = 40
        self.__growing = False

        # ANIMAZIONI BOUNCE
        self.__animationUp = QPropertyAnimation(self, "pos", self)
        self.__animationUp.setDuration(200)
        self.__animationUp.setEasingCurve(QEasingCurve.Linear)
        self.__animationUp.finished.connect(self.__on_top_reached)

        self.__animationBounce = QPropertyAnimation(self, "pos", self)
        self.__animationBounce.setDuration(1000)
        self.__animationBounce.setEasingCurve(QEasingCurve.OutBounce)
        self.__animationBounce.finished.connect(self.__on_bounce_finished)

        self.__bouncing = False
        self.__startPos = QPoint(self.pos().x(), self.pos().y())

        #PIXMAP & MASCHERA
        self.__pmap = QPixmap(self.size())
        self.__pmap_fname = ""
        self.__show_mask_preview = False

    def setDefaultIconSize(self, value):
        """ Sets default icon size when growing stops.
        @param value: size (both width and height)
        @type value: int
        """
        self.__defaultIconDimension = value

    def getDefaultIconSize(self):
        return self.__defaultIconDimension

    defaultIconSize = QtCore.pyqtProperty("int", getDefaultIconSize,
                                          setDefaultIconSize)

    def setFixetTextVisibility(self, bool):
        """ Sets if fixed text is visible or not.
        @param bool: visible or not
        @type bool: bool
        """
        self.__fixed_label.setVisible(bool)

    def getFixetTextVisibility(self):
        return self.__fixed_label.isVisible()

    fixetTextVisibility = QtCore.pyqtProperty("bool",
                                              fget=getFixetTextVisibility,
                                              fset=setFixetTextVisibility)

    def setFixedText(self, txt):
        """ Sets text on the button.
        @param txt: text
        @type txt: string
        """
        self.__fixed_label.setText(txt)

    def getFixedText(self):
        return self.__fixed_label.text()

    fixedText = QtCore.pyqtProperty("QString", getFixedText, setFixedText)

    def setFixedTextPos(self, qpoint):
        """ Sets text position in the button.
        @param qpoint: Position RELATIVE. 0,0 is top left corner of the button.
        @type qpoint: QPoint
        """
        self.__fixed_label.move(qpoint)

    def getFixedTextPos(self):
        return self.__fixed_label.pos()

    fixedTextPos = QtCore.pyqtProperty("QPoint", getFixedTextPos,
                                       setFixedTextPos)

    def setFixedTextFont(self, font):
        """ Sets text font.
        @param font: Font for fixed text.
        @type font: QFont
        """
        self.__font = font
        self.__fixed_label.setFont(self.__font)

    def getFixedTextFont(self):
        return self.__font

    fixedTextFont = QtCore.pyqtProperty("QFont", getFixedTextFont,
                                        setFixedTextFont)

    #FUNZIONI INDICATORE
    def setIndicatorVisibility(self, bool):
        """ Sets if indicator is visible or not.
        @param bool: visible or not
        @type bool: bool
        """
        self.__indicator.setVisible(bool)

    def getIndicatorVisibility(self):
        return self.__indicator.isVisible()

    indicatorVisibility = QtCore.pyqtProperty("bool",
                                              fget=getIndicatorVisibility,
                                              fset=setIndicatorVisibility)

    def setIndicatorPos(self, qpoint):
        """ Sets indicator position in the button.
        @param qpoint: Position RELATIVE. 0,0 is top left corner of the button.
        @type qpoint: QPoint
        """
        self.__indicator.move(qpoint)

    def getIndicatorPos(self):
        return self.__indicator.pos()

    indicatorPos = QtCore.pyqtProperty("QPoint", getIndicatorPos,
                                       setIndicatorPos)

    def setIndicatorSize(self, size):
        """ Sets indicator size.
        @param size: Size
        @type size: QSize
        """
        self.__indicator.resize(size)

    def getIndicatorSize(self):
        return self.__indicator.size()

    indicatorSize = QtCore.pyqtProperty("QSize", getIndicatorSize,
                                        setIndicatorSize)

    def setIndicatorFont(self, font):
        """ Sets indicator text font.
        @param font: Font for indicator text.
        @type font: QFont
        """
        self.__indicator.setFont(font)

    def getIndicatorFont(self):
        return self.__indicator.font()

    indicatorFont = QtCore.pyqtProperty("QFont", getIndicatorFont,
                                        setIndicatorFont)

    ## FUNZIONI PER BLINK
    def __on_blink_timer(self):
        self.setVisible(not (self.isVisible()))

    def setBlinking(self, blink):
        """ Sets if the button have to blink or not.
        @param blink: blinking or not
        @type blink: bool
        """
        if blink:
            self.__blink_timer.setInterval(self.__blink_timer_interval)
            self.__blink_timer.start()
        else:
            self.__blink_timer.stop()
            self.setVisible(True)

    def setBlinkInterval(self, value):
        """ Sets blink interval.
        @param blink: blink interval (msec)
        @type blink: int
        """
        self.__blink_timer_interval = value

    def getBlinkInterval(self):
        return self.__blink_timer_interval

    blinkInterval = QtCore.pyqtProperty("int", getBlinkInterval,
                                        setBlinkInterval)

    ##FUNZIONI PER FADING
    def fadeIn(self):
        """
         Button fades in from completely invisible to completely visible.
        """
        self.__opacity = 0.0
        self.__selected_fade_type = self.__FADE_TYPE.IN
        self.__fading_timer.start(self.__fade_time)

    def fadeOut(self):
        """
         Button fades out from completely visible to completely invisible.
        """
        self.__selected_fade_type = self.__FADE_TYPE.OUT
        self.__fading_timer.start(self.__fade_time)

    def setFadeTime(self, value):
        """ Sets fading time. Everytime interval is reached, alpha is increased (or decreased) by __opacity_fading_coefficient.
        @param value: fade time (msec)
        @type value: int
        """
        self.__fade_time = value

    def getFadeTime(self):
        return self.__fade_time

    fadeInterval = QtCore.pyqtProperty("int", getFadeTime, setFadeTime)

    def setFadeCoefficient(self, value):
        """ Sets fading coefficient. Alpha is increased (or decreased) by this value.
        @param value: coefficient (min 0.0 - max 1.0)
        @type value: float
        """
        self.__opacity_fading_coefficient = value

    def getFadeCoefficient(self):
        return self.__opacity_fading_coefficient

    fadeCoefficient = QtCore.pyqtProperty("double", getFadeCoefficient,
                                          setFadeCoefficient)

    def __on_fading_timer(self):
        if self.__selected_fade_type == self.__FADE_TYPE.OUT:
            if self.__opacity > 0:
                self.__opacity -= self.__opacity_fading_coefficient
                self.__opacity_effect.setOpacity(self.__opacity)
                self.setGraphicsEffect(self.__opacity_effect)
            else:
                self.__fading_timer.stop()

        if self.__selected_fade_type == self.__FADE_TYPE.IN:
            if self.__opacity <= 1.0:
                self.__opacity += self.__opacity_fading_coefficient
                self.__opacity_effect.setOpacity(self.__opacity)
                self.setGraphicsEffect(self.__opacity_effect)
            else:
                self.__fading_timer.stop()

    # FUNZIONI PER GROW\SHRINK
    def __on_growed(self):
        self.__animationShrink.setStartValue(
            QSize(self.iconSize().width(),
                  self.iconSize().height()))
        self.__animationShrink.setEndValue(
            QSize(self.iconSize().width() - self.__iconGrowsBy,
                  self.iconSize().height() - self.__iconGrowsBy))
        self.__animationShrink.start()

    def __on_shrinked(self):
        self.__animationGrow.setStartValue(
            QSize(self.iconSize().width(),
                  self.iconSize().height()))
        self.__animationGrow.setEndValue(
            QSize(self.iconSize().width() + self.__iconGrowsBy,
                  self.iconSize().height() + self.__iconGrowsBy))
        self.__animationGrow.start()

    def startGrow(self):
        """
         Button ICON starts to grow and shrink to standard value when maximum size (configured) is reached
        """
        if self.__growing:
            return
        self.__animationGrow.setStartValue(
            QSize(self.iconSize().width(),
                  self.iconSize().height()))
        self.__animationGrow.setEndValue(
            QSize(self.iconSize().width() + self.__iconGrowsBy,
                  self.iconSize().height() + self.__iconGrowsBy))
        self.__animationGrow.start()
        self.__growing = True

    def stopGrow(self):
        if self.__animationGrow.startValue().toSize() != QSize(
                0,
                0) and self.__animationShrink.startValue().toSize() != QPoint(
                    0, 0):
            self.__animationGrow.stop()
            self.__animationShrink.stop()
            self.setIconSize(
                QSize(self.__defaultIconDimension,
                      self.__defaultIconDimension))
            self.__growing = False

    #FUNZIONI PER BOUNCE
    def startBounce(self):
        """
         Button starts to bounce requiring attention.
        """
        if self.__bouncing:
            return
        self.__startPos = QPoint(self.pos().x(), self.pos().y())
        self.__animationUp.setStartValue(
            QPoint(self.__startPos.x(), self.__startPos.y()))
        self.__animationUp.setEndValue(
            QPoint(self.__startPos.x(),
                   self.__startPos.y() - self.geometry().height()))
        self.__animationUp.start()
        self.__bouncing = True

    def stopBounce(self):
        if self.__animationUp.startValue().toPoint() != QPoint(
                0,
                0) and self.__animationBounce.startValue().toPoint() != QPoint(
                    0, 0):
            self.__animationBounce.stop()
            self.__animationUp.stop()
            self.setGeometry(self.__startPos.x(), self.__startPos.y(),
                             self.geometry().width(),
                             self.geometry().height())
            self.__bouncing = False

    def __on_top_reached(self):
        self.__animationBounce.setStartValue(
            QPoint(self.pos().x(),
                   self.pos().y()))
        self.__animationBounce.setEndValue(
            QPoint(self.__startPos.x(), self.__startPos.y()))
        self.__animationBounce.start()

    def __on_bounce_finished(self):
        self.__animationUp.start()

    def stopAllAnimations(self):
        self.stopBounce()
        self.stopGrow()

    #FUNZIONI PER PIXMAP & MASCHERA
    def setPixmapFile(self, image_file):
        self.__pmap = image_file
        self.__pmap.scaled(self.size())
        #NB: Il pixmap deve essere BIANCO e NERO. Con heuristicMask il primo pixel in alto a sinistra (0,0) viene
        #usato per decidere il colore trasparente, tutto il resto è visibile.

    def getPixmapFile(self):
        return self.__pmap

    pixmapFile = QtCore.pyqtProperty("QPixmap", getPixmapFile, setPixmapFile)

    def applyMask(self, bool):
        self.__show_mask_preview = bool
        if bool:
            self.setMask(
                QBitmap(self.__pmap.createHeuristicMask().scaled(self.size())))
        else:
            self.setMask(QBitmap())

    def getMask(self):
        return self.__show_mask_preview

    appliedMask = QtCore.pyqtProperty("bool", fget=getMask, fset=applyMask)

    def __onResize(self, event):
        self.__fixed_label.move(0, self.geometry().height() - 35)
        self.__fixed_label.resize(self.geometry().width(),
                                  self.__fixed_label.geometry().height())
        self.setIndicatorPos(QPoint(self.width() - self.__indicator.width(),
                                    0))
        self.__pmap.scaled(self.size())
        if self.__show_mask_preview:
            self.setMask(
                QBitmap(self.__pmap.createHeuristicMask().scaled(self.size())))

    def __on_indicator_Resize(self, event):
        self.setIndicatorPos(QPoint(self.width() - self.__indicator.width(),
                                    0))
Exemplo n.º 3
0
class MuWizard(QWizard):

    def __init__(self, R=None, parent=None):
        QWizard.__init__(self, parent)

        self.R = R
        self.data = None

        self.addPage(self.introPage())
        self.addPage(self.loadPage())
        self.addPage(self.modelsPage())
        self.addPage(self.resultsPage())
        self.setWindowTitle('Wizard of muScale')
        self.setPixmap(QWizard.LogoPixmap, QPixmap(RES + ICONS + LOGO))

        self.setStyleSheet('QWizard {' + GRADIENT +'}\
                        QPushButton {\
                            color: #333;\
                            border: 1px solid #555;\
                            border-radius: 11px;\
                            padding: 2px;\
                            background: qradialgradient(cx: 0.3, cy: -0.4,\
                            fx: 0.3, fy: -0.4,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #888);\
                            min-width: 80px;}\
                        QPushButton:hover {\
                            color: #fff;\
                            background: qradialgradient(cx: 0.3, cy: -0.4,\
                            fx: 0.3, fy: -0.4,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #bbb);}\
                        QPushButton:pressed {\
                            color: #800;\
                            background: qradialgradient(cx: 0.4, cy: -0.1,\
                            fx: 0.4, fy: -0.1,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}\
                        QPushButton:checked {\
                            background: qradialgradient(cx: 0.4, cy: -0.1,\
                            fx: 0.4, fy: -0.1,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}\
                        QComboBox {\
                            color: #333;\
                            border: 1px solid #555;\
                            border-radius: 11px;\
                            padding: 1px 18px 1px 3px;\
                            background: qradialgradient(cx: 0.3, cy: -0.4,\
                            fx: 0.3, fy: -0.4,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #888);\
                            min-width: 20px;}\
                        QComboBox:hover {\
                            color: #fff;\
                            background: qradialgradient(cx: 0.3, cy: -0.4,\
                            fx: 0.3, fy: -0.4,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #bbb);}\
                        QComboBox::down-arrow {\
                             image: url(' + RES + ICONS + ARROW_DOWN + ');}\
                        QComboBox::down-arrow:on {\
                             top: 1px;\
                             left: 1px;}\
                        QComboBox::drop-down {\
                             subcontrol-origin: padding;\
                             subcontrol-position: top right;\
                             width: 15px;\
                             border-left-width: 1px;\
                             border-left-color: darkgray;\
                             border-left-style: solid;\
                             border-top-right-radius: 3px;\
                             border-bottom-right-radius: 3px;}\
                         QToolButton {\
                            color: #333;\
                            border: 1px solid #555;\
                            border-radius: 11px;\
                            padding: 2px;\
                            background: qradialgradient(cx: 0.3, cy: -0.4,\
                            fx: 0.3, fy: -0.4,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #888);\
                            min-width: 20px;}\
                        QToolButton:hover {\
                            color: #fff;\
                            background: qradialgradient(cx: 0.3, cy: -0.4,\
                            fx: 0.3, fy: -0.4,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #bbb);}\
                        QToolButton:pressed {\
                            background: qradialgradient(cx: 0.4, cy: -0.1,\
                            fx: 0.4, fy: -0.1,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}\
                        QToolButton:checked {\
                            background: qradialgradient(cx: 0.4, cy: -0.1,\
                            fx: 0.4, fy: -0.1,\
                            radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}')

    def introPage(self):
        intro = QWizardPage()

        intro.setTitle('Hello and welcome')
        label = QLabel('''This is a wizard.
Now you're ready to forecast some time series!
        ''')

        label.setWordWrap(True)
        layout = QVBoxLayout()
        layout.addWidget(label)
        intro.setLayout(layout)

        return intro

    def loadPage(self):
        load = WizardPageEx('loadCheck')

        load.setTitle('Initial data')
        pathLbl = QLabel("<font style='color: gray'>Specify the file with time series to forecast:</font>")
        loadLbl = QLabel("<font style='color: gray'>Click</font>")
        loadLbl.setAlignment(Qt.AlignCenter)

        self.path = QLineEdit()
        self.path.setPlaceholderText('path to file')
        getPath = QToolButton()
        getPath.setText('...')

        self.resultLbl = QLabel()
        self.resultLbl.setAlignment(Qt.AlignCenter)
        self.resultLbl.hide()

        self.preview = QLabel()
        self.preview.setAlignment(Qt.AlignCenter)
        self.preview.setWordWrap(True)
        self.preview.hide()

        self.preview.setAttribute(Qt.WA_Hover)
        self.filter = Filter()
        self.preview.installEventFilter(self.filter)

        getPath.clicked.connect(self.loadData)

        layout = QGridLayout()
        layout.addWidget(pathLbl, 0, 0)
        layout.addWidget(loadLbl, 0, 1)
        layout.addWidget(self.path, 1, 0)
        layout.addWidget(getPath, 1, 1)
        layout.addWidget(self.resultLbl, 2, 0, 1, 2)
        layout.addWidget(self.preview, 3, 0, 1, 2)
        load.setLayout(layout)

        load.previewSeries = SeriesPreview()
        self.previewSeries = load.previewSeries

        # to be able to reference from class namespace
        self.loadCheck = load.check

        return load

    def modelsPage(self):
        models = WizardPageEx('modelCheck')
        models.setTitle('Forecast')

        lbl = QLabel("<font style='color: gray'>Forecast horizon:</font>")
        self.steps = QSpinBox()
        self.steps.setRange(MIN_FORECAST, MAX_FORECAST)
        self.steps.setValue(10)
        self.start = QPushButton('Forecast')
        self.start.clicked.connect(self.modelling)
        self.custom = QPushButton('Advanced')
        self.processing = QLabel()

        self.gifLoading = QMovie(RES + ICONS + PROGRESS, QByteArray(), self)
        self.gifLoading.setCacheMode(QMovie.CacheAll)
        self.gifLoading.setSpeed(100)
        self.processing.setMovie(self.gifLoading)
        self.processing.setAlignment(Qt.AlignCenter)
        self.processing.hide()

        self.status = QLabel()
        self.status.setAlignment(Qt.AlignCenter)
        self.status.hide()

        layout = QGridLayout()
        layout.addWidget(lbl, 0, 0)
        layout.addWidget(self.steps, 0, 1)
        layout.addWidget(self.start, 0, 2)
        layout.addWidget(self.custom, 0, 3)
        layout.addWidget(self.status, 1, 0, 1, 4)
        layout.addWidget(self.processing, 2, 0, 1, 4)
        models.setLayout(layout)

        self.customOpt = CustomOption()
        self.custom.clicked.connect(self.customOpt.show)
        self.modelCheck = models.check

        return models

    def resultsPage(self):
        results = QWizardPage()
        results.setFinalPage(True)
        results.setTitle('Results')

        self.graph = QLabel("<font style='font-size: 16px;'>Plot</font>")
        self.export = QLabel("<font style='font-size: 16px;'>Export</font>")
        self.showData = QLabel("<font style='font-size: 16px;'>Data</font>")
        
        self.plotResult = MplWidget(None)
        self.plotResult.canvas.fig.set_facecolor('white')
        self.resData = QLabel('')
        self.resData.setAlignment(Qt.AlignCenter)
        self.resData.setWordWrap(True)
        self.resData.hide()

        self.resFilter = ResFilter()

        self.resLayout = QVBoxLayout()
        self.resLayout.addWidget(self.export)
        self.resLayout.addWidget(self.graph)
        self.resLayout.addWidget(self.showData)
        self.resLayout.addWidget(self.plotResult)
        self.resLayout.addWidget(self.resData)

        self.plotResult.hide()

        for index in range(0, self.resLayout.count()):
            try:
                self.resLayout.itemAt(index).widget().setAlignment(Qt.AlignCenter)
                self.resLayout.itemAt(index).widget().setStyleSheet('QLabel { color: gray; }')
                self.resLayout.itemAt(index).widget().setAttribute(Qt.WA_Hover)
                self.resLayout.itemAt(index).widget().installEventFilter(self.resFilter)
            except Exception:
                pass

        self.resLayout.setAlignment(Qt.AlignCenter)
        self.resLayout.setSpacing(60)

        results.setLayout(self.resLayout)

        return results

    #---- actions ----#
    def loadData(self):
        fileName = unicode(QFileDialog.getOpenFileName(self, 'Open text file', RES))
        if fileName:
            self.resultLbl.hide()
            self.preview.hide()
            if DataParser.istextfile(fileName):
                self.data = DataParser.getTimeSeriesFromTextData(data=open(fileName, 'r').read())
                if len(self.data[0]) > DATA_LOW_LIMIT:
                    self.resultLbl.setText("<font style='color: gray'>Success! Loaded<b> " + str(len(self.data[0])) +
                                           '</b> values, errors: <b>' + str(
                                            self.data[1]) + '</b></font>')
                    self.resultLbl.show()
                    self.path.setText(fileName)

                    previewLength = 40
                    self.preview.setText("<font style='color: gray'><b>Preview:</b> "+ ' '.join(
                                        [str(e) for e in self.data[0][:previewLength]]) +
                                        "...</font>")
                    self.previewSeries.updateData(self.data)
                    self.preview.show()
                    self.loadCheck.setChecked(True)
                else:
                    self.resultLbl.setText("<font style='color: gray'>Not enough values to form data series.</font>")
                    self.resultLbl.show()
            else:
                self.resultLbl.setText("<font style='color: gray'>Specified file is binary file!</font>")
                self.resultLbl.show()

    def modelling(self):
        self.processing.show()
        self.gifLoading.start()
        self.status.setText('')

        statusText = u"<font style='color: gray'>"

        # decomposition #
        if self.customOpt.options['enable']:
            self.signalEx = self.customOpt.options['signal']
            self.wavelet = pywt.Wavelet(self.customOpt.options['wavelet'])
            wLevel = self.customOpt.options['lvls'] - 1
            maxLevel = pywt.dwt_max_level(len(self.data[0]), self.wavelet)

            if wLevel > maxLevel:
                wLevel = maxLevel

            self.wInitialCoefficients = pywt.wavedec(self.data[0], self.wavelet,
                                                       level=wLevel, mode=self.signalEx)
            self.wCoefficients = self.wInitialCoefficients
        else:
            self.signalEx = pywt.MODES.sp1
            selected = select_wavelet(self.data[0], self.R)
            index = max(selected)
            self.wavelet = pywt.Wavelet(selected[index][1])

            wLevel = calculate_suitable_lvl(self.data[0],
                                         self.wavelet, self.R, swt=False)
            self.wInitialCoefficients = pywt.wavedec(self.data[0], self.wavelet,
                                                     level=wLevel, mode=self.signalEx)
            self.wCoefficients = self.wInitialCoefficients

        statusText += 'Wavelet: <b>' + self.wavelet.family_name + \
                      '</b> (' + self.wavelet.name + ', ' + self.wavelet.symmetry + ', orthogonal: ' + \
                      str(self.wavelet.orthogonal) + ')<br/>'

        statusText += 'Discrete Wavelet Transfom: <b>' + str(wLevel + 1) + ' levels</b><br/>'

        # models #
        options = {}
        options['multi'] = True
        options['fractal'] = False
        options['ljung'] = False
        self.models = auto_model(self.wCoefficients, self.R, options, self.data[0])

        statusText += '<br/>Selected models:<br/>'
        for lvl, model in self.models.iteritems():
            statusText += str(lvl) + '. <b>' + model.enumname.replace('_', ' ') + '</b><br/>'

        # forecasting #
        try:
            frequency = self.customOpt.options['frequency']
        except Exception:
            frequency = 8
        aic = False
        options['hw_gamma'] = True
        options['hw_model'] = 'additive'
        options['hw_period'] = frequency
        options['ar_aic'] = aic
        options['ar_method'] = 'burg'
        options['ar_order'] = 50
        options['arima_auto'] = False
        options['arima_nons'] = True
        options['arima_nons_order'] = [6, 0, 9]
        options['arima_seas'] = True
        options['arima_seas_order'] = [9, 0, 1]
        options['lsf_aic'] = aic
        options['lsf_order'] = 30
        options['ets_auto'] = aic
        options['ets_period'] = frequency
        options['sts_type'] = 'trend'
        options['append_fit'] = True

        self.multi_model_thread = MultiModelThread(self.models,
                                           self.wCoefficients,
                                           self.R,
                                           self.steps.value(),
                                           options,)
        self.multi_model_thread.done.connect(self.multiForecastFinished)
        self.multi_model_thread.start()

        statusText += '<br/>Forecasting...'

        self.status.setText(statusText)
        self.status.show()

    def multiForecastFinished(self, results):
        self.forecast = results
        self.gifLoading.stop()
        self.processing.hide()

        self.status.setText('<br/>'.join(str(self.status.text()).split('<br/>')[:-1]) +
                            '<br/>Forecast complete.')
        self.modelCheck.setChecked(True)

    #---- results -----#
    def togglePlot(self):
        if self.plotResult.isVisible():
            self.plotResult.hide()
            self.export.show()
            self.showData.show()
            self.resLayout.setSpacing(60)
        else:
            self.updateGraph()
            self.plotResult.show()
            self.export.hide()
            self.showData.hide()
            self.resLayout.setSpacing(5)

    def toggleData(self):
        if self.resData.isVisible():
            self.export.show()
            self.graph.show()
            self.showData.show()
            self.resData.hide()
            self.resLayout.setSpacing(60)
            self.adjustSize()
        else:
            self.plotResult.hide()
            self.graph.hide()
            self.export.hide()
            self.showData.show()
            self.resData.show()
            self.resLayout.setSpacing(5)

            res = self.inverseWT()
            max = len(self.data[0]) - 1
            resText = '<table border="0" align="center" style="border-style: groove;">'
            resText += '<tr><td align="center"><i>Initial</i></td><td align="center"><i>Forecast</i></td></tr>'
            resText += '<tr><td align="center">...</td><td></td></tr>'
            table = zip(self.data[0][max-10:max], res[max-10:max])
            for i, j in table:
                resText += '<tr>'
                resText += '<td align="center">' + str(i) + '</td>'
                resText += '<td align="center">' + str(j) + '</td>'
                resText += '</tr>'

            for e in res[max+1:max+10]:
                resText += '<tr>'
                resText += '<td align="center"></td>'
                resText += '<td align="center">' + str(e) + '</td>'
                resText += '</tr>'

            resText += '<tr><td align="center"></td><td align="center">...</td></tr>'
            resText += '</table>'

            self.resData.setText(resText)
            self.adjustSize()

    def inverseWT(self):
        return pywt.waverec(update_dwt([e [1] for e in self.forecast], self.wavelet, self.signalEx),
                                            self.wavelet, mode=self.signalEx)

    def updateGraph(self):
        self.plotResult.updatePlot(self.inverseWT(), label='Simulation', color='r')
        self.plotResult.updatePlot(self.data[0], label='Time series', color='b')

    def exportToXls(self):
        # opening file dialog
        fileName = QFileDialog.getSaveFileName(self,
            'Save as', RES, 'Microsoft Excel Spreadsheet (*.xls)')

        if fileName.count() > 0:
            try:
                COLUMN_WIDTH = 3000

                alignment = Alignment()
                alignment.horizontal = Alignment.HORZ_CENTER
                alignment.vertical = Alignment.VERT_CENTER

                borders = Borders()
                borders.left = Borders.THIN
                borders.right = Borders.THIN
                borders.top = Borders.THIN
                borders.bottom = Borders.THIN

                style = XFStyle()
                style.alignment = alignment
                style.borders = borders

                font = Font()
                font.bold = True
                headerStyle = XFStyle()
                headerStyle.font = font

                separate = Borders()
                separate.left = Borders.THIN
                separate.right = Borders.DOUBLE
                separate.top = Borders.THIN
                separate.bottom = Borders.THIN
                separateStyle = XFStyle()
                separateStyle.borders = separate

                book = Workbook(encoding='utf-8')

                # modelling data
                dec_sheet = book.add_sheet('Data decomposition')

                # decomposition data
                # initial data
                column = 0
                row = 0
                dec_sheet.write(row, column, 'Time series', headerStyle)
                dec_sheet.col(column).width = COLUMN_WIDTH
                row += 1
                for item in self.data[0]:
                    dec_sheet.write(row, column, item, separateStyle)
                    row += 1

                # decomposition
                for lvl in self.wCoefficients:
                    row = 0
                    column += 1
                    dec_sheet.write(row, column, 'Level' + str(column - 1), headerStyle)
                    dec_sheet.col(column).width = COLUMN_WIDTH
                    row += 1
                    for item in lvl:
                        dec_sheet.write(row, column, item, style)
                        row += 1

                # decomposition graphs
                pass

                levels_sheet = book.add_sheet('Multiscale forecast')

                # levels data
                column = 0
                for lvl in self.forecast:
                    row = 0
                    levels_sheet.write(row, column, 'Level' + str(column), headerStyle)
                    levels_sheet.col(column).width = COLUMN_WIDTH
                    row += 1
                    for item in lvl[1]:
                        levels_sheet.write(row, column, float(item), style)
                        row += 1
                    column += 1

                result_sheet = book.add_sheet('Results')

                # initial
                column = 0
                row = 0
                result_sheet.write(row, column, 'Initial data', headerStyle)
                result_sheet.col(column).width = COLUMN_WIDTH
                row += 1
                for item in self.data[0]:
                    result_sheet.write(row, column, item, separateStyle)
                    row += 1

                # forecast
                row = 0
                column += 1
                result_sheet.write(row, column, 'Forecast', headerStyle)
                result_sheet.col(column).width = COLUMN_WIDTH
                row += 1
                for item in self.inverseWT():
                    result_sheet.write(row, column, item, style)
                    row += 1

                row = 0
                column = 2
                self.updateGraph()
                self.plotResult.saveFigure('forecast', format='bmp')

                result_sheet.insert_bitmap(RES + TEMP + 'forecast.bmp', row, column)

                # saving xls
                try:
                    book.save(unicode(fileName))
                except Exception:
                    pass

            except Exception, e:
                pass