<jupyter_start><jupyter_text>Using PyQtHere is a minimal example using PyQt5.<jupyter_code>%reset -f
from PyQt5 import QtWidgets, QtGui, QtCore


class App(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()
        self.setup_UI()
        self.show()
    
    def setup_UI(self):
        self.setWindowTitle("pytypist")

        frame = QtWidgets.QFrame(self)
        self.setCentralWidget(frame)
        
        text = "Some text here <br> And another line <br> And some more"
        text_widget = self.text_widget = QtWidgets.QTextEdit(text, frame)
        button = self.button = QtWidgets.QPushButton("Push me!", frame)

        vbox = QtWidgets.QVBoxLayout(frame)
        vbox.addWidget(text_widget)
        vbox.addWidget(button)
        
        button.clicked.connect(self.on_click)

    def on_click(self):
        print("Push it real good!")


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    ex = App()
    app.exec_()<jupyter_output><empty_output><jupyter_text>Tracking progress as someone is typingStart by recording all text typed by the user. Overlay this with what is on the screen to get a score.See the [key enum](https://www.riverbankcomputing.com/static/Docs/PyQt4/qt.htmlKey-enum) defined by PyQt<jupyter_code>from PyQt5.QtCore import Qt

Qt.Key_Backspace
%reset -f
from PyQt5 import QtWidgets, QtGui, QtCore


class MyTextEdit(QtWidgets.QTextEdit):
    def __init__(self, text, parent):
        super().__init__(text, parent)
        self.parent = parent
        self.text_entered = ""

    def keyPressEvent(self, event):
        #print(event.key(), event.text())
        if event.key() == QtCore.Qt.Key_Backspace:
            if len(self.text_entered) > 0:
                self.text_entered = self.text_entered[:-1]
        else:
            text = event.text()
            self.text_entered += text
        self.parent.setWindowTitle(self.text_entered)


class App(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setup_UI()
        self.show()

    def setup_UI(self):
        text = "Some text here. And another line. And some more"
        text_widget = self.text_widget = MyTextEdit(text, self)
        self.setCentralWidget(text_widget)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    ex = App()
    app.exec_()<jupyter_output><empty_output><jupyter_text>Comparing this to our target text<jupyter_code>%reset -f
from PyQt5 import QtWidgets, QtGui, QtCore


class MyTextEdit(QtWidgets.QTextEdit):
    def __init__(self, parent):
        super().__init__("", parent)
        self.parent = parent

    def set_target_text(self, text):
        self.finished = False
        self.text_entered = ""
        self.text_target = text
        self.update_display()

    def keyPressEvent(self, event):
        if self.finished:
            return
        if event.key() == QtCore.Qt.Key_Backspace:
            if len(self.text_entered) > 0:
                self.text_entered = self.text_entered[:-1]
        else:
            text = event.text()
            self.text_entered += text
        self.update_display()

    def update_display(self):
        text_entered = self.text_entered
        text_target = self.text_target

        len_entered = len(text_entered)
        len_target = len(text_target)
        if len_entered >= len_target:
            self.finished = True
            self.parent.setWindowTitle("Finished exercise.")
            

        display_text = ""
        for char_entered,char_target in zip(text_entered, text_target):
            color = "green"
            if char_entered != char_target:
                color = "red"
            display_text += '<span style="color:{}">{}</span>'.format(color, char_target)
        display_text += text_target[len_entered:]
        self.setText(display_text)
        
        cursor = self.textCursor()  # PyQt5.QtGui.QTextCursor instance
        cursor.setPosition(len_entered, QtGui.QTextCursor.MoveAnchor)
        self.setTextCursor(cursor)


class App(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setup_UI()
        self.show()

    def setup_UI(self):
        text_widget = self.text_widget = MyTextEdit(self)
        text_widget.set_target_text("This is the text to enter!")
        self.setCentralWidget(text_widget)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    ex = App()
    app.exec_()<jupyter_output><empty_output><jupyter_text>See the [QTextCursor](https://het.as.utexas.edu/HET/Software/PyQt/qtextcursor.html) docs for details on the [setPosition](https://het.as.utexas.edu/HET/Software/PyQt/qtextcursor.htmlsetPosition) method.<jupyter_code>help(QtGui.QTextCursor.setPosition)<jupyter_output>Help on built-in function setPosition:

setPosition(...)
    setPosition(self, int, mode: QTextCursor.MoveMode = QTextCursor.MoveAnchor)
{'fraction_non_alphanumeric': 0.11015911872705018, 'fraction_numerical': 0.002243982048143615, 'mean_word_length': 2.757088122605364, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 23, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '263159', 'license': 'MIT', 'n_tokens_mistral': 1594, 'n_tokens_neox': 1541, 'n_words': 313, 'path': 'notes/Using PyQt.ipynb', 'repo_name': 'simongarisch/pytypist', 'size': '4902'}