如何在显示大量文本时加快滚动响应速度

Posted

技术标签:

【中文标题】如何在显示大量文本时加快滚动响应速度【英文标题】:How to speed up scrolling responsiveness when displaying lots of text 【发布时间】:2016-12-09 17:25:15 【问题描述】:

我正在尝试创建一个 Python 脚本来突出显示 .txt 文件中的特定模式。为此,我修改了一个使用 Tkinter 突出显示给定数据集的脚本。但是,我倾向于让它处理的文件大约是 10000 行,这导致滚动缓慢,因为我认为它渲染了所有内容 - 无论它是否在屏幕上(如果我是,请纠正我错误的)。是否可以更改我的代码,使其以更有效的方式呈现输出?我已经尝试寻找一种方法来做到这一点,但我自己没有找到任何东西。

我的代码如下:

from Tkinter import *

class FullScreenApp(object):
    def __init__(self, master, **kwargs):
        self.master=master
        pad=3
        self._geom='200x200+0+0'
        master.geometry("0x1+0+0".format(
            master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))
        master.bind('<Escape>',self.toggle_geom)            
    def toggle_geom(self,event):
        geom=self.master.winfo_geometry()
        print(geom,self._geom)
        self.master.geometry(self._geom)
        self._geom=geom

root = Tk()
app = FullScreenApp(root)
t = Text(root)
t.pack()

#Import file
with open('data.txt') as f:
    for line in f:
        t.insert(END, line)

#Search terms - Leave blank if not required       
search_term0 = '0xCAFE'
search_term1 = '0x0011'
search_term2 = '0x961E'
search_term3 = '0x0000'
search_term4 = ''

#Assigns highlighted colours for terms not blank
t.tag_config(search_term0, background='red')
if search_term1 != '':
    t.tag_config(search_term1, background='red')
if search_term2 != '':    
    t.tag_config(search_term2, background='red')
if search_term3 != '':
    t.tag_config(search_term3, background='red')
if search_term4 != '':
    t.tag_config(search_term4, background='red')

#Define search
#Requires text widget, the keyword, and a tag
def search(text_widget, keyword, tag):
    pos = '1.0'
    while True:
        idx = text_widget.search(keyword, pos, END)
        if not idx:
            break
        pos = '+c'.format(idx, len(keyword))
        text_widget.tag_add(tag, idx, pos)

#Search for terms that are not blank
search(t, search_term0, search_term0)
if search_term1 != '':
    search(t, search_term1, search_term1)
if search_term2 != '':
    search(t, search_term2, search_term2)
if search_term3 != '':
    search(t, search_term3, search_term3)
if search_term4 != '':
    search(t, search_term4, search_term3)

root.mainloop()

以下链接中给出了文件中的数据示例:here

非常感谢您的宝贵时间,非常感谢。

【问题讨论】:

我已更改问题的标题以反映您想要修复的内容,而不是您认为需要修复的内容。主要是因为我认为情况会相反:我认为渲染所有内容会在第一次显示时变得非常慢,但滚动速度非常快(因为它已经完成了所有渲染)但必须重新渲染每个它滚动的时间会减慢它的速度。 您的代码中的某些缩进被破坏了。 “按住时,它可能会减慢到每秒 2 或 3 行” - 只有您在问题中发布的 exact 代码?另外,文件中的每一行都长吗?我每秒可能会收到 40-50 行。 我怀疑你有一些操作系统设置限制了这一点 - 你可以用鼠标滚轮更快地滚动吗? 我拿了一个quick screencap 我的滚动。我开始用鼠标滚轮滚动,但后来我改为用箭头键滚动。 【参考方案1】:

假设 MCVE 如下:

import tkinter as tk


def create_text(text_len):
    _text = list()
    for _ in range(text_len):
        _text.append("\n".format(_))
    _text = "".join(_text)
    return _text


if __name__ == '__main__':
    root = tk.Tk()
    txt = tk.Text(root)

    txt.text = create_text(10000)
    txt.insert('end', txt.text)

    txt.pack()
    root.mainloop()

分析

基于this 我认为这不是渲染问题。我认为拥有fixed rate of registering &lt;KeyPress&gt; events 是个问题。这意味着每秒注册的事件数量是固定的,即使硬件可能能够以更快的速度注册。类似的规则也应该适用于鼠标滚动事件。


渲染解决方案

也许为txt['height'] 的缓冲区比例分割文本会有所帮助。但这不就是 Tk 应该呈现的方式吗?


渲染无关问题的解决方案

如果一个步骤将被定义为光标移动到上一行或下一行,对于每个注册的 UpDown 事件;然后scrolling_speed = step * event_register_frequency


通过增加步长

一个简单的解决方法是为键绑定的每个注册增加步长,例如增加要跳转的行数。

但是已经有这样的默认行为,假设页面长度> 1行,Page UpPage Down具有页面的步长。这使得滚动速度增加,即使事件注册率保持不变。

或者,可以定义一个具有更大步长的新事件处理程序,为 UpDown 的每次注册调用多个光标移动,例如:

import tkinter as tk


def create_text(text_len):
    _text = list()
    for _ in range(text_len):
        _text.append("\n".format(_))
    _text = "".join(_text)
    return _text


def step(event):
    if txt._step_size != 1:
        _no_of_lines_to_jump = txt._step_size
        if event.keysym == 'Up':
            _no_of_lines_to_jump *= -1

        _position = root.tk.call('tk::TextUpDownLine', txt, _no_of_lines_to_jump)
        root.tk.call('tk::TextSetCursor', txt, _position)
        return "break"

if __name__ == '__main__':
    root = tk.Tk()
    txt = tk.Text(root)

    txt.text = create_text(10000)
    txt.insert('end', txt.text)

    txt._step_size = 12
    txt.bind("<Up>", step)
    txt.bind("<Down>", step)

    txt.pack()
    root.mainloop()

通过模仿按键事件注册表率增加

正如here 中提到的,实际上修改按键注册表速率超出了 Tk 的范围。相反,它可以被模仿:

import tkinter as tk


def create_text(text_len):
    _text = list()
    for _ in range(text_len):
        _text.append("\n".format(_))
    _text = "".join(_text)
    return _text


def step_up(*event):
    _position = root.tk.call('tk::TextUpDownLine', txt, -1)
    root.tk.call('tk::TextSetCursor', txt, _position)

    if txt._repeat_on:
        root.after(txt._repeat_freq, step_up)
    return "break"


def step_down(*event):
    _position = root.tk.call('tk::TextUpDownLine', txt, 1)
    root.tk.call('tk::TextSetCursor', txt, _position)

    if txt._repeat_on:
        root.after(txt._repeat_freq, step_down)
    return "break"


def stop(*event):
    if txt._repeat_on:
        txt._repeat_on = False
        root.after(txt._repeat_freq + 1, stop)
    else:
        txt._repeat_on = True

if __name__ == '__main__':

    root = tk.Tk()

    txt = tk.Text(root)

    txt.text = create_text(10000)
    txt.insert('end', txt.text)
    txt._repeat_freq = 100
    txt._repeat_on = True

    txt.bind("<KeyPress-Up>", step_up)
    txt.bind("<KeyRelease-Up>", stop)
    txt.bind("<KeyPress-Down>", step_down)
    txt.bind("<KeyRelease-Down>", stop)

    txt.pack()
    root.mainloop()

通过增加步长和模仿注册率增加

import tkinter as tk


def create_text(text_len):
    _text = list()
    for _ in range(text_len):
        _text.append("\n".format(_))
    _text = "".join(_text)
    return _text


def step_up(*event):
    _no_of_lines_to_jump = -txt._step_size
    _position = root.tk.call('tk::TextUpDownLine', txt, _no_of_lines_to_jump)
    root.tk.call('tk::TextSetCursor', txt, _position)

    if txt._repeat_on:
        root.after(txt._repeat_freq, step_up)
    return "break"


def step_down(*event):
    _no_of_lines_to_jump = txt._step_size
    _position = root.tk.call('tk::TextUpDownLine', txt, _no_of_lines_to_jump)
    root.tk.call('tk::TextSetCursor', txt, _position)

    if txt._repeat_on:
        root.after(txt._repeat_freq, step_down)
    return "break"


def stop(*event):
    if txt._repeat_on:
        txt._repeat_on = False
        root.after(txt._repeat_freq + 1, stop)
    else:
        txt._repeat_on = True

if __name__ == '__main__':

    root = tk.Tk()

    txt = tk.Text(root)

    txt.text = create_text(10000)
    txt.insert('end', txt.text)
    txt._step_size = 1
    txt._repeat_freq = 100
    txt._repeat_on = True

    txt.bind("<KeyPress-Up>", step_up)
    txt.bind("<KeyRelease-Up>", stop)
    txt.bind("<KeyPress-Down>", step_down)
    txt.bind("<KeyRelease-Down>", stop)

    txt.pack()
    root.mainloop()

【讨论】:

它说我可以在 1 小时内提供赏金【参考方案2】:

所以这可以通过称为多线程的东西来解决。一台计算机可以同时执行多项任务,否则您的网络体验将不一样。这是一个演示多线程的简单函数

from threading import Thread

def execOnDifferentThread(funct=print, params=("hello world",)):
    t = Thread(target=funct, args=params)
    t.start()

现在请注意,这可能不是最好的示例,但现在要并行运行一个函数,您所要做的就是 execOnDifferentThread(funct=A, params=B) 其中 A 是函数名称,B 是 元组 将传递给您的函数的参数。现在,我不想为您编写代码,但是使用此功能,您可以multi-thread 代码的某些部分以使其更快。如果您真的被卡住了,只需评论在哪里并提供帮助。但是请先自己尝试,现在您已经掌握了多线程的能力

【讨论】:

另外,记住在多线程中使用全局变量时,将它们声明为全局变量 我建议线程化代码的t.insertsearch 部分

以上是关于如何在显示大量文本时加快滚动响应速度的主要内容,如果未能解决你的问题,请参考以下文章

使用鼠标滚轮时如何加快 JScrollPane 中的滚动速度?

如何加快 UIScrollView 中的滚动速度? [关闭]

当用户向下滚动时,我可以动态更改网站的滚动速度以加快速度吗?

Bootstrap手风琴,点击时滚动到活动手风琴的顶部,我将如何在嵌套手风琴上实现?

如何使用资产库加快图像加载速度?

如何在 Qt C++ 应用程序中显示具有大量小部件的可滚动列表作为项目?