Tkinter. Как реализовать отображение номеров строк у Text
Здрасте! Делаю небольшой редактор кода на Tkinter. Как я могу реализовать отображение номеров строк у Text?
Ответы (1 шт):
Автор решения: GrAnd
→ Ссылка
Слева вставляется ещё один узенький редактор, в котором пишутся номера строк. Номера строк обновляются динамически по мере изменения количества строк в основном редакторе, и синхронизируется скролл. Я такое реализовывал ещё в прошлом веке на чистом Tk (который на Tcl.)
Вот, нашёл у себя тот самый проект, вырезал имплементацию редактора и быстренько сконвертировал из Tcl на Python.
import tkinter as tk
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._orig = str(self) + "_orig"
self.tk.call("rename", str(self), self._orig)
self.tk.createcommand(str(self), self._proxy)
def _proxy(self, command, *args):
cmd = (self._orig, command) + args
result = self.tk.call(cmd)
if command in ("insert", "delete", "replace"):
self.event_generate("<<TextModified>>")
return result
class Editor:
def __init__ (self, parent):
self._in_sync = False
tmp = tk.Label()
fg_color = tmp.cget("foreground")
bg_color = tmp.cget("background")
tmp.destroy()
self.w = tk.Frame(parent)
self.w.pack(fill=tk.BOTH, expand=True)
self.hs = tk.Scrollbar(self.w, orient=tk.HORIZONTAL)
self.vs = tk.Scrollbar(self.w)
self.ruler = tk.Text(self.w, bg=bg_color, fg=fg_color, wrap=tk.NONE,
relief=tk.FLAT, width=5, cursor="arrow",
selectborderwidth=0, exportselection=0)
self.editor = CustomText(self.w, wrap=tk.NONE, exportselection=1,
insertwidth=2, xscrollcommand=self.hs.set)
self.editor.bind("<<TextModified>>", self.rulersync)
self.hs.configure(command=self.editor.xview)
self.vs.configure(command=self.scrollsync)
self.ruler.configure (yscrollcommand=lambda *args: self.edsync(self.editor, self.ruler, *args))
self.editor.configure(yscrollcommand=lambda *args: self.edsync(self.ruler, self.editor, *args))
self.ruler.tag_configure("right", justify=tk.RIGHT)
self.ruler.insert(tk.END, "1", "right")
self.ruler.configure(state=tk.DISABLED)
self.w.grid_rowconfigure(0, weight=1, minsize=0)
self.w.grid_columnconfigure(1, weight=1, minsize=0)
self.editor.grid(padx=2, pady=0, row=0, column=1, sticky="news")
self.ruler.grid (padx=2, pady=0, row=0, column=0, sticky="nws" )
self.vs.grid (padx=1, pady=0, row=0, column=2, sticky="news")
self.hs.grid (padx=0, pady=1, row=1, column=1, sticky="news")
#-------------------------------------------------------------------------------
# Description : Scroll two widgets by one scrollbar
# : (for scrollbar -command)
#-------------------------------------------------------------------------------
def scrollsync(self, *args):
self.editor.yview(*args)
self.ruler.yview_moveto(self.editor.yview()[0])
#-------------------------------------------------------------------------------
# Description : Set position of scrollbar
# : (for text -yscrollcommand)
# Paremeters : sw - source widgets
# : tw - target widget
#-------------------------------------------------------------------------------
def edsync(self, tw, sw, *args):
if not self._in_sync:
self._in_sync = True
tw.yview_moveto(sw.yview()[0])
self.w.update_idletasks()
self.vs.set(*args)
self._in_sync = False
#-------------------------------------------------------------------------------
# Description : Updates line numbers text in ruler
# : to match actual line numbers in the editor
#-------------------------------------------------------------------------------
def rulersync(self, *args):
elines = int(self.editor.index(tk.END).split(".")[0]) - 1
rlines = int(self.ruler.index(tk.END).split(".")[0]) - 1
if rlines != elines:
self.ruler.configure(state=tk.NORMAL)
if rlines < elines:
for i in range(rlines+1, elines+1):
self.ruler.insert(tk.END, f"\n{i}", "right")
else:
self.ruler.delete(f"{elines+1}.0 -1chars", tk.END)
self.ruler.configure(state=tk.DISABLED)
self.ruler.yview_moveto(self.editor.yview()[0])
self.w.update_idletasks()
root = tk.Tk()
ed = Editor(root)
ed.editor.insert(tk.END, "line " + "\nline ".join(map(str, range(1,100))))
root.mainloop()
