Я настроил рамку, ее расположение (просто идея, она нуждается в улучшении):
![](https://i.stack.imgur.com/YJdSz.png)
Когда пользователь введет «Enter», он будет создать новую запись и новую метку (виджет волнистой линии).
Если пользователь введет «Backspace» и эта запись будет нулевой, он удалит и эту запись, и метку (виджет волнистой линии).
Когда пользователь вводит стрелку «Вверх», виджет предыдущего входа будет сфокусирован.
....
Код:
import tkinter
class CustomText(tkinter.Frame):
def __init__(self,master):
super(CustomText,self).__init__()
self.last_line = 0
self.index_line = 0
self.master = master
self['background'] = 'white'
self.check_func = self.master.register(self.check)
first_line = tkinter.Entry(self,font=("",16),relief="flat",validate="key",validatecommand=(self.check_func,'%W','%P'))
first_line.pack(fill="x")
first_underline = tkinter.Label(self,background="white",fg="red",font=("",4))
first_underline.pack(anchor="nw")
self.widget_dict = {
first_line:first_underline # a dict which save the squiggly line widget(as a value) and entry widget(as a key)
}
# bind event:
first_line.bind("<Return>",self.create_new_line)
first_line.bind("<Up>",self.to_previous_line)
first_line.bind("<Down>",self.to_next_line)
first_line.bind("<FocusIn>",self.focused)
def focused(self,event): # when one entry widget is focused,change the index_line number
self.index_line = list(self.widget_dict.keys()).index(event.widget)
def create_new_line(self,event): # when user input enter,generate an entry and a label
self.index_line += 1
self.last_line += 1
new_line = tkinter.Entry(self,font=("",14),relief="flat",validate="key",validatecommand=(self.check_func,'%W','%P'))
new_line.pack(fill='x')
new_underline = tkinter.Label(self, background="white", fg="red", font=("", 4))
new_underline.pack(anchor="nw")
# also bind an event
new_line.bind("<Return>", self.create_new_line)
new_line.bind("<Up>",self.to_previous_line)
new_line.bind("<Down>",self.to_next_line)
new_line.bind("<FocusIn>",self.focused)
# the difference between the first line:when user delete all the words in this widget and he input "backspace" again, it will delete the entry and label widget,
new_line.bind("<BackSpace>",self.delete_this_line)
new_line.focus_set()
self.widget_dict[new_line] = new_underline
def to_next_line(self,event): # when user type "Down",go to the previous line
if self.index_line != self.last_line:
self.index_line += 1
to_widget = tuple(self.widget_dict.keys())[self.index_line]
to_widget.focus_set()
if event: # to the same index of next entry widget.
to_widget.icursor(event.widget.index("insert"))
def to_previous_line(self,event): # when user type "Up",go to the previous line
if self.index_line:
self.index_line -= 1 # the number of index minus 1
to_widget = tuple(self.widget_dict.keys())[self.index_line]
to_widget.focus_set()
if event:
to_widget.icursor(event.widget.index("insert"))
def delete_this_line(self,event):
if not event.widget.get():
self.last_line -= 1
self.widget_dict[event.widget].destroy() # delete it in visual
del self.widget_dict[event.widget] # delete reference in the self.widget_dict
event.widget.destroy()
del event.widget
self.to_previous_line(None)
def check(self,widget_str,input_str): # this is an error-check function
widget = self.nametowidget(widget_str) # convert the widgetname to a widget object
# an example
error_str = "abc"
if input_str == error_str: # now is to check the grammar
underline_widget = self.widget_dict[widget]
underline_widget['text'] = "〜"*len(error_str)*2 # add a squiggly line visually
return True
root = tkinter.Tk()
t = CustomText(root)
t.pack()
root.mainloop()
Пример изображения (показать волнистая линия, когда пользователь вводит "ab c"):
![enter image description here](https://i.stack.imgur.com/fxYqL.png)
Что необходимо улучшить:
- высота строки метки (волнистая линия) должна быть меньше. (Чтобы приблизить волнистую линию и виджет ввода)
- На самом деле метка (волнистая линия) может быть изображением. (В моем примере один символ == два символа "~")
- функция проверки.
- Вы можете добавить две полосы прокрутки.