4.python+QTextbrowser内容显示
本章主要解决的问题:
1、利用QTextbrowser显示文本数据。
2、探究数据转换为str类型对QTextbrowser显示性能的影响。
3、如何显示各种类型的数据。
4、管理多QTextbrowser显示。
5、解决QTextbrowser持续写入数据导致占用内存多导致程序崩溃的问题。
4.1单QTextbrowser显示
QTextbrowser继承自QTextEdit,提供了一个带有超文本导航的富文本浏览器,只能浏览不能编辑。
QTextbrowser通过append的方式将数据显示在控件的尾部,由于程序中可能存在不同的功能类需要显示内容至同一个QTextbrowser,为了方便管理内容的显示,定义一个显示管理类。显示管理类主要的目的是将数据写入和数据显示两个步骤进行解耦,任一需要显示信息的功能类(如界面)只需要将待显示内容写入指定的缓存即可,不需要关心显示的细节。显示管理类定期读取缓存中的内容,append到QTextbrowser中显示,整体显示流程如下图所示。
图1 显示管理类
显示管理类继承自QThread,可以作为线程独立运行,也可以作为普通的类由外部驱动显示。在本程序中,为了减少线程数量,显示管理类由循环监听线程驱动,监听线程会定期检查Queue中是否有数据需要显示。需要注意的是,该方案虽然可以减少一个线程的数量,但是会受到其他任务执行的影响,如果任务执行时间长可能带来显示延迟的问题。
创建显示管理类时,会初始化一个数据缓存,数据缓存是一个Queue,各功能类将数据写入此Queue。由于只能将字符串类型的数据append到QTextbrowser中,所以在显示管理类读取到数据之后,需要对数据进行判断转换,再显示,数据类型转换的方式有如下方式:
1、通过str()的方式直接转换为字符串类型。但是如果数据量大,数据直接转换之后显示到QTextbrowser中可能会出现性能慢、界面卡死的问题,原因未知。
2、针对不同的数据类型进行转换。为了规避直接转换带来的性能问题,需要对不同的数据类型进行更精细的转换,目前显示管理类支持对list、dict和tuple类型的数据进行转换,其他类型的数据暂时采用str()的方式,代码如下所示。
def convert_type_to_str(content=None):
"""将其他类型的数据转为str类型"""
msg_to_text = ""
if isinstance(content, str):
msg_to_text += content + '\n'
elif isinstance(content, list):
msg_to_text += '['
for data in content:
msg_to_text += convert_type_to_str(data).strip() + ', '
msg_to_text = msg_to_text.strip().strip(',')
msg_to_text += ']\n'
elif isinstance(content, dict):
msg_to_text += '{'
for key, value in content.items():
msg_to_text += convert_type_to_str(key).strip() + ': '
msg_to_text += convert_type_to_str(value).strip() + ', '
msg_to_text = msg_to_text.strip().strip(',')
msg_to_text += '}\n'
elif isinstance(content, tuple):
msg_to_text += '('
for arg in content:
msg_to_text += convert_type_to_str(arg).strip() + ', '
msg_to_text = msg_to_text.strip().strip(',')
msg_to_text += ')\n'
else:
msg_to_text += str(content) + '\n'
return msg_to_text
程序采样第二种转换方法,使用方法有两个优点,一是避免了直接转换导致性能慢的问题,二是用户在显示数据时,不需要考虑数据的类型,可以将各种类型的数据直接写入数据缓存。
持续显示数据,QTextbrowser中数据量过大时,可能带来程序性能慢的问题,为了避免此问题,需要定期删除部分显示内容,释放占用的内存。删除内容有两种方案,一是直接删除所有的内容,清空显示,二是先截取部分最新的显示内容,再清空显示,再显示截取的内容,如此则有显示的连续性。
显示管理类提供了设置参数,支持对显示进行配置,配置选项如下图所示:
图2 显示设置
1、使能显示:可以开关显示。
2、显示时间:如勾选,则每次显示内容会附带显示当前时间。
3、读时转换:如勾选,则在显示管理类读取数据时转换数据的类型,否则在写入数据时转换数据的类型。
4、批量显示:如勾选,则显示管理类会读取Queue中所有的数据,再一次性显示,否则读取一次数据显示一次。
5、超限保存:如勾选,则显示的数据如果超过超限长度,则数据只会显示一部分,并且将完整的数据保存到txt中,否则,数据依然只会显示一部分,但是完整数据不会被保存。
6、超限长度:设置数据显示长度的阈值。
超限保存的功能是为了避免单次显示数据长度过长导致的性能问题。
总结针对QTextbrowser显示的性能优化:
1、针对不同数据类型进行转换。
2、定期清理显示内容。
3、单次显示数据超限截断。
4.2多QTextbrowser显示
对于多个子界面均包含各自的QTextbrowser的程序,如果每个子界面各自管理QTextbrowser,存在冗余代码的问题,且不利于外部功能类显示数据。因此需要扩展显示管理类,使其能够统一管理程序内所有的QTextbrowser。
多QTextbrowser和单QTextbrowser显示的差异主要在于显示管理类新增了显示注册表,存储界面及其QTextbrowser的对应关系:{gui_id: QTextbrowser}。显示的流程为:
1、子界面初始化时,如果该界面含有QTextbrowser,则在显示管理类中注册。
2、各功能类将需要显示的数据和期望显示界面的gui_id写入数据缓存。
3、显示管理类读取数据缓存中的数据,查显示注册表找到目标界面的QTextbrowser后进行显示。
显示管理类通过显示注册表管理QTextbrowser的方式的优点在于,为功能类和QTextbrowser搭建了桥梁,功能类可以将数据显示到任一QTextbrowser中,如界面1不仅可以将数据显示到本界面的QTextbrowser,也可以将数据显示到界面2的QTextbrowser中。
图3 多QTextbrowser显示
为了支持QTextbrowser的显示设置功能,子界面在注册时,不仅注册了QTextbrowser,同时也注册了显示设置参数,以便对每个QTextbrowser进行单独的显示设置。
4.3QTextbrowser直显
长期频繁的在QTextbrowser中显示数据,会导致程序占用的内存持续增长,最终导致程序崩溃。且如果界面存在频繁显示数据的需求,通过显示管理类进行显示存在显示效率低的问题。
为解决以上两个问题,开发的QTextbrowser直显功能,其特点为:
1、数据直接显示到QTextbrowser。
2、提升QTextbrowser显示清理的频率。
3、降低单次显示数据的长度。
图4 QTextbrowser直显
如上图所示,后台任务线程将任务过程中产生的数据emit到界面,界面接收到数据之后,调用直显管理类的显示方法。直显管理类在接收到数据之后,并非将所有数据显示到QTextbrowser,而是将数据根据设定的超限长度进行截断后数据显示到QTextbrowser中,而原始数据则按需保存到txt。
直显管理类与显示管理类的差异在于,直显管理类没有数据缓存,在接收到数据之后直接显示到QTextbrowser,因此界面如果有直显需求,均需要创建直显管理类。
直显管理类中会记录显示数据的次数,如果达到设定的次数,则触发清空QTextbrowser的操作,及时释放占用的内存。
通过直显管理类,程序可以及时释放占用的内存,并且提升显示的效率。
4.4总结
本章的主要创新点为:
1、统一管理界面内所有的QTextbrowser。
2、数据写入和显示解耦。
3、显示任意格式的数据。
4、可设置每个QTextbrowser的显示方式。
5、解决QTextBrowser导致界面崩溃的问题:
(1) 直显,不通过统一管理类。
(2) 长度超限数据截断显示,完整数据存为txt。
(3) 频繁清理已显示内容。
4.5程序示例
程序源码地址:https://github.com/AlvinsFish/UiExample
程序部分截图:
图5 程序截图