PDA

View Full Version : [PyQt5] Making a text appear char by char in QTextEdit



lvlanson
30th March 2020, 17:14
I want to make an interactive tutorial for special textdocuments. I would like to be able to insert text by the program and also have the user to add something.

I would like to have the text appear char by char. I tried this, but it just blocks the window from appearing completely, until the text is written out

import sys
import time
from PyQt5 import QtWidgets as qtw


class Text_Script(qtw.QTextEdit):
def __init__(self):
super().__init__()

def write_intro(self):
welcome_string = "Welcome!"
for i in range(0, len(welcome_string)):
time.sleep(0.2)
self.insertPlainText(welcome_string[i])

calling in the main window


import sys
from Text_Script import Text_Script
from PyQt5 import QtWidgets as qtw


class Lastenheft(qtw.QWidget):
def __init__(self):
super().__init__()
self.init_me()

def init_me(self):
self.setLayout(qtw.QVBoxLayout())
self.text_script = Text_Script()
self.next_btn = qtw.QPushButton("Next")
self.layout().addWidget(self.text_script)
self.layout().addWidget(self.next_btn)
self.showMaximized()
self.text_script.write_intro()

if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
mw = Lastenheft()
sys.exit(app.exec())

Is there a way to achieve this?

d_stranz
30th March 2020, 20:05
Your call to sleep() is basically locking the UI until it returns, and your for() loop has no place for the UI event loop to run to update the text document display on screen. So nothing happens until the for() loop exits.

Instead of using sleep(), use a QTimer instead. Unlike sleep(), QTimer allows the event loop to run while it is counting down, so the UI gets updated.

What you will have to do is create a slot in your Text_Script class and connect the slot to the QTimer's timeout() signal. In that slot, you will put the next character in the string into the text document, then restart the timer if you haven't reached the end of the string yet.

You will have to restructure your code to make the string a member variable of the class (so you can access it from the slot) and add another member variable to keep an index to the current character in the string. Each time your slot code runs, you output the character at the index, then increment the index. If the index is less than the length of the string (minus one), you start the timer again, otherwise you do nothing.

I don't know much python, but I think your code would probably look something like this:



class Text_Script(qtw.QTextEdit):
def __init__(self):
super().__init__()
self.timer = QTimer()
self.timer.connect( timer.timeout, whatever the syntax is..., self.putNextCharacterSlot )
self.stringIndex = 0;
self.stringToOutput = QString()

def write_intro(self):
self.stringToOutput = "Welcome!"
self.stringIndex = 0;
timer.start( 0.2 )

def putNextCharacterSlot( self ) :
if ( self.stringIndex < self.stringToOutput.length() ) :
self.insertPlainText( self.stringToOutput[ self.stringIndex ] )
self.stringIndex++
if ( self.stringIndex < self.stringToOutput.length() - 1 ) :
timer.start( 0.2 )

lvlanson
31st March 2020, 10:26
Thanks man, works like a charm.

I had to make some modifications regarding Python3 syntax.

d_stranz
31st March 2020, 19:58
I had to make some modifications regarding Python3 syntax.

Not surprised. I can read python, but I have never written anything serious in it. Thirty years of C++ habits die hard.