Как обрабатывать одновременно два события PyQt5
Пишу приложение по измерению скорости печати.
Столкнулся с проблемой: при введении текста в self.area_for_typing мне нужно обработать сразу два события (не уверен, что это так называется): start и contents_change.
Но из-за цикла в start работать корректно может только один из методов.
Я погуглил и узнал про возможность создания многопоточности, попытался ее сделать, но не получилось.
Как при введении текста сделать так, чтобы одновременно работал и start, и contents_change?
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys, random, time
from PyQt5.Qt import *
from PyQt5 import QtWidgets
from PyQt5.QtTest import *
class Window(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.text_for_typing = QTextEdit(self)
self.text_for_typing.setEnabled(False)
self.text_for_typing.setGeometry(0, 53, 600, 244)
self.text_for_typing.setStyleSheet("color: rgb(255,255,255); \
font: 24pt Arial Rounded MT Bold;")
self.numbers = "0123456789"
self.wpm = QLabel(self)
self.wpm.setGeometry(0,0, 197, 47)
self.wpm.setStyleSheet("font: 28pt Arial Rounded MT Bold; \
color: rgb(255,0,0);")
self.wpm.setText("<center>0 WPM</center>")
self.secs = QLabel(self)
self.secs.setGeometry(203, 0, 195, 47)
self.secs.setStyleSheet("font: 28pt Arial Rounded MT Bold; \
color: rgb(0,85,0);")
self.secs.setText("<center>0 Sec</center>")
self.accuracy = QLabel(self)
self.accuracy.setGeometry(410, 0, 197, 47)
self.accuracy.setStyleSheet("font: 28pt Arial Rounded MT Bold; \
color: rgb(0,85,127);")
self.accuracy.setText("<center>0 %</center>")
self.area_for_typing = QTextEdit(self)
self.area_for_typing.setGeometry(0,303,600,450)
self.area_for_typing.setStyleSheet("color: rgb(170,170,127); \
font: 16pt Arial Rounded MT Bold;")
self.area_for_typing.document().contentsChange.connect(self.contents_change)
self.format = QTextCharFormat()
self.format.setFont(QFont("Arial", 22, QFont.Bold))
self.setStyleSheet("border-radius: 10px; \
background-color: rgb(0, 0, 0);")
self.setWindowTitle("TypingSpeed")
self.setWindowIcon(QIcon("keyboard.ico"))
self.resize(600,450)
self.setFixedSize(self.size())
self.generate_symbols()
self.show()
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QPen(Qt.white,4,Qt.SolidLine))
painter.drawLine(0, 50, 600, 50)
painter.drawLine(200,50,200,0)
painter.drawLine(400, 50, 400, 0)
painter.drawLine(0,300,600,300)
def contents_change(self, position, charsRemoved, charsAdded):
if not self.area_for_typing.document().toPlainText():
return
cursor = self.text_for_typing.textCursor()
cursor.setPosition(position)
end = cursor.movePosition(QTextCursor.NextCharacter, 1)
self.text = self.text_for_typing.toPlainText()
try:
if end:
letter_text = self.text[position]
letter_area_for_typing = self.area_for_typing.document().toPlainText()[position]
if letter_text == letter_area_for_typing:
self.format.setTextOutline(QPen(QColor("green")))
else:
self.format.setTextOutline(QPen(QColor("red")))
except:
pass
cursor.mergeCharFormat(self.format)
def generate_symbols(self):
text_for_typing = ""
strings = []
file = open("ENRUS .TXT", encoding = "utf-8")
file.seek(0)
for line in file:
strings.append(line[:-1])
for i in range(20):
random_int = random.randint(0,97)
word = strings[random_int]
text_for_typing+= word + " "
text_for_typing = text_for_typing[0].swapcase() + text_for_typing[1:]
self.text_for_typing.setText(f"<center>{text_for_typing}</center>")
def start(self):
self.seconds = 0
self.current_wpm = 0
self.accuracy_per = 0
if self.area_for_typing.toPlainText() != "":
try:
while True:
text_for_typing = self.text_for_typing.toPlainText()
area_text = self.area_for_typing.toPlainText()
correct_letters = 0
allow = False
for i in range(0,len(area_text)):
if area_text != "":
if text_for_typing[i] == area_text[i]:
correct_letters+=1
if len(text_for_typing)-1 <= len(area_text):
allow = True
break
if allow == False:
QTest.qWait(1000)
self.seconds+=1
if self.seconds > 0 and correct_letters > 0:
self.accuracy_per = round(( correct_letters / len(area_text) ) * 100)
self.current_wpm = round(((correct_letters/self.seconds)*60)/7.2) #7.2 - средняя длина русского слова
self.secs.setText(f"<center>{self.seconds} Sec</center>")
self.wpm.setText(f"<center>{self.current_wpm} WPM</center>")
self.accuracy.setText(f"<center>{self.accuracy_per} %</center>")
qApp.processEvents()
except:
pass
if len(text_for_typing)-1 <= len(area_text):
self.text_for_typing.clear()
self.area_for_typing.clear()
self.generate_symbols()
self.wpm.setText("<center>0 WPM</center>")
self.secs.setText("<center>0 Sec</center>")
self.accuracy.setText("<center>0 %</center>")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
Ответы (1 шт):
Я не сильно вникал в то что вы написали сегодня.
Запомните, никогда не используйте циклы while True и time.sleep() в основном потоке приложения.
Если какие-либо расчеты занимают много времени - вам нужен дополнительный поток.
Если расчеты НЕ занимают много времени, но требую периодического выполнения (это ваш случай) - вам нужен класс QTimer.
import sys
import random
#import time
from PyQt5.Qt import *
from PyQt5 import QtWidgets
#from PyQt5.QtTest import *
class Window(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.text_for_typing = QTextEdit(self)
self.text_for_typing.setEnabled(False)
self.text_for_typing.setGeometry(0, 53, 600, 244)
self.text_for_typing.setStyleSheet("color: rgb(255,255,255); \
font: 17pt Arial Rounded MT Bold;")
self.numbers = "0123456789"
self.wpm = QLabel(self)
self.wpm.setGeometry(0,0, 197, 47)
self.wpm.setStyleSheet("font: 28pt Arial Rounded MT Bold; \
color: rgb(255,0,0);")
self.wpm.setText("<center>0 WPM</center>")
self.secs = QLabel(self)
self.secs.setGeometry(203, 0, 195, 47)
self.secs.setStyleSheet("font: 28pt Arial Rounded MT Bold; \
color: rgb(0,85,0);")
self.secs.setText("<center>0 Sec</center>")
self.accuracy = QLabel(self)
self.accuracy.setGeometry(410, 0, 197, 47)
self.accuracy.setStyleSheet("font: 28pt Arial Rounded MT Bold; \
color: rgb(0,85,127);")
self.accuracy.setText("<center>0 %</center>")
self.area_for_typing = QTextEdit(self)
self.area_for_typing.setGeometry(0,303,600,450)
self.area_for_typing.setStyleSheet("color: rgb(170,170,127); \
font: 16pt Arial Rounded MT Bold;")
self.area_for_typing.document().contentsChange.connect(self.contents_change)
self.format = QTextCharFormat()
self.format.setFont(QFont("Arial", 22, QFont.Bold))
self.setStyleSheet("border-radius: 10px; \
background-color: rgb(0, 0, 0);")
self.setWindowTitle("TypingSpeed")
self.setWindowIcon(QIcon("keyboard.ico"))
self.resize(600, 450)
# self.setFixedSize(self.size())
self.generate_symbols()
# self.show()
# +++
self.timer = QTimer() # +++
self.timer.timeout.connect(self.start) # +++
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QPen(Qt.white,4,Qt.SolidLine))
painter.drawLine(0, 50, 600, 50)
painter.drawLine(200, 50, 200, 0)
painter.drawLine(400, 50, 400, 0)
painter.drawLine(0, 300, 600, 300)
def contents_change(self, position, charsRemoved, charsAdded):
if not self.area_for_typing.document().toPlainText():
# ???
return
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
if len(self.area_for_typing.document().toPlainText()) == 1:
self.seconds = 0
self.current_wpm = 0
self.accuracy_per = 0
self.timer.start(1000)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cursor = self.text_for_typing.textCursor()
cursor.setPosition(position)
end = cursor.movePosition(QTextCursor.NextCharacter, 1)
self.text = self.text_for_typing.toPlainText()
try:
if end:
letter_text = self.text[position]
letter_area_for_typing = self.area_for_typing.document().toPlainText()[position]
if letter_text == letter_area_for_typing:
self.format.setTextOutline(QPen(QColor("green")))
else:
self.format.setTextOutline(QPen(QColor("red")))
except:
pass
cursor.mergeCharFormat(self.format)
def generate_symbols(self):
text_for_typing = ""
strings = []
# txt
file = open("ENRUS.txt", encoding = "utf-8")
file.seek(0)
for line in file:
strings.append(line[:-1])
for i in range(20):
# random_int = random.randint(0, 97)
random_int = random.randint(0, 20)
word = strings[random_int]
text_for_typing+= word + " "
text_for_typing = text_for_typing[0].swapcase() + text_for_typing[1:]
self.text_for_typing.setText(f"<center>{text_for_typing}</center>")
def start(self):
print(f'def start(self): ==================== работает') #
# self.seconds = 0
# self.current_wpm = 0
# self.accuracy_per = 0
if self.area_for_typing.toPlainText() != "":
try:
# ??? while True:
text_for_typing = self.text_for_typing.toPlainText()
area_text = self.area_for_typing.toPlainText()
correct_letters = 0
allow = False
for i in range(0,len(area_text)):
if area_text != "":
if text_for_typing[i] == area_text[i]:
correct_letters+=1
if len(text_for_typing)-1 <= len(area_text):
allow = True
# ??? SyntaxError: 'break' вне цикла
# ??? break
return
if allow == False:
# ??? QTest.qWait(1000)
self.seconds += 1
if self.seconds > 0 and correct_letters > 0:
self.accuracy_per = round(( correct_letters / len(area_text) ) * 100)
self.current_wpm = round(((correct_letters/self.seconds)*60)/7.2) #7.2 - средняя длина русского слова
self.secs.setText(f"<center>{self.seconds} Sec</center>")
self.wpm.setText(f"<center>{self.current_wpm} WPM</center>")
self.accuracy.setText(f"<center>{self.accuracy_per} %</center>")
# ??? qApp.processEvents()
except:
pass
if len(text_for_typing)-1 <= len(area_text):
self.text_for_typing.clear()
self.area_for_typing.clear()
self.generate_symbols()
self.wpm.setText("<center>0 WPM</center>")
self.secs.setText("<center>0 Sec</center>")
self.accuracy.setText("<center>0 %</center>")
# +++
self.timer.stop() # +++
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
