python-3.x - PyQt5:如何让 QTextEdit 的内容在选项卡更改时刷新?
问题描述
[为清楚起见重新提出问题]
我有一个带有 2 个选项卡的简单 GUI,第一个包含用于程序日志的只读 QTextEdit,第二个包含用于用户输入的各种小部件和一个“生成”按钮。
GUI 是使用 Qt Designer 制作的。注意:除了只读的 QTextEdit 之外,所有元素都具有默认属性(它们未编辑/未更改选项)
箭头显示用于日志的 QTextEdit 和导致选项卡更改的生成按钮
单击生成按钮后,焦点选项卡将切换到第一个(带有日志),并且使用单独的线程来计算用户数据并将其日志写入 QTextEdit
class GUI:
def __init__(self, core):
self.core = core
self.app = QtWidgets.QApplication(sys.argv)
self.window = Application()
self.__link_buttons__()
def __link_buttons__(self):
generate_button = self.window.get_generate_button()
if generate_button is None:
raise RuntimeError("could not find the generation button needed for the gui ( possibly an issue with the .gui file ), cannot start software")
generate_button.clicked.connect(self.__generate_threaded)
def __generate_threaded(self):
logger.logger_instance.log("user requested generation", LogLevel.NORMAL)
self.window.set_main_tab_to_log()
if self.core.check_if_generating() is True:
logger.logger_instance.log("Generator is currently in use", LogLevel.ERROR)
return
thread = threading.Thread(target=self.core.generate_gui)
thread.start()
class Application(QtWidgets.QMainWindow):
def __init__(self):
super(Application, self).__init__()
self.ui = uic.loadUi(settings.settings_instance.gui_path / "main_window.ui", self)
self.show()
self.__tabs = self.ui.findChild(QTabWidget, "mainTabs")
self.__log_tab = self.ui.findChild(QWidget, "logTab")
def get_gui_handle(self):
return self.ui.findChild(QTextEdit, "LogOutput")
def get_generate_button(self):
return self.ui.findChild(QPushButton, "generateButton")
def set_main_tab_to_log(self):
self.__tabs.setCurrentWidget(self.__log_tab)
logger 是负责在 GUI 上写入 QTextEdit 的类,它调用get_gui_handle()
start 上的方法来获取 QTextEdit 然后用于append()
写入它(具有线程保护)
可能重要的细节:我使用的是标准 Python 线程 ( import threading
),而不是 Qt,因为其他软件都使用它们,我不确定它们是否可以混合使用
记录器确实成功写入 QTextEdit,但应用程序未按预期显示文本。任何新文本都会正常显示,但以前的日志不会,并且只会在窗口调整大小/单击文本/更改选项卡/...时显示(我认为是让应用程序重新呈现 QTextEdit 的事件)
图片为清楚起见:
第一个日志按预期显示:
第二个日志也按预期显示,但第一个日志现在不可见
窗口调整大小/文本覆盖/选项卡更改后再次显示第一个日志
解决方案
问题是,即使我使用了线程保护,我还是从一个单独的线程调用该append()
方法(线程保护还不够!)。在来自不同线程的调用后尝试刷新/实现 gui 也是一个坏主意。
这是因为 Qt 管理内部事件并且不使用主线程似乎绕过它们反过来导致 Qt 看不到它的内容已更改。
解决方案的重要说明:在这种特定情况下,使用线程或 qt 线程是无关紧要的,因为 qt 只是包装了一个线程库。然而,强烈建议在所有软件上使用 qt 线程,以避免在同一程序中使用 2 个不同的线程库,这反过来又会导致问题。
我使用pyqtSignal
是为了能够与 gui 正确通信而不会出现问题
from PyQt5.QtCore import QObject, pyqtSignal
class GUI(QObject):
__gui_log_signal = pyqtSignal(str)
def __init___():
# ...
self.__gui_handle = self.window.get_gui_handle() # this method returns the reference to the QTextEdit directly
self.__gui_log_signal.connect(self.__gui_handle.append)
这样我可以self.__gui_log_signal.emit("string or variable here")
在需要时使用
需要注意的重要细节:(首先使获得答案比预期更难的事情)
pyqtSygnal
必须在类的根目录中,而不是在else中__init__()
它不能正常工作(在我的实例中emit()
找不到方法)- 可
pyqtSignal
用于传递变量,它们只需要这样声明(例如:)pqtSygnal(str)
,然后连接到使用相同类型/变量计数的方法 - 使用
pyqtSygnal
必须扩展QObject
的类或扩展它的类
推荐阅读
- angular - 查询参数 Angular 5 路由错误
- arrays - ValueError:使用序列设置数组元素以将 word2vec 模型合并到我的 pandas 数据框中
- c# - 基于其他值的动态列值,例如在 Excel 中
- c# - MongoDB UpdateOne 没有更新记录
- xamarin - Xamarin Forms Media Plugin - 拍照返回屏幕弹出到根目录
- algorithm - 分析递归算法
- java - Java Hibernate 无效映射异常
- amazon-web-services - cloud-init cc_mounts.py 忽略 AWS EFS 挂载
- vba - Excel 错误 1004 用于特殊粘贴
- php - Twig 2 数组和 For 循环