@TOC
前言
本文只作为笔记记录。
目前,我们已经从Tin标记文本生成了标记单元。比如下方Tin标记文本。
<title>TITLE|2
<p>*THIS ;
|is title.|
经过TinParser
处理后,返回一个iterable对象,单元为一个元组:
(
(1,'<title>','TITLE','2'),
(2,'<p>','*THIS ','is title')
)
主要内容为:行号、标签名,标记内容。
那么为了在tkinter文本框中呈现这些内容,我们需要根据标签名称,确定标记内容的意义,这就需要用到解释器。这个名字可能有点怪,因为在新的Tin标记语言实现中,沿用边解释边渲染的方法,只不过遍历对象不再是之前的列表,而是现在的可迭代对象,速度更快。
逐个解释
在新版Tin标记语言实现中,TinText使用遍历通过TinParser
返回的迭代对象,采用逐个解释和渲染的方法,效率更高,而不是等所有解释完再一起渲染。另外Tin标记的即时呈现特性也需要这样做。
篇幅有限,本专栏的所有后续文章均以<title>, <p>
标签为例。
TinText
的初始化如下:
class TinText(ScrolledText):
"""
TinEngine.TinText
tin标记语言渲染核心
"""
RENDERING=False#渲染状态
def __init__(self, master, *args, **kw):
"""
部分参数是直接确定的
开发者只能通过实例化后进行更改
"""
super().__init__(master, *args, **kw)
self.config(borderwidth=0, relief="flat", insertbackground='#000000', insertborderwidth=1,
wrap='char', spacing1=10)
self.tinml=TinML()#tin标记记录
self.tinparser=TinParser()#解析器
self.balloon=Balloon()#提示框
self.img_thread_pool=ThreadPoolExecutor(max_workers=10)#图片下载线程池
self.__initialize()
def __initialize(self):
#自身样式
#鼠标为箭头
self.config(cursor='arrow')
#==========
#相关设置
#标题
font_info=self.cget('font').split(' ')
self.font_family=font_info[0]
self.font_size=int(font_info[1])
self.title_level=('1','2','3','4','5','6')
self.title_size_dict={'1':self.font_size+12,
'2':self.font_size+10,
'3':self.font_size+8,
'4':self.font_size+6,
'5':self.font_size+4,
'6':self.font_size+2
}
#文本块开头标记
self.paragraph_mark=('*','/','_','-','!')
self.paragraph_link_re=re.compile('.*?!\[(.*?)\]\((..*?)\)')
#==========
#基本样式
#错误信息
self.tag_config('error',foreground='red')
#标题,鼠标为输入样式
self.tag_config('title')
self.tag_config('title1',font=(self.font_family,self.title_size_dict['1']))
self.tag_config('title2',font=(self.font_family,self.title_size_dict['2']))
self.tag_config('title3',font=(self.font_family,self.title_size_dict['3']))
self.tag_config('title4',font=(self.font_family,self.title_size_dict['4']))
self.tag_config('title5',font=(self.font_family,self.title_size_dict['5']))
self.tag_config('title6',font=(self.font_family,self.title_size_dict['6']))
self.tag_bind('title','<Enter>',lambda e:self.config(cursor='xterm'))
self.tag_bind('title','<Leave>',lambda e:self.config(cursor='arrow'))
#普通文本段,各个样式(粗体*、斜体/、下划线_、删除线-、超链接!等)文本段,鼠标为输入样式
self.tag_config('paragraph')#基础样式,每个都有
self.tag_bind('paragraph','<Enter>',lambda e:self.config(cursor='xterm'))
self.tag_bind('paragraph','<Leave>',lambda e:self.config(cursor='arrow'))
#...
接下来,我们创建一个render
方法,对标记单元进行标记解释。
def render(self,tintext='<tin>TinText',new=True):
#渲染tin标记
self.RENDERING=True
img_threadings=list()
tinconts=self.tinparser.parse(tintext)
self.config(state='normal')
#...
for unit in tinconts:
#unit[0]为行数,unit[1]为标记标签,unit[2]必定存在
#处理错误
if type(unit)!=tuple:
self.__render_err(unit.msg)
break
#解析标记、渲染
unit_length=len(unit)
match unit[1]:
#...
case '<p>':
#<p>text1|[text2]|...
#参数无穷,只在最后一段文本进行换行,适用于<p>内文本样式拼接
# 开头标记为空格时,剪去一个空格,因此文本块开头如果需要一个空格时,
# 在前一个文本块加入空格,或在该文本快加入两个空格,以此类推
for p in unit[2:-1]:
self.__render_paragraph(p)
self.__render_paragraph(unit[-1],True)
self.tinml.addtin('<p>',text=unit[2:])
case '':
#默认文本,同<p>的第一个参数
self.__render_paragraph(''.join(unit[2]),True)
case '<title>'|'<t>':
#<title>title|[level]
#title-标题
#level-标题级别,1~6
level=''
if unit_length>4:
err=f'[{unit[0]}]<title>标记参数超出限制:\n{"|".join(unit[1:])}\n<title>标题|级别'
self.__render_err(err)
break
if unit_length>=3:
level='1'
if unit_length>=4:
level=unit[3]
if level=='':
level='1'
elif level not in self.title_level:
err=f'[{unit[0]}]<title>标题级别应在1~6中,而非"{level}"'
self.__render_err(err)
break
self.__render_title(unit[2],level)
对于<p>
标记,我们直接进行单元分割。
对于<title>
标记,我们首先确定第一个标记内容就是标题内容,然后通过判断存不存在第二个标记内容,来确定到底是什么层级的标题。
当标记内容已经被明确意义后,就可以进行下一步的渲染了。