我正在尝试创建多个 GUI 元素来显示视频的文字记录,问题是它在创建 GUI 时冻结,因为它创建了所有 GUI,例如 QTextEdit、QLineEdit、QPushButtons 和 QGraphicsTextItems。需要创建所有这些 GUI 元素,以便用户可以在视频中随时编辑和更改文本。
我尝试过,将 GUI 元素的创建放在 QThread 中,然后发送创建的GUI 元素到主线程,我尝试将 SRT 文件读取器功能放在线程上,但这两个选项都不起作用,因为不允许在线程内创建/管理 GUI 元素,并且 srt 文件功能不是原因它冻结了应用程序:
import sys
from PySide6.QtCore import *
from PySide6.QtWidgets import *
class CreatePreviewText(QObject):
text_data_updated = Signal(list)
def __init__(self, scroll_layout):
super().__init__()
self.text_data = []
self.previous_text = {}
self.scroll_layout = scroll_layout
# SPLITS THE SRT TO, START / END TIMES AND TEXT
def load_srt_file(self, file_path):
try:
with open(file_path, 'r') as file:
srt_lines = file.readlines()
transcripts = []
for line in srt_lines:
if '-->' in line:
start, end = line.strip().split(' --> ')
transcripts.append({'start': start, 'end': end, 'text': ''})
else:
line = line.strip()
if line and transcripts:
transcripts[-1]['text'] += line + '\n'
file.close()
self.create_transcript_widgets(transcripts)
except Exception as e:
print("Error Writing Transcribe To Widget:", e)
# CREATES THE, BUTTONS WIDGET, LINE EDIT WIDGETS, TEXT EDIT WIDGETS
def create_transcript_widgets(self, transcripts):
text_and_duration = []
for transcript in transcripts:
start = transcript['start']
end = transcript['end']
text = transcript['text'].strip()
add_button = QPushButton()
add_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
remove_button = QPushButton()
remove_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
new_button_layout = QHBoxLayout()
new_button_layout.addWidget(add_button)
new_button_layout.addWidget(remove_button)
new_button_layout.addStretch(1)
new_duration_line = QLineEdit()
new_duration_line.setStyleSheet("border: none;")
new_duration_line.setReadOnly(False)
new_duration_line.setInputMask("99:99:99,999 --> 99:99:99,999")
new_text_edit = QTextEdit()
new_text_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
new_text_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
new_text_edit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
new_text_edit.setText(text)
duration_str = f"{start} --> {end}"
new_duration_line.setText(duration_str)
start_total_milliseconds, end_total_milliseconds = self.convert_to_ms(start, end)
self.scroll_layout.addLayout(new_button_layout)
self.scroll_layout.addWidget(new_duration_line)
self.scroll_layout.addWidget(new_text_edit)
text_and_duration.append([new_duration_line, new_text_edit, start_total_milliseconds, end_total_milliseconds, new_button_layout])
self.create_preview_text(text_and_duration)
# CREATES THE QGRAPHICSTEXTITEM FOR MY ACTUAL APP TO SHOW
def create_preview_text(self, text_and_duration):
try:
for duration_line, text_edit, start_total_milliseconds, end_total_milliseconds, button_layout in text_and_duration:
text_preview = QGraphicsTextItem()
self.text_data.append([text_preview, duration_line, text_edit, start_total_milliseconds, end_total_milliseconds, button_layout])
text_preview.hide()
self.text_data_updated.emit(self.text_data)
except Exception as e:
print("Error creating: ", e)
def convert_to_ms(self, start_time, end_time):
start_parts = start_time.split(':')
start_hours = int(start_parts[0])
start_minutes = int(start_parts[1])
start_seconds, start_milliseconds = map(int, start_parts[2].split(','))
start_total_milliseconds = (start_hours * 3600 + start_minutes * 60 + start_seconds) * 1000 + start_milliseconds
end_parts = end_time.split(':')
end_hours = int(end_parts[0])
end_minutes = int(end_parts[1])
end_seconds, end_milliseconds = map(int, end_parts[2].split(','))
end_total_milliseconds = (end_hours * 3600 + end_minutes * 60 + end_seconds) * 1000 + end_milliseconds
return start_total_milliseconds, end_total_milliseconds
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('SRT Preview Tool')
self.setGeometry(100, 100, 800, 600)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.layout = QVBoxLayout()
self.central_widget.setLayout(self.layout)
# Setup scroll layout
scroll_container = QWidget()
scroll_container_layout = QVBoxLayout(scroll_container)
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_container_layout.addWidget(scroll_area)
scroll_content = QWidget(scroll_area)
scroll_area.setWidget(scroll_content)
self.scroll_layout = QVBoxLayout(scroll_content)
self.layout.addWidget(scroll_container)
# Setup buttons
self.load_button = QPushButton('Load SRT File')
self.load_button.clicked.connect(self.load_srt_file)
self.layout.addWidget(self.load_button)
# Setup CreatePreviewText
self.create_preview_text = CreatePreviewText(self.scroll_layout)
def load_srt_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, 'Open SRT File', '', 'SRT Files (*.srt)')
if file_path:
self.create_preview_text.load_srt_file(file_path)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
SRT 文件如下所示:
00:00:04,206 --> 00:00:04,246
TO
00:00:04,266 --> 00:00:04,386
DO
00:00:04,485 --> 00:00:04,686
THREE
00:00:04,726 --> 00:00:04,985
SUPER
的 PyQt 应用程序在创建 GUI 元素时冻结,因为在主线程中执行了所有繁重的工作。这会导致事件循环阻塞,从而使的应用程序看起来冻结。
可以使用以下方法解决此问题:
1. 使用 QThread 和信号
-
创建一个继承自
QObject
的工作线程类。 - 在这个类中,将创建 GUI 元素的任务移到一个单独的函数中。
- 使用信号将创建好的 GUI 元素发送回主线程。
- 在主线程中,连接信号并在接收到信号时将 GUI 元素添加到布局中。
以下是修改后的代码:
import sys
from PySide6.QtCore import *
from PySide6.QtWidgets import *
class CreatePreviewTextWorker(QObject):
text_data_updated = Signal(list)
def __init__(self, parent=None):
super().__init__(parent)
self.text_data = []
@Slot(str)
def load_srt_file(self, file_path):
try:
with open(file_path, 'r') as file:
# ... [load_srt_file 代码与之前相同] ...
self.create_transcript_widgets(transcripts)
except Exception as e:
print("Error Writing Transcribe To Widget:", e)
def create_transcript_widgets(self, transcripts):
# ... [create_transcript_widgets 和 create_preview_text 代码与之前相同] ...
self.text_data_updated.emit(self.text_data)
def convert_to_ms(self, start_time, end_time):
# ... [convert_to_ms 代码与之前相同] ...
class MainWindow(QMainWindow):
def __init__(self):
# ... [__init__ 代码与之前相同] ...
self.worker_thread = QThread(self)
self.worker = CreatePreviewTextWorker()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
self.create_preview_text.text_data_updated.connect(self.update_text_data)
def update_text_data(self, text_data):
for text_preview, duration_line, text_edit, start_total_milliseconds, end_total_milliseconds, button_layout in text_data:
self.scroll_layout.addLayout(button_layout)
self.scroll_layout.addWidget(duration_line)
self.scroll_layout.addWidget(text_edit)
def load_srt_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, 'Open SRT File', '', 'SRT Files (*.srt)')
if file_path:
QMetaObject.invokeMethod(self.worker, 'load_srt_file', Qt.QueuedConnection, Q_ARG(str, file_path))
def closeEvent(self, event):
self.worker_thread.quit()
self.worker_thread.wait()
super().closeEvent(event)
# ... [其余代码与之前相同] ...
在这个改进版本中:
-
我们创建了一个
CreatePreviewTextWorker
类,它继承自QObject
并包含了创建 GUI 元素的代码。 -
load_srt_file
函数现在是一个Slot
,可以使用QMetaObject.invokeMethod
从主线程调用。 -
text_data_updated
信号用于将创建好的 GUI 元素发送回主线程。 -
在
MainWindow
中,我们创建了一个新的线程self.worker_thread
并将CreatePreviewTextWorker
实例移动到该线程。 -
update_text_data
函数连接到text_data_updated
信号,并在接收到信号时将 GUI 元素添加到布局中。
2. 使用 QApplication.processEvents()
-
在创建 GUI 元素的循环中定期调用
QApplication.processEvents()
。 这将允许事件循环处理待处理的事件,包括更新 UI。
def create_transcript_widgets(self, transcripts):
# ... [代码与之前相同] ...
for i, transcript in enumerate(transcripts):
# ... [创建 GUI 元素的代码] ...
if i % 10 == 0: # 每创建 10 个元素就处理一次事件
QApplication.processEvents()
self.create_preview_text(text_and_duration)
这种方法更简单,但它可能无法完全解决问题,尤其是在创建大量 GUI 元素时。
建议
建议使用第一种方法,因为它可以更好地将 GUI 更新与数据处理分离,从而提供更流畅的用户体验。
标签:python,qt,pyqt,pyside6 From: 78773020