222 lines
7.4 KiB
Python
222 lines
7.4 KiB
Python
"""
|
|
This module contains all kinds of animated buttons
|
|
"""
|
|
|
|
import math
|
|
import random
|
|
|
|
from PyQt5.QtCore import (QAbstractAnimation, Qt, QRectF, QLineF,
|
|
QPropertyAnimation, pyqtProperty, pyqtSignal)
|
|
from PyQt5.QtGui import QPen
|
|
from PyQt5.QtWidgets import (QGraphicsObject)
|
|
|
|
from general import extras
|
|
|
|
RANDOMCHAR = "~!@#$%^&*()_+`-=[]\{}|;:'<>,./?\""
|
|
|
|
|
|
class AnimBox(QGraphicsObject):
|
|
# Prepare the signal
|
|
hoverEnter = pyqtSignal()
|
|
hoverExit = pyqtSignal()
|
|
buttonClicked = pyqtSignal(str)
|
|
|
|
# Initialisation
|
|
def __init__(self, x, y, width, height, text, parent=None):
|
|
super().__init__(parent=parent)
|
|
self.x = x
|
|
self.y = y
|
|
self.width = width
|
|
self.height = height
|
|
self.text = text
|
|
self.circumference = 2*(width+height)
|
|
|
|
# Set up pens for drawing
|
|
self.default_pen = QPen()
|
|
self.default_pen.setColor(Qt.white)
|
|
self.outline_pen = QPen()
|
|
self.outline_pen.setColor(Qt.white)
|
|
self.outline_pen.setWidth(5)
|
|
|
|
# Whether the mouse hover over the box
|
|
self.detected = False
|
|
self.btn_rect = QRectF(self.x, self.y, self.width, self.height)
|
|
# The 4 lines to construct the box
|
|
self.left = QLineF()
|
|
self.down = QLineF()
|
|
self.right = QLineF()
|
|
self.up = QLineF()
|
|
|
|
self.line_order = [self.up, self.right, self.down, self.left]
|
|
|
|
self.set_freeze(False)
|
|
|
|
# Length of the box to be drawn
|
|
self.length = 0
|
|
# Set up the length to be animated
|
|
self.anim = QPropertyAnimation(self, b'length')
|
|
self.anim.setDuration(400) # Animation speed
|
|
self.anim.setStartValue(0)
|
|
for t in range(1, 10):
|
|
self.anim.setKeyValueAt(t / 10, self.logistic_func(t / 10))
|
|
self.anim.setEndValue(self.circumference)
|
|
self.animText = AnimatedText(self.text, parent=self)
|
|
|
|
def set_freeze(self, freeze):
|
|
if freeze:
|
|
self.setAcceptedMouseButtons(Qt.NoButton)
|
|
self.setAcceptHoverEvents(False)
|
|
else:
|
|
self.setAcceptedMouseButtons(Qt.LeftButton)
|
|
self.setAcceptHoverEvents(True)
|
|
|
|
# Toggle the animation to be play forward or backward
|
|
def toggle_anim(self, toggling):
|
|
if toggling:
|
|
self.anim.setDirection(QAbstractAnimation.Forward)
|
|
else:
|
|
self.anim.setDirection(QAbstractAnimation.Backward)
|
|
|
|
self.anim.start()
|
|
|
|
# The logistic function that determines the animation motion
|
|
def logistic_func(self, x):
|
|
return self.circumference / (1+math.exp(-(x-0.5)*18))
|
|
|
|
# Reimplemented boundingRect
|
|
def boundingRect(self):
|
|
return QRectF(self.x-5, self.y-5, self.width+10, self.height+10)
|
|
|
|
# Reimplemented paint
|
|
def paint(self, painter, style, widget=None):
|
|
painter.setPen(self.outline_pen)
|
|
for line in self.line_order:
|
|
if line.length() > 1:
|
|
painter.drawLine(line)
|
|
painter.setPen(self.default_pen)
|
|
painter.fillRect(self.btn_rect, Qt.black)
|
|
painter.drawRect(self.btn_rect)
|
|
|
|
# Defining the length to be drawn as a pyqtProperty
|
|
@pyqtProperty(float)
|
|
def length(self):
|
|
return self._length
|
|
|
|
# Determine the length of the four lines to be drawn
|
|
@length.setter
|
|
def length(self, value):
|
|
self._length = value
|
|
remaining_length = value
|
|
if remaining_length >= 2 * self.width + self.height:
|
|
length_to_draw = remaining_length - (2 * self.width + self.height)
|
|
remaining_length -= length_to_draw
|
|
else:
|
|
length_to_draw = 0
|
|
|
|
self.line_order[3].setLine(self.x, self.y + self.height,
|
|
self.x, self.y + self.height - length_to_draw)
|
|
if remaining_length >= self.width + self.height:
|
|
length_to_draw = remaining_length - (self.width + self.height)
|
|
remaining_length -= length_to_draw
|
|
else:
|
|
length_to_draw = 0
|
|
self.line_order[2].setLine(self.x + self.width, self.y + self.height,
|
|
self.x + self.width - length_to_draw,
|
|
self.y + self.height)
|
|
|
|
if remaining_length >= self.width:
|
|
length_to_draw = remaining_length - self.width
|
|
remaining_length -= length_to_draw
|
|
else:
|
|
length_to_draw = 0
|
|
|
|
self.line_order[1].setLine(self.x + self.width, self.y,
|
|
self.x + self.width, self.y + length_to_draw)
|
|
self.line_order[0].setLine(self.x, self.y,
|
|
self.x + remaining_length, self.y)
|
|
self.update()
|
|
|
|
# Reimplemented hoverEvents to detect the mouse and toggle the animation
|
|
def hoverEnterEvent(self, event):
|
|
if ~self.detected:
|
|
self.hoverEnter.emit()
|
|
self.detected = True
|
|
self.toggle_anim(True)
|
|
super().hoverEnterEvent(event)
|
|
|
|
def hoverLeaveEvent(self, event):
|
|
if self.detected:
|
|
self.hoverExit.emit()
|
|
self.detected = False
|
|
self.toggle_anim(False)
|
|
super().hoverLeaveEvent(event)
|
|
|
|
def mousePressEvent(self, event):
|
|
self.length = 0
|
|
self.buttonClicked.emit(self.text)
|
|
|
|
|
|
class AnimatedText(QGraphicsObject):
|
|
|
|
def __init__(self, text, parent=None):
|
|
super().__init__(parent=parent)
|
|
self.parent = parent
|
|
self.actual_text = text
|
|
self.shown_text = ''
|
|
self.delay = 3
|
|
|
|
# Set up pens for drawing
|
|
self.default_pen = QPen()
|
|
self.default_pen.setColor(Qt.white)
|
|
|
|
self.randomchar_list = [c for c in RANDOMCHAR]
|
|
self.shown_length = 0
|
|
|
|
# Set up the length to be animated
|
|
self.anim = QPropertyAnimation(self, b'shown_length')
|
|
self.anim.setDuration(len(self.actual_text) * 50) # Animation speed
|
|
self.anim.setStartValue(0)
|
|
for t in range(1, 10):
|
|
self.anim.setKeyValueAt(t / 10, (len(self.actual_text) + self.delay) * t/10)
|
|
self.anim.setEndValue(len(self.actual_text) + self.delay)
|
|
self.visibleChanged.connect(self.show_text)
|
|
|
|
def show_text(self):
|
|
if self.isVisible():
|
|
self.toggle_anim(True)
|
|
else:
|
|
self.shown_length = 0
|
|
|
|
# Toggle the animation to be play forward or backward
|
|
def toggle_anim(self, toggling):
|
|
if toggling:
|
|
self.anim.setDirection(QAbstractAnimation.Forward)
|
|
else:
|
|
self.anim.setDirection(QAbstractAnimation.Backward)
|
|
|
|
self.anim.start()
|
|
|
|
def boundingRect(self):
|
|
return self.parent.boundingRect()
|
|
|
|
def paint(self, painter, style, widget=None):
|
|
painter.setPen(self.default_pen)
|
|
painter.drawText(self.parent.boundingRect(), Qt.AlignCenter, self.shown_text)
|
|
|
|
# Defining the length to be drawn as a pyqtProperty
|
|
@pyqtProperty(int)
|
|
def shown_length(self):
|
|
return self._shown_length
|
|
|
|
# Determine the length of the four lines to be drawn
|
|
@shown_length.setter
|
|
def shown_length(self, value):
|
|
self._shown_length = value
|
|
text_length = extras.bound_value(0, value-self.delay, len(self.actual_text))
|
|
text = self.actual_text[:text_length]
|
|
random.shuffle(self.randomchar_list)
|
|
text += ''.join(self.randomchar_list[:min(value, 3)])
|
|
|
|
self.shown_text = text[:min(self.shown_length, len(self.actual_text))]
|
|
self.update()
|