0

I'm trying to make Typing speed test app in Pyqt, but it recently started crashing when I was inside QLineEdit. Sometimes it crashed instantly after I tried typing, sometimes only after tens of character were typed.

My code:

from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication, QStackedWidget, QPushButton, QSizePolicy, QLabel, QLineEdit, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import *
import sys
import time
import random
import threading

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("Typing Speed Test App")
        self.setMinimumSize(1280,720)

        global stacked
        stacked = QStackedWidget(self)
        self.setCentralWidget(stacked)
        stacked.addWidget(Menu())
        stacked.addWidget(TS_Test())
        stacked.addWidget(Statistics())

        stacked.setCurrentIndex(1)          # test only


class Menu(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.frameWidth = self.frameSize().width()
        self.frameHeight = self.frameSize().height() 
        self.initUI()
    def initUI(self):
        self.button_test = QPushButton(self)
        self.button_graph = QPushButton(self)
        self.button_test.setFixedWidth(50)

    
        self.button_test.clicked.connect(lambda: stacked.setCurrentIndex(1))
        self.button_graph.clicked.connect(lambda: stacked.setCurrentIndex(2))
        self.button_graph.clicked.connect(lambda: print(self.frameSize().width()))

        self.button_test.move(self.frameSize().width()*50//100-50,200)
        self.button_graph.move(200,230)
    

class TS_Test(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        f = open('paragraphs.txt').read()
        self.sentences = f.split('BREAK\n')
        self.sentence = random.choice(self.sentences)
        self.sentence = self.sentence.strip('\n')
        self.word = self.sentence.split()
        self.setStyleSheet("QLabel{font-size: 15px;}")

        self.initUI()
        self.start_thread()

    def initUI(self):
        self.button_back = QPushButton(self)
        self.button_back.clicked.connect(lambda: stacked.setCurrentIndex(0))
        self.button_back.move(30,50)

        self.lineEdit = QLineEdit()
        self.label = QLabel()
        self.accuracy_label = QLabel()
        self.wpm_label = QLabel()

        self.first_letter = self.sentence[0]

        self.layout = QVBoxLayout(self)

        self.layout.addWidget(self.label)
        self.layout.addWidget(self.lineEdit)
        self.layout.addWidget(self.accuracy_label)
        self.layout.addWidget(self.wpm_label)

        
        self.label.setText(self.sentence)
    
        self.layout.setContentsMargins(250,250,250,300)
        self.setLayout(self.layout)

    def start_thread(self):                   
        self.t_start=threading.Thread(target=self.start)
        self.t_start.start()                              

    def start(self):
        while True:
            if len(self.lineEdit.text()) > 0:
                if self.lineEdit.text()[0] == self.first_letter:
                    self.time_thread()
                    break
                
    def time_thread(self):
        print('start')
        timer_start = time.perf_counter()
        self.correct_char = 0
        
        
        while True:
            if (len(self.lineEdit.text()) == len(self.sentence)) and (self.lineEdit.text().split()[-1] == self.word[-1]):
                self.written_word = self.lineEdit.text().split(' ')
                timer_stop = time.perf_counter()
                timer = timer_stop - timer_start

                self.wpm = len(self.written_word) / timer * 60

                for i in range(len(self.sentence)):
                    if self.lineEdit.text()[i] == self.sentence[i]:
                        self.correct_char += 1
                self.accuracy = self.correct_char / len(self.sentence) * 100

                print(f"Accuracy = {self.correct_char / len(self.sentence) * 100}")
                print(f'WPM: {self.wpm:0.3f}')
                
                self.accuracy_label.setText(f'Accuracy = {self.accuracy}%')
                self.wpm_label.setText(f'WPM: {self.wpm:0.3f}')

                break


class Statistics(QWidget):
    def __init__(self):
        QWidget.__init__(self)        
        self.initUI()

    def initUI(self):
        self.button_back = QPushButton(self)
        self.button_back.clicked.connect(lambda: stacked.setCurrentIndex(0))
        self.button_back.move(400,300)
    


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainwin = MainWindow()
    mainwin.show()
    sys.exit(app.exec_())

I've tried doing try and except so that it can print me traceback error, but even that didn't print anything. I read somewhere that it might be because I'm using library threading instead of QThread, but I have no idea if that has something to do with it.

EDIT: Content in paragraphs.txt:

This line serves as test.
BREAK
Sentence number one.
BREAK
Sentence number two.
BREAK
Line number three.
  • found QT, found thread, found segmentation fault , time to increase the qt threading segfaults counter by 1 , if i had a dollar for everytime i saw these 3 together i'd definitely not need to browse stack overflow anymore. – Ahmed AEK Oct 30 '22 at 18:50
  • the obvious solution is to not update the GUI from the threads, but without an example of `'paragraphs.txt'` none can tell you how, also QThread is more likely to cause segmentation faults than plain threading. – Ahmed AEK Oct 30 '22 at 18:52
  • 1
    Qt does not support GUI operations of any kind outside the main thread. You must use signals to communicate in a thread-safe way between worker threads and the main thread. – ekhumoro Oct 30 '22 at 19:01
  • @AhmedAEK edited to show example of .txt file – Aethelsirius Oct 30 '22 at 19:01

0 Answers0