我正在使用 Tkinter/Python3 开发 Devanagari/Indic 编辑器,并且对 Text 小部件字体渲染有疑问。
梵文(以及所有印度语言文字)是一个字母音节,辅音组(“簇”)和元音符号形成一个音节。辅音使用 Unicode ZWJ 字符连接,Text 小部件负责呈现(元音变音符号可能会重新排序)。出现在辅音之后的元音字符使用变音符号形式而不是它们的完整形式。请参阅 Microsoft 的以下说明。
天城文音节- 天城文 > 书写系统的有效正字法“单位”。音节由辅音字母、独立>元音和从属元音组成。在文本序列中,这些字符 > 按音序存储(尽管它们在显示时可能不按音序表示)。音节一经定形,便不可分。 光标不能定位在音节内。>本文档中讨论的转换不跨越音节边界。
当不使用鼠标拖动选择文本时,我会注意 Python 事件处理程序,以便 INSERT 和 CURRENT 光标永远不会位于音节内的字符位置,因此不会显示元音变音符号和辅音半形式。
但是,在处理鼠标拖动(用于文本选择)时,我感到很困惑。我似乎无法弄清楚如何在拖动(B1-Motion)事件期间禁止鼠标光标移动到音节之间的字符位置。
我
self.editorText.mark_set ("my_index", "@%d,%d" % (event.x, event.y))
用来获取拖动索引,然后使用我的例程寻找音节左侧和外部的第一个字符位置。但这只是行不通。
请参阅下面有关字体渲染问题的图片...
任何帮助表示赞赏!
-PP
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import font
reverse = {}
ucode = {}
varna = {}
enc1 = {}
enc2 = {}
first_vowel = ''
allLines = """VIRAMA Special 2381 -
p Consonant 2346 -
r Consonant 2352 -
A Vowel 2310 2366
aa Vowel 2310 2366
u Vowel 2313 2369
D Consonant 2337 -
ai Vowel 2320 2376
g Consonant 2327 -
n Consonant 2344 -
ZWNJ JoinerSpecial 8204 -
ZWJ JoinerSpecial 8205 -
"""
#default EditorText height in pixels
editorTextWinfoHeightPixels = 1
editorTextFontHeightPixels = 1
class klasse(object):
def __init__(self, master):
self.top_line_number = 1
self.bottom_line_number = 20
self.last_line_number = 1
self.current_line_number = 1
self.current_char_position = 0
self.previous_linelength = 0
self.current_linelength = 0
self.ieSelection = ''
self.ieSelectionLength = 0
self.dragLineStart = 0
self.dragLineEnd = 0
self.dragCharStart = 0
self.dragCharEnd = 0
self.numCopiedLines = 0
self.fonts=list(font.families())
self.fonts.sort()
maxFontNameLength = len(max(self.fonts))
self.frame = tk.Frame(root)
self.editorFrame = tk.Frame(self.frame)
self.editorFrame.pack(side="left", fill="both", expand="yes")
self.editorText = tk.Text(self.editorFrame, exportselection="true")
self.editorText.pack(side="top", fill="both", expand="yes", padx=2)
self.editorText.config( wrap="word", # use word wrapping
undo=True,
font=('Arial', 20))
self.editorText.see(tk.INSERT)
self.editorText.focus()
self.editorText.bindtags((self.editorText))
self.editorText.bind('<B1-Motion>', self.handleMouseDrag)
self.editorText.bind('<Button-1>', self.mouseButtonCallback)
self.frame.pack(fill="both", expand="yes")
self.bottom_line_number = int(301/editorTextFontHeightPixels)
self.editorText.insert("insert", chr(2346))
self.editorText.insert("insert", chr(2381))
self.editorText.insert("insert", chr(2352))
self.editorText.insert("insert", chr(2366))
self.editorText.insert("insert", chr(2313))
self.editorText.insert("insert", chr(2337))
self.editorText.insert("insert", chr(2346))
self.editorText.insert("insert", chr(2366))
#self.editorText.insert("insert", chr(2376))
self.editorText.insert("insert", chr(2327))
self.editorText.insert("insert", chr(2344))
def handleMouseDrag (self, event):
[start_line_number, start_char_position] = self.editorText.index('current').split('.')
start_line_number = int(start_line_number)
start_char_position = int(start_char_position)
self.editorText.mark_set ("anirb_index", "@%d,%d" % (event.x, event.y))
[temp_current_line_number, temp_current_char_position] = self.editorText.index('anirb_index').split('.')
temp_current_line_number = int(temp_current_line_number)
temp_current_char_position = int(temp_current_char_position)
if temp_current_line_number > start_line_number or\
(temp_current_line_number == start_line_number and temp_current_char_position > start_char_position):
#forward selection
self.editorText.mark_set ("sel.first", str(start_line_number)+'.'+str(start_char_position))
self.editorText.mark_set ("sel.last", str(temp_current_line_number)+'.'+str(temp_current_char_position))
else:
#backward selection
self.editorText.mark_set ("sel.first", str(temp_current_line_number)+'.'+str(temp_current_char_position))
self.editorText.mark_set ("sel.last", str(start_line_number)+'.'+str(start_char_position))
if self.editorText.tag_ranges ("sel") != []:
self.editorText.tag_remove ("sel", "1.0", "end")
self.current_line_number = temp_current_line_number
self.current_char_position = temp_current_char_position
self.editorText.tag_add ("sel", "sel.first", "sel.last")
self.editorText.tag_config ("sel", background="darkblue", foreground="white")
self.editorText.mark_set ("insert", "sel.last")
def mouseSettle (self, callingWidget, mouseCursorIndex):
global ucode
current_char = ''
[temp_current_line_number, temp_current_char_position] = mouseCursorIndex
while (1):
if temp_current_char_position == 0:
break
curr_char_index = str(temp_current_line_number) + '.' + str(temp_current_char_position)
prev_char_index = str(temp_current_line_number) + '.' + str(temp_current_char_position - 1)
char_to_left = self.editorText.get (prev_char_index, curr_char_index)
if char_to_left != '':
char_to_left = ord(char_to_left)
next_char_index = str(temp_current_line_number) + '.' + str(temp_current_char_position + 1)
current_char = self.editorText.get (curr_char_index, next_char_index)
if current_char != '':
current_char = ord(current_char)
if current_char not in ucode.keys() or current_char == '':
ucode[current_char] = "null"
if char_to_left not in ucode.keys() or char_to_left == '':
ucode[char_to_left] = "null"
if ("consonant" in ucode[current_char] and "special" not in ucode[char_to_left]) or \
(ucode[current_char] == "null" and "special" not in ucode[char_to_left]) or \
ucode[char_to_left] == "vowel" or \
ucode[char_to_left] == "standalone" or \
ucode[char_to_left] == "null":
break
temp_current_char_position -= 1
#endwhile
return temp_current_char_position
#enddef
#left mouse button click in editorText widget
def mouseButtonCallback (self, event=None):
global ucode
[self.current_line_number, temp_current_char_position] = self.editorText.index('current').split('.')
self.current_line_number = int(self.current_line_number)
temp_current_char_position = int(temp_current_char_position)
self.current_linelength = len(self.editorText.get (str(self.current_line_number)+'.0', str(self.current_line_number)+'.end'))
if temp_current_char_position >= self.current_linelength:
self.current_char_position = self.current_linelength
else:
#mouseSettle will return the new character position for INSERT cursor
self.current_char_position = self.mouseSettle (0, [self.current_line_number, temp_current_char_position])
self.editorText.mark_set ("insert", str(self.current_line_number)+'.'+str(self.current_char_position))
self.editorText.mark_set ("current", str(self.current_line_number)+'.'+str(self.current_char_position))
#remove all selection
if self.editorText.tag_ranges ("sel") != []:
self.editorText.tag_remove ("sel", "1.0", "end")
difference = self.bottom_line_number - self.top_line_number
if self.bottom_line_number < self.current_line_number:
self.bottom_line_number = self.current_line_number
self.top_line_number = self.bottom_line_number - difference
if self.top_line_number > self.current_line_number:
top_line_number = self.current_line_number
self.bottom_line_number = self.top_line_number + difference
#in case user clicked on a partially visible line at the top or bottom adjust those
self.editorText.see(tk.INSERT)
self.editorText.focus()
def parse ():
global reverse
global ucode
global enc1
global enc2
global varna
global first_vowel
for line in allLines.split('\n'):
#split on the comment character
lineList = line.split('#')
line = lineList[0] #whatever came before the comment character
if line.lstrip() == '':
continue
lineList = line.split()
#discard comments
if len(lineList) == 4:
(key, v, e1, e2) = lineList
print ("key is ---", key)
if e1 == "-":
e1 = ''
if e2 == "-":
e2 = ''
elif len(lineList) > 0 and len(lineList) < 4:
print('Error: Illegal Map ')
varna[key] = v.lower()
enc1[key] = e1.lower()
enc2[key] = e2.lower()
if varna[key] == "vowel" and enc2[key] == '':
first_vowel = key
#now create the hashes that characterize each 16-bit unicode character
if "+" not in e1 and e1 != '':
ucode[int(e1)] = v.lower()
#this is the reverse map of enc1 to the latin input character (key)
reverse[int(e1)] = key
if "+" not in e2 and e2 != '':
ucode[int(e2)] = v.lower()
#this is the reverse map of enc2 to the latin input character (key)
reverse[int(e2)] = key
parse()
counter = 0
root = tk.Tk()
app = klasse(root)
root.minsize (900, 350)
root.mainloop()