From 23be7cf4499da3628d27e5fd76da0c4b12a06be2 Mon Sep 17 00:00:00 2001 From: En Yi Date: Sat, 21 Jul 2018 22:21:10 +0800 Subject: [PATCH] Move animated text classes into textbox --- graphic_components/buttons.py | 73 +----------- graphic_components/scoreboard.py | 123 +------------------ graphic_components/textbox.py | 196 +++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 192 deletions(-) create mode 100644 graphic_components/textbox.py diff --git a/graphic_components/buttons.py b/graphic_components/buttons.py index 95ae345..0f18e6b 100644 --- a/graphic_components/buttons.py +++ b/graphic_components/buttons.py @@ -1,18 +1,15 @@ -""" -This module contains all kinds of animated buttons +"""This module contains all the buttons used. A base class AnimBox handles the drawing and animation, inherited +by all the buttons. """ import math -import random from PyQt5.QtCore import (QAbstractAnimation, Qt, QRectF, QLineF, QPropertyAnimation, pyqtProperty, pyqtSignal) from PyQt5.QtGui import QPen, QColor from PyQt5.QtWidgets import (QGraphicsObject) -from general import extras - -RANDOMCHAR = "~!@#$%^&*()_+`-=[]\{}|;:'<>,./?\"" +from .textbox import AnimatedText class AnimBox(QGraphicsObject): @@ -27,7 +24,6 @@ class AnimBox(QGraphicsObject): self.y = y self.width = width self.height = height - #self.text = text self.circumference = 2*(width+height) # Set up pens for drawing @@ -54,7 +50,6 @@ class AnimBox(QGraphicsObject): 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)) @@ -203,66 +198,4 @@ class MenuButton(AnimBox): 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() diff --git a/graphic_components/scoreboard.py b/graphic_components/scoreboard.py index 7b052a6..97b3019 100644 --- a/graphic_components/scoreboard.py +++ b/graphic_components/scoreboard.py @@ -14,6 +14,7 @@ else: hs_file = "./sudoku/general/highscore.txt" from general import highscore as hs +from .textbox import AnimatedLabel if not os.path.exists(hs_file): print('Missing High Score file. Generating one. ') @@ -228,128 +229,6 @@ class NameInput(QWidget): self.nameReceived.emit(name) -class AnimatedLabel(QLabel): - """ - QLabel that a message, which is displayed through animation. - """ - - def __init__(self, text, speed=75, delay=20, parent=None): - """ - Does some text processing, and set up the animation to display the text - - Parameters - ---------- - text: str - Text to be displayed - speed : int - The period at which a new character is printed - The total time is calculated as length of text * speed - 0 means instant display, like a regular QLabel. - delay : int - The number of garbage to be printed before printing the actual text - parent: QWidget - Pass into QLabel init method - """ - super().__init__(text, parent) - - self.speed = speed - - self.setWordWrap(True) - self.setContentsMargins(0, 0, 0, 0) - # Text processing - # Make sure the garbage does not exceed the length of actual text - self.actual_text = text - self.shown_text = '' - if delay >= 0: - self.delay = min(delay, len(self.actual_text)) - else: - self.delay = len(self.actual_text) - - # Find out where the new paragraphs are so that it is retained - self.splitpoints = [] - current_point = 0 - line_splits = self.actual_text.split('\n') - for line in line_splits: - current_point += len(line) - self.splitpoints.append(current_point) - current_point += 1 - - # Set up the shown text length to be animated - self.shown_length = 0 - self.anim = QPropertyAnimation(self, b'shown_length') - self.anim.setDuration(len(self.actual_text) * speed) - self.anim.setStartValue(0) - self.anim.setEndValue(len(self.actual_text) + self.delay) - - #self.setStyleSheet(""" - # color: rgb(0, 255, 0); - # """) - self.toggle_anim(True) - - @pyqtProperty(int) - def shown_length(self): - """ - int : The value for the animation - - When the value is set, the text to be printed is generated accordingly. - It determines whether actual text is to be printed, and retains the - paragraphs when printing garbage. - """ - return self._shown_length - - @shown_length.setter - def shown_length(self, value): - self._shown_length = value - - if value < self.delay: - # All printed text should be garbage - garbage = [chr(num) for num in - [random.choice(range(33, 127)) for _ in range(value)]] - - # Retain the paragraphs - for num in self.splitpoints[:-1]: - if num < value: - garbage[num] = '\n' - - self.setText(''.join(garbage)) - else: - # Printed text contain some actual text - garbage = [chr(num) for num in - [random.choice(range(33, 127)) for _ in - range(min(len(self.actual_text) + self.delay - value, self.delay))]] - - # Retain the paragraphs, but offset by the number of actual text - non_garbage = value - self.delay - for num in self.splitpoints[:-1]: - if num - non_garbage > 0 and num < value: - garbage[num - non_garbage] = '\n' - - self.setText(self.actual_text[:value - self.delay] + ''.join(garbage)) - - def toggle_anim(self, toggling): - """ - Toggle the animation to be play forward or backward - - Parameters - ---------- - toggling: bool - True for forward, False for backward - """ - if toggling: - self.anim.setDirection(QAbstractAnimation.Forward) - else: - self.anim.setDirection(QAbstractAnimation.Backward) - - self.anim.start() - - def replace_text(self, new_text): - self.shown_length = 0 - self.actual_text = new_text - self.anim.setDuration(len(self.actual_text) * self.speed) - self.anim.setEndValue(len(self.actual_text) + self.delay) - self.toggle_anim(True) - - if __name__ == '__main__': app = 0 app = QApplication(sys.argv) diff --git a/graphic_components/textbox.py b/graphic_components/textbox.py new file mode 100644 index 0000000..05b8c0b --- /dev/null +++ b/graphic_components/textbox.py @@ -0,0 +1,196 @@ +import random + +from PyQt5.QtCore import (QAbstractAnimation, Qt, QPropertyAnimation, pyqtProperty) +from PyQt5.QtGui import QPen +from PyQt5.QtWidgets import QGraphicsObject, QLabel + + +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.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 + if value < self.delay: + # All printed text should be garbage + garbage = [chr(num) for num in + [random.choice(range(33, 127)) for _ in range(value)]] + self.shown_text = ''.join(garbage) + else: + # Printed text contain some actual text + garbage = [chr(num) for num in + [random.choice(range(33, 127)) for _ in + range(min(len(self.actual_text) + self.delay - value, self.delay))]] + self.shown_text = self.actual_text[:value - self.delay] + ''.join(garbage) + + self.update() + +class AnimatedLabel(QLabel): + """ + QLabel that a message, which is displayed through animation. + """ + + def __init__(self, text, speed=75, delay=20, parent=None): + """ + Does some text processing, and set up the animation to display the text + + Parameters + ---------- + text: str + Text to be displayed + speed : int + The period at which a new character is printed + The total time is calculated as length of text * speed + 0 means instant display, like a regular QLabel. + delay : int + The number of garbage to be printed before printing the actual text + parent: QWidget + Pass into QLabel init method + """ + super().__init__(text, parent) + + self.speed = speed + + self.setWordWrap(True) + self.setContentsMargins(0, 0, 0, 0) + # Text processing + # Make sure the garbage does not exceed the length of actual text + self.actual_text = text + self.shown_text = '' + if delay >= 0: + self.delay = min(delay, len(self.actual_text)) + else: + self.delay = len(self.actual_text) + + # Find out where the new paragraphs are so that it is retained + self.splitpoints = [] + current_point = 0 + line_splits = self.actual_text.split('\n') + for line in line_splits: + current_point += len(line) + self.splitpoints.append(current_point) + current_point += 1 + + # Set up the shown text length to be animated + self.shown_length = 0 + self.anim = QPropertyAnimation(self, b'shown_length') + self.anim.setDuration(len(self.actual_text) * speed) + self.anim.setStartValue(0) + self.anim.setEndValue(len(self.actual_text) + self.delay) + + #self.setStyleSheet(""" + # color: rgb(0, 255, 0); + # """) + self.toggle_anim(True) + + @pyqtProperty(int) + def shown_length(self): + """ + int : The value for the animation + + When the value is set, the text to be printed is generated accordingly. + It determines whether actual text is to be printed, and retains the + paragraphs when printing garbage. + """ + return self._shown_length + + @shown_length.setter + def shown_length(self, value): + self._shown_length = value + + if value < self.delay: + # All printed text should be garbage + garbage = [chr(num) for num in + [random.choice(range(33, 127)) for _ in range(value)]] + + # Retain the paragraphs + for num in self.splitpoints[:-1]: + if num < value: + garbage[num] = '\n' + + self.setText(''.join(garbage)) + else: + # Printed text contain some actual text + garbage = [chr(num) for num in + [random.choice(range(33, 127)) for _ in + range(min(len(self.actual_text) + self.delay - value, self.delay))]] + + # Retain the paragraphs, but offset by the number of actual text + non_garbage = value - self.delay + for num in self.splitpoints[:-1]: + if num - non_garbage > 0 and num < value: + garbage[num - non_garbage] = '\n' + + self.setText(self.actual_text[:value - self.delay] + ''.join(garbage)) + + def toggle_anim(self, toggling): + """ + Toggle the animation to be play forward or backward + + Parameters + ---------- + toggling: bool + True for forward, False for backward + """ + if toggling: + self.anim.setDirection(QAbstractAnimation.Forward) + else: + self.anim.setDirection(QAbstractAnimation.Backward) + + self.anim.start() + + def replace_text(self, new_text): + self.shown_length = 0 + self.actual_text = new_text + self.anim.setDuration(len(self.actual_text) * self.speed) + self.anim.setEndValue(len(self.actual_text) + self.delay) + self.toggle_anim(True)