用 QThread 改变 QTextBrowser

Posted

技术标签:

【中文标题】用 QThread 改变 QTextBrowser【英文标题】:Changing QTextBrowser with QThread 【发布时间】:2020-11-01 18:33:13 【问题描述】:

我正在尝试使用 QTextBrowser 显示一些数据。但是它冻结了,因为我没有以正确的方式使用线程。我在 *** 上搜索了一些 QThread 问题,但无法弄清楚为什么我的代码不起作用...

我想打印五次“Hello”,每个进程休眠 1 秒

我也想通过 QLineEdit 获得输入来设置 time.sleep(cooldown),但我可以在解决我的 Qthread 问题后进行。

我搜索并尝试对我的代码实施的解决方案是;

PyQt5: Update labels inrun time

PyQt5: Updating Label?

可能是我使用了错误的实现方法。但是我是新手,请帮帮我

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'denn.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets
from time import *

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        MainWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.lineEdit.setFont(font)
        self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.textBrowser.setFont(font)
        self.textBrowser.setObjectName("textBrowser")
        self.verticalLayout.addWidget(self.textBrowser)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.pushButton.setFont(font)
        self.pushButton.setStyleSheet("QPushButton \n"
"    color: white;\n"
"    border: 2px solid #46963f;\n"
"    border-radius: 20px;\n"
"    border-style: outset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #46963f\n"
"        );*/\n"
"    background:#46963f;\n"
"    padding: 5px;\n"
"    \n"
"\n"
"QPushButton:hover \n"
"    border: 2px solid #07121b;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"    \n"
"\n"
"QPushButton:pressed \n"
"    border: 2px solid #07121b;\n"
"    border-style: inset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.4, cy: -0.1, fx: 0.4, fy: -0.1,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"\n"
"    ")
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Deneme Programı"))
        self.lineEdit.setPlaceholderText(_translate("MainWindow", "Bekleme Süresi"))
        self.pushButton.setText(_translate("MainWindow", "Başlat"))

        try:
            self.pushButton.clicked.connect(self.x)
        except Exception as e:
            print(e)

    def x(self):
        cd = int(self.lineEdit.text())
        for i in range(5):
            self.thread = DummyThread(self, cd)
            self.thread.start()
            self.thread.finished.connect(self.a)

    def a(self):
        self.textBrowser.setText(self.textBrowser.toPlainText() + "\nHello")

class DummyThread(QThread):
    finished = pyqtSignal()
    def __init__(self,cd):
        super(DummyThread, self).__init__()
        self.cd = cd
    def run(self):
        time.sleep(self.cd)
        self.finished.emit()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

DummyThread 不接受任何参数,创建实例时删除self。 既然你from time import *,你需要从time.sleep(1) 中删除time.。 这将产生以下代码:

    def x(self):
        self.thread = DummyThread()
        self.thread.start()
        self.thread.finished.connect(self.a)
    def a(self):
        self.textBrowser.setText(self.textBrowser.toPlainText() + "\nHello")

class DummyThread(QThread):
    finished = pyqtSignal()
    def run(self):
        sleep(1)
        self.finished.emit()

附言正如文件介绍所暗示的,不建议在 PyUIC 生成的文件中添加代码。

【讨论】:

【参考方案2】:

您的代码失败的原因是它不是从 QObject 继承的。 要使 QThreads 工作,父级必须继承自 QObject 或派生类而不是 object

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'denn.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets
import time


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        MainWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.lineEdit.setFont(font)
        self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.textBrowser.setFont(font)
        self.textBrowser.setObjectName("textBrowser")
        self.verticalLayout.addWidget(self.textBrowser)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.pushButton.setFont(font)
        self.pushButton.setStyleSheet("QPushButton \n"
"    color: white;\n"
"    border: 2px solid #46963f;\n"
"    border-radius: 20px;\n"
"    border-style: outset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #46963f\n"
"        );*/\n"
"    background:#46963f;\n"
"    padding: 5px;\n"
"    \n"
"\n"
"QPushButton:hover \n"
"    border: 2px solid #07121b;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"    \n"
"\n"
"QPushButton:pressed \n"
"    border: 2px solid #07121b;\n"
"    border-style: inset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.4, cy: -0.1, fx: 0.4, fy: -0.1,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"\n"
"    ")
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Deneme Programı"))
        self.lineEdit.setPlaceholderText(_translate("MainWindow", "Bekleme Süresi"))
        self.pushButton.setText(_translate("MainWindow", "Başlat"))

        

class Window(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.x)
    def x(self):
        cd = int(self.ui.lineEdit.text())
        for i in range(5):
            self.thread = DummyThread(self, cd)
            self.thread.finished.connect(self.a)
            self.thread.start()

    def a(self):
        self.ui.textBrowser.setText(self.ui.textBrowser.toPlainText() + "\nHello")

class DummyThread(QThread):
    finished = pyqtSignal()
    def __init__(self, parent, cd):
        super(DummyThread, self).__init__(parent)
        self.cd = cd

    def run(self):
        time.sleep(self.cd)
        self.finished.emit()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

【讨论】:

你好,它没有给我任何错误,而且每次睡眠后它也没有打印你好。它在过程完成时打印。我想在睡觉后看到每一张照片 我没有改变你的逻辑。如果你给它数字 1,它将启动 5 个线程并在更新 gui 之前休眠 1 秒。所有这些线程都将并发运行(在睡眠时)。【参考方案3】:

您的代码中有 3 个问题。

    您在线程构造函数中使用了 2 个参数 (self, cd),而 DummyThread 的 __init__ 只接受一个。请注意,即使您在 init 中添加 parent 参数,它也不起作用,因为 QThread(作为所有 QObjects)仅接受 QObject 实例作为父级,而该范围内的 selfUi_MainWindow 实例(这是一个简单的 python object。 你使用了from time import *,所以你不能使用time.sleep,而是使用sleep。 您不断地覆盖self.thread 实例,这意味着每次您尝试使用相同的引用创建一个新实例时,前一个实例将立即被垃圾回收,从而导致线程被销毁时崩溃虽然它仍在处理中。请注意,即使您只创建一个实例并尝试在前一个实例仍处于活动状态时再次运行x,或者如果您不使用线程实例的持久引用(因为它将函数返回后立即销毁)。

因此,为了解决您的问题,您需要进行以下修改:

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # ...
        self.threads = []

    def x(self):
        cd = int(self.lineEdit.text())
        for i in range(5):
            thread = DummyThread(cd)
            thread.start()
            thread.finished.connect(self.a)
            self.threads.append(thread)

请注意,如果您需要同时多次运行相同的处理,您可以使用 QRunnable 代替。 另请注意,正如在另一个答案中已经指出的那样,在文件开头和其他数百个问题中,您应该切勿修改 pyuic 创建的文件(也不要尝试模仿它们的行为),并且造成这种情况的众多原因之一是它可能导致对对象结构的混淆(就像您的情况一样)。我建议您阅读有关using Designer 的官方指南中有关使用这些文件的正确方法的更多信息。

【讨论】:

以上是关于用 QThread 改变 QTextBrowser的主要内容,如果未能解决你的问题,请参考以下文章

如何控制QTextBrowser水平滚动条

如何为 QTextBrowser 中的对象添加工具提示?

QTextBrowser / C++ 附加可点击文本

从 QTextBrowser 打开文件

如何使 QTextBrowser 具有最大的尺寸?

QTextBrowser 还是 QWebView?