Python 3.x 如何在Python的gui和非gui应用程序中使用QThread?
我正在编写一个模糊搜索应用程序,它可以在文本中查找单词,即使单词有错误。我写了gui表单,但由于计算量大,它冻结了。所以我创建了一个继承自QThread的类,并从中向gui发送信号,这样进度条就可以开始工作,gui表单就不再冻结。但我也应该创建这个应用程序的控制台版本,在这个版本中,我不需要gui表单,但需要在继承自QThread的类中编写的方法。但不使用PyQT库是不可能的,在控制台版本中使用PyQT库很奇怪。所以我不知道如何解决这个问题。我的老师建议使用线程,但我没有发现如何像在QThread中那样从Thread类发出信号 这是一个QThread类Python 3.x 如何在Python的gui和非gui应用程序中使用QThread?,python-3.x,pyqt5,qthread,Python 3.x,Pyqt5,Qthread,我正在编写一个模糊搜索应用程序,它可以在文本中查找单词,即使单词有错误。我写了gui表单,但由于计算量大,它冻结了。所以我创建了一个继承自QThread的类,并从中向gui发送信号,这样进度条就可以开始工作,gui表单就不再冻结。但我也应该创建这个应用程序的控制台版本,在这个版本中,我不需要gui表单,但需要在继承自QThread的类中编写的方法。但不使用PyQT库是不可能的,在控制台版本中使用PyQT库很奇怪。所以我不知道如何解决这个问题。我的老师建议使用线程,但我没有发现如何像在QThrea
import text_methods
from PyQt5.QtCore import pyqtSignal, QThread
class FuzzySearch(QThread):
sig_words_count = pyqtSignal(int)
sig_step = pyqtSignal(int)
sig_done = pyqtSignal(bool)
sig_insertions = pyqtSignal(str)
sig_insertions_indexes = pyqtSignal(list)
def __init__(self, text, words, case_sensitive):
super().__init__()
self.text = text
self.words = words
self.case_sensitive = case_sensitive
self.insertions_indexes = {}
self.text_dict = {}
def run(self):
self.get_insertions_info(self.text, self.words)
def find_insertions_of_word(self, word, word_number):
word_insertions = {}
for textword in self.text_dict.keys():
if text_methods.is_optimal_distance(word, textword):
word_insertions[textword] = self.text_dict[textword]
for index in self.text_dict[textword]:
self.insertions_indexes[index] = index + len(textword)
self.sig_step.emit(word_number)
return word_insertions
'''Get information about insertions of words in the text'''
def find_insertions(self, text, words):
word_number = 1
insertions = {}
self.text_dict = text_methods.transform_text_to_dict(text, self.case_sensitive)
words_list = text_methods.transform_words_to_list(words, self.case_sensitive)
self.sig_words_count.emit(len(words_list))
for word in words_list:
print(word_number)
insertions[word] = self.find_insertions_of_word(word, word_number)
word_number += 1
self.insertions_indexes = sorted(self.insertions_indexes.items())
return insertions
'''Get information about insertions of words in the text in special format'''
def get_insertions_info(self, text, words):
insertions = self.find_insertions(text, words)
insertions_info = ''
for word in insertions.keys():
insertions_info += 'Вы искали слово "' + word + '"\n'
if len(insertions[word]) == 0:
insertions_info += ' По этому запросу не было найдено слов\n'
else:
insertions_info += ' По этому запросу были найдены слова:\n'
for textword in insertions[word].keys():
insertions_info += ' "' + textword + '" на позициях: '
insertions_info += ", ".join([str(i) for i in insertions[word][textword]])
insertions_info += '\n'
self.sig_done.emit(True)
self.sig_insertions.emit(insertions_info)
self.sig_insertions_indexes.emit(self.insertions_indexes)
self.quit()
正如您所看到的,我将许多发出的信号传输到gui模块,在该模块中,它们连接到方法findu insertions中的类FindButton中的插槽:
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QApplication, QWidget,\
QLabel, QPushButton, QTextEdit, QFileDialog,\
QMessageBox, QProgressBar, QCheckBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from fuzzysearch import FuzzySearch
import sys
class OpenButton(QPushButton):
def __init__(self, name, font, textedit):
super().__init__(name, font=font)
self.textedit = textedit
self.clicked.connect(self.open_dialog)
def open_dialog(self):
fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')
if fname[0]:
with open(fname[0], 'r') as f:
data = f.read()
self.textedit.setText(data)
class FindButton(QPushButton):
def __init__(self, name, font, text, words, result, window):
super().__init__(name, font=font)
self.window = window
self.textedit = text
self.wordsedit = words
self.resultedit = result
self.checkbox = window.case_sensitive_checkbox
self.clicked.connect(self.find_insertions)
def find_insertions(self):
text = self.textedit.toPlainText()
words = self.wordsedit.toPlainText()
if text == '':
QMessageBox.information(self, 'Нет текста',
'Текст не был введен. \nВведите текст.')
elif words == '':
QMessageBox.information(self, 'Нет слов',
'Слова не были введены. \nВведите слова через запятую.')
else:
self.setDisabled(True)
self.text_editor = TextEditor(text, self.textedit)
self.fuzzy_search = FuzzySearch(text, words, self.checkbox.checkState())
self.fuzzy_search.sig_words_count.connect(self.window.progress_bar.setMaximum)
self.fuzzy_search.sig_step.connect(self.window.progress_bar.setValue)
self.fuzzy_search.sig_done.connect(self.setEnabled)
self.fuzzy_search.sig_insertions.connect(self.resultedit.setText)
self.fuzzy_search.sig_insertions_indexes.connect(self.text_editor.mark)
self.fuzzy_search.start()
class TextEditor:
def __init__(self, text, textedit):
self.text = text
self.textedit = textedit
def mark(self, to_mark):
self.textedit.clear()
current_index = 0
for item in to_mark:
self.write_not_marked_text(self.text[current_index:item[0]])
self.write_marked_text(self.text[item[0]:item[1]])
current_index = item[1]
self.write_not_marked_text(self.text[current_index:])
def write_not_marked_text(self, text):
font = QFont("Times", 10)
font.setItalic(False)
font.setBold(False)
self.textedit.setCurrentFont(font)
self.textedit.setTextColor(Qt.black)
self.textedit.insertPlainText(text)
def write_marked_text(self, text):
font = QFont("Times", 10)
font.setItalic(True)
font.setBold(True)
self.textedit.setCurrentFont(font)
self.textedit.setTextColor(Qt.red)
self.textedit.insertPlainText(text)
class Window(QWidget):
def __init__(self, font):
super().__init__()
self.standard_font = font
self.text_edit_font = QFont("Times", 10)
text_label = QLabel("Введите или откройте текст",
font=self.standard_font)
words_label = QLabel("Введите или откройте слова (через запятую)",
font=self.standard_font)
result_label = QLabel("Результат",
font=self.standard_font)
text_edit = QTextEdit(font=self.text_edit_font)
words_edit = QTextEdit(font=self.text_edit_font)
result_edit = QTextEdit(font=self.text_edit_font)
self.case_sensitive_checkbox = QCheckBox('Учитывать регистр')
self.case_sensitive_checkbox.setFont(self.standard_font)
self.progress_bar = QProgressBar()
self.progress_bar.setValue(0)
open_btn1 = OpenButton("Открыть", self.standard_font, text_edit)
open_btn2 = OpenButton("Открыть", self.standard_font, words_edit)
find_btn = FindButton("Найти слова в тексте", self.standard_font,
text_edit, words_edit, result_edit, self)
text_label_box = QHBoxLayout()
text_label_box.addWidget(text_label, alignment=Qt.AlignLeft)
text_label_box.addWidget(open_btn1, alignment=Qt.AlignRight)
words_label_box = QHBoxLayout()
words_label_box.addWidget(words_label, alignment=Qt.AlignLeft)
words_label_box.addWidget(open_btn2, alignment=Qt.AlignRight)
words_box = QVBoxLayout()
words_box.addLayout(words_label_box)
words_box.addWidget(words_edit)
result_box = QVBoxLayout()
result_box.addWidget(result_label, alignment=Qt.AlignLeft)
result_box.addWidget(result_edit)
bottom_box = QHBoxLayout()
bottom_box.addLayout(words_box)
bottom_box.addLayout(result_box)
find_and_progress_box = QHBoxLayout()
find_and_progress_box.addWidget(find_btn, alignment=Qt.AlignLeft)
find_and_progress_box.addWidget(self.case_sensitive_checkbox)
find_and_progress_box.addWidget(self.progress_bar)
main_box = QVBoxLayout()
main_box.addLayout(text_label_box)
main_box.addWidget(text_edit)
main_box.addLayout(bottom_box)
main_box.addLayout(find_and_progress_box)
self.setLayout(main_box)
self.setGeometry(300, 300, 1100, 700)
self.setWindowTitle('Нечеткий поиск')
self.show()
def start_application():
app = QApplication(sys.argv)
w = Window(QFont("Times", 12))
sys.exit(app.exec_())
而且它工作得很好。但它在控制台版本中不起作用,因为如果没有QEventLoop,QThread将无法工作:
import fuzzysearch
class ConsoleVersion():
def __init__(self, text, words):
self.text = text
self.words = words
def search_words_in_text(self):
with self.text:
with self.words:
self.f = fuzzysearch.FuzzySearch(self.text.read(), self.words.read(), False)
self.f.sig_insertions.connect(self.get_insertions)
self.f.start()
def get_insertions(self, insertions):
print(insertions)
在主文件中,我编写了解析参数和两个版本之间的选择
import argparse
import gui
import console_version
def parse_args():
parser = argparse.ArgumentParser(description='Fuzzy search in text')
parser.add_argument('-g', '--graphics', help='graphical version', action='store_true')
parser.add_argument('-c', '--console', help='console version', nargs=2, type=argparse.FileType('r'), metavar=('TEXTFILE', 'WORDSFILE'))
return parser.parse_args()
if __name__ == '__main__':
args = parse_args()
if args.graphics:
gui.start_application()
if args.console:
cv = console_version.ConsoleVersion(args.console[0], args.console[1])
cv.search_words_in_text()
和模块文本方法:
from re import split, sub
def transform_text_to_dict(text, case_sensitive):
text_dict = {}
index = 0
if case_sensitive:
splitted_text = split("[^'а-яА-ЯA-Za-z0-9_-]", text)
else:
splitted_text = split("[^'а-яА-ЯA-Za-z0-9_-]", text.lower())
for element in splitted_text:
if element not in text_dict:
text_dict[element] = []
text_dict[element].append(index)
index += len(element) + 1
return text_dict
def transform_words_to_list(words, case_sensitive):
words = sub("^\s+|\n|\r|\s+$", '', words)
if case_sensitive:
return split(' *, *', words)
else:
return split(' *, *', words.lower())
'''Damerau-Levenstein'''
def find_distance(word1: str, word2: str):
len1, len2 = len(word1), len(word2)
if len1 > len2:
word1, word2 = word2, word1
len1, len2 = len2, len1
current_row = range(len1 + 1)
previous_row = range(len1 + 1)
pre_previous_row = range(len1 + 1)
for i in range(1, len2 + 1):
if i == 1:
previous_row, current_row = current_row, [i] + [0] * len1
else:
pre_previous_row, previous_row, current_row = previous_row, current_row, [i] + [0] * len1
for j in range(1, len1 + 1):
add = previous_row[j] + 1
delete = current_row[j - 1] + 1
change = previous_row[j - 1]
if word1[j - 1] != word2[i - 1]:
change += 1
if word1[j - 1] == word2[i - 2] and word1[j - 2] == word2[i - 1]:
transpose = pre_previous_row[j - 2] + 1
current_row[j] = min(add, delete, change, transpose)
else:
current_row[j] = min(add, delete, change)
return current_row[len1]
def is_optimal_distance(word1 : str, word2 : str):
distance = find_distance(word1, word2)
l = min(len(word1), len(word2))
return distance <= l // 4
从重新导入拆分,子
def将文本转换为dict(文本,区分大小写):
text_dict={}
索引=0
如果区分大小写:
拆分文本=拆分([^'а-аА-ЯA-Za-z0-9а-],文本)
其他:
拆分的文本=拆分([^'а-аА-ЯA-Za-z0-9а-]),文本。下()
对于拆分文本中的元素:
如果元素不在文本目录中:
文本内容[元素]=[]
text_dict[元素].追加(索引)
索引+=len(元素)+1
返回文本
def将单词转换为单词列表(单词,区分大小写):
words=sub(“^\s+|\n |\r |\s+$”,“”,words)
如果区分大小写:
返回拆分(“*,*”,单词)
其他:
返回拆分(“*,*”,words.lower()
“Damerau-Levenstein”
def查找距离(word1:str,word2:str):
len1,len2=len(单词1),len(单词2)
如果len1>len2:
word1,word2=word2,word1
len1,len2=len2,len1
当前行=范围(len1+1)
上一行=范围(len1+1)
前一行=范围(len1+1)
对于范围(1,len2+1)中的i:
如果i==1:
上一行,当前行=当前行,[i]+[0]*len1
其他:
pre_previous_row,previous_row,current_row=previous_row,current_row,[i]+[0]*len1
对于范围(1,len1+1)内的j:
添加=上一行[j]+1
删除=当前_行[j-1]+1
更改=上一行[j-1]
如果单词1[j-1]!=字2[i-1]:
更改+=1
如果word1[j-1]==word2[i-2]和word1[j-2]==word2[i-1]:
转置=前一行[j-2]+1
当前_行[j]=min(添加、删除、更改、转置)
其他:
当前_行[j]=min(添加、删除、更改)
返回当前_行[len1]
def是最佳距离(word1:str,word2:str):
距离=查找距离(单词1,单词2)
l=min(len(单词1),len(单词2))
返回距离Qt,因此为了处理任务,始终需要为其内部创建一个循环,必须构造一个类型为QCoreApplication
、QGuiApplication
或QApplication
的对象,例如,QThread
不是一个线程,而是一个监视线程状态的线程处理程序,如果您不放置它,那么应用程序将立即关闭,因为run方法没有在主线程中执行
if args.console:
app = QCoreApplication(sys.argv)
cv = console_version.ConsoleVersion(args.console[0], args.console[1])
cv.search_words_in_text()
sys.exit(app.exec_())
在控制台版本中,它不能很好地工作,这意味着什么?您还可以演示如何使用ConsoleVersion类。但它在console版本中不起作用,因为如果没有QEventLoop,QThread将不起作用。我必须为您的代码提供什么类型的输入?你能给我举个例子吗?我无法测试你的代码,因为我缺少text\u methods模块,但我认为你的代码需要放置:app=QCoreApplication(sys.argv)
在创建cv.Changeif args.console:cv=console\u version.consolevervion(args.console[0],args.console[1])cv.search\u words\u in\u text()
toif args.console:app=QCoreApplication(sys.argv)cv=console\u version.consolevervion(args.console[0],args.console[1])cv.search\u words\u in\u text()sys.exit(app.exec\ux())