在Tkinter Textarea中显示空白/不可打印的字符(可能是通过字体)?

时间:2014-11-12 20:24:51

标签: python fonts tkinter whitespace

程序SciTE(它的Scintilla组件,而不是)具有非常好的功能:您可以启用View / Whitespace和View / End of Line,并获得这样的显示:

test.png

正如你所看到的,有点作为空格,标签的箭头(geany也是一样的,因为它也使用了Scintilla组件) - 而且我想要有相同的在Tkinter textarea中显示。

我尝试查看Scintilla的src/Editor.cxx,因为它有一个vs.viewWhitespace变量,但我无法看到代码实际处理这种情况的位置(以及如何)。

通过问题How to show/reveal hidden or invisible characters in NetBeans?,我发现Netbeans,至少在某个时间,可能使用了I font whitespace.ttf。如果您下载并安装它,您将看到它只有一个空格点,并且没有定义其他字形。

所以我在我的Ubuntu 11.04上尝试了这个,使用Python 2.7:

#!/usr/bin/env python
# -*- coding: utf-8 -*- # must specify, else 2.7 chokes even on Unicode in comments

import sys, os

import Tkinter as tk
import tkMessageBox as tkMsgBox
import ttk

DEFAULT_TEXT_INP = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer eu leo leo. Donec felis urna, rhoncus nec ullamcorper bibendum, dapibus ac urna.
  Etiam ut mauris non tellus tristique dictum in ac est. Phasellus feugiat maximus nulla. Donec varius tortor nec orci posuere porttitor.
    Suspendisse rhoncus condimentum bibendum. Cras euismod blandit massa, at ullamcorper ligula malesuada sit amet. Sed sit amet metus arcu.
Aenean quis luctus tellus.
Aliquam ac sem enim.
"""

class GuiContainer:
  def __init__(self):
    self.root = None
    self.frame = None
  def initBuildWindow(self):
    self.root = tk.Tk()
    self.root.geometry("650x450+50+50")
    self.root.title("{0} GUI".format(sys.argv[0]))
    self.frame = tk.Frame(self.root, name="mframe")
    self.frame.pack(fill=tk.BOTH, expand=1)
    self.tainput = tk.Text(self.frame, wrap=tk.NONE, bd=0, height=17,
                      undo=True, name="tainput"
                      )
    self.tainput.pack(fill=tk.BOTH, expand=1)
    self.tainput.config(font="whitespace 11")
    self.tainput.insert(tk.INSERT, DEFAULT_TEXT_INP)
    self.tainput.config(wrap=tk.WORD)

def main():
  guiCO = GuiContainer()
  guiCO.initBuildWindow()
  guiCO.root.mainloop()

if __name__ == "__main__":
  main()

......得到了:

test2.png

显然,因为"空白"字体没有任何其他字形,系统"通过"默认为任何字体,并从那里填写其他字形。

所以我的问题是:

  • 是否有适当的"在Tkinter的TextArea中显示不可打印/空白字符的方法?或者,如果没有,任何解决方法?
  • 如果没有,是否有适合Linux的等宽字体 - 还包括用于空格的cdots和用于标签的箭头(以及可能用于LF的字符) - 我可以用它来模拟那些"显示空白& #34;显示?

3 个答案:

答案 0 :(得分:1)

据我所知,tcl / tk没有内置方法来切换空格,制表符和/或换行符的显示。在大多数情况下,标记最重要的是为标签字符添加空间。这对于处理比例字体的文本小部件(如tk.Text)来说也是最难的。标签停靠位于像素位置,而不是“#”字符'位置。背景颜色甚至可以应用于像素宽的空间。没有比像素宽的字符。

答案 1 :(得分:0)

我最近碰巧执行了这个。您可以通过处理空格输入的键事件来替换窗口小部件中的空格。

proc ConvertWhitespace {data showwhitespace} {
    if {$showwhitespace} {
        set data [string map {\u0020 \u00b7 \t \u00bb\t} $data]
    } else {
        set data [string map {\u00b7 \u0020 \u00bb\t \t} $data]
    }
    return $data
}

proc OnKeyWhitespace {textwidget char} {
    global showwhitespace
    if {$showwhitespace} {
        switch -exact -- $char {
            \u0020 {set char \u00b7}
            \u0009 {set char \u00bb\t}
        }
    }
    tk::TextInsert $textwidget $char
}

proc OnShowWhitespace {textwidget app} {
    global showwhitespace
    set data [$textwidget get 1.0 "end - 1c"]
    $textwidget replace 1.0 end [ConvertWhitespace $data $showwhitespace]
}

bind $textwidget <Key-space> {OnKeyWhitespace %W %A; break}
bind $textwidget <Key-Tab> {OnKeyWhitespace %W %A; break}

要使用此功能,请在应用程序中包含一个普通的text窗口小部件,并使用最后两行绑定Key-spaceKey-Tab以调用特殊输入过程。这会插入空格的中心点和制表符的箭头字形以及实际的制表符,以使间距正确。 OnShowWhitespace可以连接到一个checkbutton菜单项或一些其他事件来切换空白字符的显示。在加载数据或保存到文件或从文件保存时,如果显示空白字符,则需要通过ConvertWhitespace过程传递来自窗口小部件的文本来修复它。

我在Tcl / Tk中做了这个,所以这个例子在Tcl中,但它也与Tkinter一起使用。

答案 2 :(得分:0)

我将上面的Tcl脚本移植到Python 3.6及其工作正常,除了空格的替换字符(0xb7)阻止自动换行。我尝试添加一个零宽度的空格字符(在注释掉的行中为0x200b),但tkinker并没有把它当作一个简单的中断字符。

    # Display invisible characters space, tab and newline

    text = "\tMary had a little lamb\n\tWhose fleece was white as snow.\n\n"\
    "\nCommands:\t\tCtrl-w toggles the display.\n"\
    "\t\tCtrl-q quits the programme.\n\n"\
    "I ported the above Tcl script to Python 3.6 and its work all right, except that the substitution character for space (0xb7) prevents word wrapping.\n"\
    "I tried adding a zero-width space character (ZWSP - 0x200b in the commented-out lines), but tkinker doesn't treat it as an easy break character."

    from tkinter import *

    class GuiContainer:
        def __init__(self):
            self.root = None
            self.frame = None
            self.showinvis = False
        def initBuildWindow(self):
            self.root = Tk()
            self.root.geometry("650x450+50+50")
            self.root.title("{0} GUI".format(sys.argv[0]))
            self.frame = Frame(self.root, name="mframe")
            self.frame.pack(fill=BOTH, expand=1)
            self.tainput = Text(self.frame, wrap=WORD, bd=0, height=17, undo=True)
            self.tainput.pack(fill=BOTH, expand=1)
            self.tainput.insert(INSERT, text)
            self.tainput.bind("<Key-space>",lambda event: self.onKeyWhitechar(self.tainput, ' '))
            self.tainput.bind("<Key-Tab>", lambda event: self.onKeyWhitechar(self.tainput,'\t'))
            self.tainput.bind("<Return>", lambda event: self.onKeyWhitechar(self.tainput,'\n'))
            self.tainput.bind("<Control-w>", lambda event: self.onShowInvisible())
            self.tainput.bind("<Control-q>", lambda event: self.root.quit())
            self.tainput.focus_force()
            self.tainput.mark_set(INSERT, END)

        def onKeyWhitechar(self, char, event=None):
            convstr = ''
            if self.showinvis:
                if char == ' ':      convstr = '·'      # 0x20 -> 0xb7
                #if char == ' ':      convstr = '·'+chr(0x200b)
                elif char == '\t':   convstr = '»\t'
                elif char == '\n':   convstr = '¶\n'
                self.tainput.insert(INSERT, convstr)
        def convertWhitechars(self):
            if self.showinvis:
                convlst = [[' ','·'], ['\t','»\t'], ['\n','¶\n']]
                #convlst = [[' ','·'+chr(0x200b)], ['\t','»\t'], ['\n','¶\n']]
            else:
                convlst = [['·',' '], ['»\t','\t'], ['¶\n','\n']]
                #convlst = [['·'+chr(0x200b),' '], ['»\t','\t'], ['¶\n','\n']]
            for i in range(len(convlst)):
                res = True
                char = convlst[i][0]; subchar = convlst[i][1]
                while res:
                    res = self.replace(char, subchar)
        def replace(self, char, subchar):
            where = '1.0'; past_subchar = '1.0'
            while where:
                where = self.tainput.search(char, past_subchar, END+'-1c')
                past_subchar= '{}+{}c'.format(where, len(subchar));
                past_char = '{}+{}c'.format(where, len(char));
                if where:
                    self.tainput.delete(where, past_char)
                    self.tainput.insert(where, subchar)
                else:
                    return False
        def onShowInvisible(self):
            self.showinvis = not self.showinvis
            self.convertWhitechars()

    def main():
        guiCO = GuiContainer()
        guiCO.initBuildWindow()
        guiCO.root.mainloop()

    if __name__ == "__main__":
        main()