I am trying to create a Python script to highlight specific patterns in a .txt file. To do this, I have altered a script which used Tkinter to highlight a given set of data. However, the files I tend to get it to process are around 10000 lines, which results in slow scrolling as I think it renders everything - whether it is on the screen or not (correct me if I'm wrong). Is it possible to alter my code such that it renders the output in a more efficient way? I have tried searching for a means to do this, but have not found anything myself.
My code is as follows:
from Tkinter import *
class FullScreenApp(object):
def __init__(self, master, **kwargs):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry("{0}x{1}+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()
An example of the data in a file is given in the following link: here
Many thanks for your time, it is really appreciated.
Assuming MCVE is the following:
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()
Based on this I don't think it is a rendering issue. I think it's an issue with having a fixed rate of registering <KeyPress>
events. Meaning that the number of events registered per second is fixed, even though the hardware may be capable of registering at a faster rate. A similar regulation should be true also for the mouse-scroll event.
Perhaps slicing text for a buffer proportion of txt['height']
would help. But isn't that how Tk supposed to be rendering anyway?
If a step would be defined as the cursor's movement to the previous or the next line, for every registered event of Up or Down; then scrolling_speed = step * event_register_frequency
.
By increasing the step size
An easy workaround would be to simply increase the step size, as in increasing the number of lines to jump, for each registration of the key bind.
But there's already such default behavior, assuming the page length > 1 line, Page Up or Page Down has a step size of a page. Which makes the scrolling speed increase, even though the event registration rate remains the same.
Alternatively, a new event handler with a greater step size may be defined to call multiple cursor movements for each registration of Up and Down, such as:
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()
By mimicking keypress event registry rate increase:
As mentioned in here actually modifying keypress registry rate is out of scope of Tk. Instead, it can be mimicked:
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()
By both increasing step-size and mimicking registry rate increase
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With