首页 > 编程问答 >创建 GUI 元素时 PyQt 应用程序冻结

创建 GUI 元素时 PyQt 应用程序冻结

时间:2024-07-21 03:49:28浏览次数:12  
标签:python qt pyqt pyside6

我正在尝试创建多个 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

相关文章

  • python 舰队容器
    我正在尝试使用容器在flet中制作一个菜单,它应该是半透明的,但其中的项目不是。我尝试将opacity=1分配给元素,但没有成功-它们与容器一样透明感谢任何帮助我的代码:nickname=ft.TextField(label="xxx",hint_text="xxx")column=ft.Column(controls=[nickname......
  • Python应用程序跨子包共享的配置文件
    我正在构建一个应用程序来控制一些硬件。我在包中实现了不同类型的硬件:电机和测量设备。我的文件结构如下:name_of_my_app/__init__.pymain.pyconfig.iniCONFIG.pymotors/__init__.pyone_kind_of_motor.pymeasurement_devices/......
  • python中时间序列数据的梯度计算
    我正在尝试编写一个函数,它可以从最适合下面的线返回梯度dataframe在浏览了谷歌的几个资源之后,我仍然不确定这是如何完成的。我明白最佳拟合线的计算公式为:y=mx+b将因变量(y)设置为foos,将自变量(x)设置为DateTimeDatafram......
  • 调试用 C 编写的 Python 扩展
    我非常熟悉编写C代码,并且很擅长编写Python代码。我正在尝试学习如何用C编写可以从OSX10.15.7上的Python-3.9.X调用的模块。我已经得到了几个“helloworld”类型的示例,但是对于复杂的示例,我正在努力弄清楚如何调试我编写的C扩展。MWE:src/add.c//......
  • 具有块大小选项的 Python pandas read_sas 因索引不匹配而失败并出现值错误
    我有一个非常大的SAS文件,无法容纳在我的服务器内存中。我只需要转换为镶木地板格式的文件。为此,我使用pandas中chunksize方法的read_sas选项分块读取它。它主要是在工作/做它的工作。除此之外,一段时间后它会失败并出现以下错误。此特定SAS文件有794......
  • 使用 requests 包 python 时打开文件太多
    我正在使用Pythonrequests包向API发出大量请求。然而,在某些时候,我的程序由于“打开的文件太多”而崩溃。当我明确关闭我的会话时,我真的不知道这是怎么回事。我使用以下代码:importrequestsimportmultiprocessingimportnumpyasnps=requests.session()s.keep......
  • Python 是一种选择性解释语言吗?为什么下面的代码不起作用?
    由于程序是从上到下运行的,为什么下面的代码不执行块中的第一行就直接抛出错误?if5>2:print("TwoislessthanFive!")print("Fiveisgreaterthantwo!")错误:文件“/Users/____/Desktop/Pythonpractise/practise.py”,第3行print("五比二大!")Indentati......
  • 裁剪时间变量 Python Matplotlib Xarray
    我不确定这是否是一个愚蠢的问题,但我想按时间变量剪辑.nc文件。我在xarray中打开了数据集,但以下ds.sel行(之前已运行)仅返回错误。ds=xr.open_dataset('/Users/mia/Desktop/RMP/data/tracking/mcs_tracks_2015_11.nc')selected_days=ds.sel(time=slice('2015-11-22',......
  • 用于匹配两个数据列表中的项目的高效数据结构 - python
    我有两个列表,其中一个列表填充ID,另一个列表填充进程名称。多个进程名称可以共享一个ID。我希望能够创建一个可以使用特定ID的数据结构,然后返回与该ID关联的进程列表。我还希望能够使用特定的进程名称并返回与其连接的ID列表。我知道我可以为此创建一个字典,但是I......
  • 有人可以解决我的代码中的问题吗?而且我无法在我的电脑上安装 nsetools。如何在 python
    从nsetools导入Nseimportpandasaspdnse=Nse()all_stock_codes=nse.get_stock_codes()companies_with_low_pe=[]对于all_stock_codes中的代码:如果代码=='符号':继续尝试:stock_quote=nse.get_quote(代码)pe_ratio=stock_quote.get('priceT......