是否可以在不使用画布的情况下在 Tkinter 文本小部件中的单词下方显示红色波浪线?(比如拼错单词)

Jad*_*win 18 python user-interface tkinter

根据问题标题:是否可以在不使用画布小部件的情况下在 Tkinter 文本小部件中的单词下方出现红色波浪线?(与拼错单词时相同的波浪线)

我要做这样的事情:

在此处输入图片说明

如果是这样,我会从哪里开始?

acw*_*668 16

这只是使用用户定义的 XBM 作为小部件bgstipple内文本的一部分Text来模拟波浪线效果的示例:

  • 创建一个 XBM 图像,例如squiggly.xbm,如下所示:

在此处输入图片说明

一个 10x20 像素的 XBM

  • 然后你可以Text使用上面的 XBM 图像文件在小部件中配置一个标记,如bgstipple红色:
# config a tag with squiggly.xbm as bgstipple in red color
textbox.tag_config("squiggly", bgstipple="@squiggly.xbm", background='red')
Run Code Online (Sandbox Code Playgroud)
  • 并将标签应用于Text小部件内的文本部分:
textbox.insert("end", "hello", "squiggly") # add squiggly line
Run Code Online (Sandbox Code Playgroud)

下面是一个示例代码:

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()
Run Code Online (Sandbox Code Playgroud)

和输出:

在此处输入图片说明在此处输入图片说明

请注意,XBM 图像的高度需要与字体大小和行间距相匹配。


jiz*_*AMA 8

我定制了一个框架,它的布局(只是一个想法,需要改进):

当用户输入“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()
Run Code Online (Sandbox Code Playgroud)

示例图像(当用户输入“abc”时显示波浪线):

在此处输入图片说明


需要改进的地方:

  1. 标签(波浪线)的行高应该更小。(使波浪线和条目小部件更接近)
  2. 实际上,标签(波浪线)可以是图像。(在我的示例中,一个字符 == 两个“~”字符)
  3. 检查的功能。
  4. 你可以添加两个滚动条。