This is a little replica of my app where I've numerous text widget create with a for loop; I've also create a search entry and option to highlight the context inside the text widget to make it easier to find the text that one is looking for. I was wondering if there is a way to also automatically move the scrollbar to the searched text: so, if I'm at beginning of my app (where the number 0 is) and search for a number at the bottom (e.g, 81) the 81 number would be highlighted and the frame go down by itself to show me the number. This is my code right now:
class App(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.entry = tk.Entry(self, justify="center", width=6)
self.entry.pack(padx=(15,0), ipady=5, side="top", anchor="e")
button = tk.Button(self, text="Search", width=13, command=self.search)
button.pack(side="top", anchor="e")
self.frame = tk.Frame(self)
self.frame.pack(fill="both", expand="true", side="bottom")
self.canvas = tk.Canvas(self.frame, highlightthickness=0)
self.canvas.pack(side="left", fill="both", expand="true")
self.canvas.bind_all("<MouseWheel>", self.on_mousewheel)
self.pop()
def on_mousewheel(self, event):
shift = (event.state & 0x1) != 0
scroll = -1 if event.delta > 0 else 1
if shift:
self.canvas.xview_scroll(scroll, "units")
else:
self.canvas.yview_scroll(scroll, "units")
def pop(self):
self.list = list(range(101))
self.loop = []
self.scrollbar = ttk.Scrollbar(self.frame, orient="vertical", command=self.canvas.yview)
self.scrollbar.pack(side="right", fill="y")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.bind('<Configure>', lambda e: self.canvas.configure(scrollregion = self.canvas.bbox("all")))
self.list_frame = tk.Frame(self.canvas)
self.canvas.create_window((0,0), window=self.list_frame, anchor="nw")
for index, number in enumerate(self.list):
self.frame_2 = tk.Frame(self.list_frame)
self.frame_2.pack(pady=(15,0), fill="x", expand="yes", side="top")
self.text = tk.Text(self.frame_2, wrap="word", height=1)
self.text.pack(padx=15, side="left")
self.text.tag_configure("normal")
self.text.insert("end", number, "normal")
self.text.config(state="disabled")
self.text.tag_configure("select", foreground="white", background="green")
self.canc_button = tk.Button(self.frame_2, text="Cancel", command=lambda x=index: self.delete(x))
self.canc_button.pack(side="right")
self.loop.append((self.text, self.canc_button))
def search(self):
self.found = self.entry.get()
for self.text in self.loop:
if self.found:
self.text[0].tag_remove("select", 1.0,"end-1c")
idx = '1.0'
while 1:
idx = self.text[0].search(self.found, idx, nocase=1, stopindex="end")
if not idx: break
lastidx = f"{idx} {int(len(self.found))}c"
self.text[0].tag_add("select", idx, lastidx)
idx = lastidx
if __name__ == "__main__":
root = tk.Tk()
App(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Thank you!
CodePudding user response:
This worked for me:
There are comments where I changed something, explaining what the added code does
class App(tk.Frame):
# (rest of code)
def search(self):
self.found = self.entry.get()
# variable to store index of Label with found text
scrollto = None
for i, self.text in enumerate(self.loop):
if self.found:
self.text[0].tag_remove("select", 1.0, "end-1c")
idx = '1.0'
while 1:
idx = self.text[0].search(self.found, idx, nocase=1, stopindex="end")
if not idx: break
# if not already found text, set index
if scrollto is None:
scrollto = i
lastidx = f"{idx} {int(len(self.found))}c"
self.text[0].tag_add("select", idx, lastidx)
idx = lastidx
# if found any result, scroll to it
if scrollto is not None:
fraction = scrollto/len(self.loop)
self.canvas.yview(tk.MOVETO, fraction)