Возможно ли, чтобы красная текстовая линия появлялась под словами в текстовом виджете Tkinter без использования canvas? (Как для слов с ошибками) - PullRequest
12 голосов
/ 29 апреля 2020

Согласно заголовку вопроса: возможно ли, чтобы под словами в текстовом виджете Tkinter появлялась красная волнистая линия без использования виджета Canvas? (То же самое, что и при слове, написанном с ошибкой)

Я собираюсь что-то вроде этого:

enter image description here

Если да, то где я бы начал?

Ответы [ 2 ]

9 голосов
/ 05 мая 2020

Это просто пример использования пользовательского XBM в качестве bgstipple части текста внутри виджета Text для имитации эффекта волнистой линии:

  • создание изображения XBM Например, squiggly.xbm, как показано ниже:

enter image description here

XBM с 10x20 пикселей

  • , затем вы можно настроить тег в виджете Text, используя указанный выше файл изображения XBM, как bgstipple красным цветом:
# config a tag with squiggly.xbm as bgstipple in red color
textbox.tag_config("squiggly", bgstipple="@squiggly.xbm", background='red')
  • и применить тег к части текста внутри Text widget:
textbox.insert("end", "hello", "squiggly") # add squiggly line

Ниже приведен пример кода:

import tkinter as tk

root = tk.Tk()

textbox = tk.Text(root, width=30, height=10, font=('Courier New',12), spacing1=1)
textbox.pack()

# config a tag with squiggly.xbm as bgstipple in red color
textbox.tag_config("squiggly", bgstipple="@squiggly.xbm", background='red')
textbox.insert("end", "hello", "squiggly") # add squiggly line
textbox.insert("end", " world! ")
textbox.insert("end", "Python", "squiggly") # add squiggly line
textbox.insert("end", "\nthis is second line")

root.mainloop()

И вывод:

enter image description hereenter image description here

Обратите внимание, что высота изображения XBM должна соответствовать размеру шрифта и расстоянию между строками.

8 голосов
/ 02 мая 2020

Я настроил рамку, ее расположение (просто идея, она нуждается в улучшении):

Когда пользователь введет «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


Что необходимо улучшить:

  1. высота строки метки (волнистая линия) должна быть меньше. (Чтобы приблизить волнистую линию и виджет ввода)
  2. На самом деле метка (волнистая линия) может быть изображением. (В моем примере один символ == два символа "~")
  3. функция проверки.
  4. Вы можете добавить две полосы прокрутки.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...