为啥即使使用 QThreadPool,GUI 也会冻结?

Posted

技术标签:

【中文标题】为啥即使使用 QThreadPool,GUI 也会冻结?【英文标题】:Why does the GUI freeze even with QThreadPool?为什么即使使用 QThreadPool,GUI 也会冻结? 【发布时间】:2017-05-27 21:59:25 【问题描述】:

我正在尝试构建一个使用简单 QTextEdit 的自定义 Python 控制台小部件。行编辑框接收输入并通过线程中的 python 解释器运行它。

这里是interpreter.py

import sys
from io import StringIO, IncrementalNewlineDecoder
from code import InteractiveConsole
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
from stream import NewLineIO

class PythonInterpreter(QObject, InteractiveConsole):
    output = pyqtSignal(str)

    def __init__(self):
        QObject.__init__(self)
        self.l = 
        InteractiveConsole.__init__(self, self.l)
        self.out = NewLineIO()
        self.out.output.signal_str.connect(self.console)

    def write(self, string):
        self.output.emit(string)

    def runcode(self, codez):
        """
        Reimplementation to capture stdout and stderr
        """
        sys.stdout = self.out
        sys.stderr = self.out
        sys.excepthook = sys.__excepthook__
        result = InteractiveConsole.runcode(self, codez) # Where the magic happens
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__
        #self.output.emit(self.out.getvalue()) # Send the output
        return result

    @pyqtSlot(str)
    def console(self, string):
        #print(string, file=sys.__stdout__)
        self.output.emit(string)

class javascriptInterpreter(QObject):
    pass

这是 main.py

import sys
from traceback import TracebackException
from PyQt5 import QtCore, uic
from PyQt5.QtCore import QThread, QThreadPool
from PyQt5.QtGui import QFont, QColor
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QAction)
from PyQt5.Qsci import QsciScintilla, QsciLexerPython, QsciAPIs
from interpreters import PythonInterpreter
from lexers import PythonLexer
from threads import Worker, WorkerSignals
from stream import NewLineIO

class MainWindow(QDialog):

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.ui = uic.loadUi("main.ui")
        self.ui.showMaximized()

        # Code Editor
        self.font = QFont()
        self.font.setFamily('Courier New')
        self.font.setFixedPitch(True)
        self.font.setPointSize(10)
        self.ui.code_editor.setFont(self.font)
        self.lexer = PythonLexer(self.ui, self.font)
        self.lexer.lock()

        # Console
        self.interpreter = PythonInterpreter()
        self.ui.console_log.isReadOnly()
        self.ui.console_input.returnPressed.connect(self.send_console_input)
        self.interpreter.output.connect(self.send_console_log)

        # Threads
        self.threadpool = QThreadPool()

    def send_console_input(self):
        command = self.ui.console_input.text()
        self.ui.console_input.clear()
        worker = Worker(self.interpreter.push, str(command))
        worker.signals.result.connect(print)
        worker.signals.finished.connect(self.thread_complete)
        #self.interpreter.push(str(command))

        self.threadpool.start(worker)
        print("Thread Count: ", 
              self.threadpool.activeThreadCount(),
              file=sys.__stdout__)

    def thread_complete(self):
        print("Thread Complete !")

    def send_console_log(self, command):
        print(command, file=sys.__stdout__)
        self.ui.console_log.append(command)


app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())

每次发出来自PythonInterpreter 实例的信号时,它都会调用self.send_console_log,后者接收在解释器上运行的命令的输出。

但是,如果我运行像for i in range(10000):print(i) 这样的大循环作为通过self.send_console_input 发送到解释器的命令,它将在self.send_console_log 中执行打印语句,但不是.append()。它将冻结直到循环完成并且整个内容将附加到QTextEdit

如何解决这个问题?

【问题讨论】:

@ekhumoro 缺少什么?我会添加它。 【参考方案1】:

我不确定,但如果 GUI 是主要部分,它必须在其他线程之前先启动。它可能发生在 GUI 之前开始的一些或一个胎面。看看这些

【讨论】:

GUI 首先启动。 main.py 中的self.ui.showMaximized() 如果数字大于一定数量,您是否尝试过拆分数字 它是一个解释器,所以它应该能够从一个线程中完成它。另外,拆分数字是什么意思? 假设你有 1000 个,这个 500 - 500 个。 添加try-除了附加部分,看看什么是错误。这样更有帮助

以上是关于为啥即使使用 QThreadPool,GUI 也会冻结?的主要内容,如果未能解决你的问题,请参考以下文章

即使我在单独的线程中运行,QT GUI 也会冻结

为啥即使使用单个 reducer 也会调用 Partitioner

为啥即使使用了调用,组合框也会抛出异常

Java:为啥即使路径完整,使用 file.exists() 也会给出错误值?

为啥 Nginx 即使使用 root 用户也会返回 403 Forbidden 错误

为啥即使没有人在监控,zoneminder 监控功能也会持续使用摄像头