引言
在Tin中,“容器”这个概念早在第一篇定义文章中就出现了。由于本篇文章主要讲述如何实现容器机制,并且在前面的文章中已经说明了如何实现容器的解析,因此本篇侧重通过Python代码实现容器的渲染。
渲染流程
以下代码均已解析和渲染<-pass->标签为例。
分离
通过前面的几篇文章可知,Tin采用列表式的顺序渲染,即逐行解析每一行Tin标签(逐个解析列表中的每一项),然后根据该行的标签名称,处理标签后的参数,接着根据参数处理得到的结果进行渲染。
但是每一个容器名称都只是一个标志而已,容器中的内容与正规的Tin标记没有任何区别,那么怎么让Tin分离容器中的内容呢?其实并不复杂。
因为Tin采用逐个解析的模式,在处理列表之前有一段前置操作(前面的文章中有),如果我们在这段操作中放入一个解析标志和一段额外的解析代码,问题就解决了。在Tin中,使用变量 pass_t 来表明现在处理的标签是否是容器内的标签。代码如下:
def point_file(self,unit:list, cls=1, **args):
#...
pass_t=0#定义一个“标志”
pass_list=[]#加载容器内容的列表
#...
for i in unit:#开始处理列表
#...
if pass_t==1:
#渲染操作...
pass_list.append(i)#加入标记
continue
#...前置代码完成,开始处理标签
#...
elif tag[0]=='<-pass->':#开始
pass_t=1
elif tag[0]=='</-pass>':#结束
pass_t=0#恢复标志
#...渲染操作
pass_list=[]#清空列表
通过这段代码框架,我们得到了 pass_list,一个含有该容器所有内容的列表。
当然,实际上Tin要比这个复杂,这里仅仅是框架
接下来就到辨别是否渲染的部分了。
辨别渲染
因为“-pass-”标签集的渲染与否,取决于读者输入的密码是否符合当前认可的密码,在上一篇文章中,就有一个变量:keypass,如果 keypass 的值为 True,那么Tin将渲染标签集(容器)中的内容。否则跳过。代码如下:
这段代码在处理 <-pass->标签的代码段中。
pass_t=0
if keypass==True:#如果密码正确,正常渲染
self.point_file(pass_list,insert=self.end)
pass_list=[]
错误返回
在Tin中,使用 self.error() 函数返回错误行数以及错误原因。
Tin一般返回错误标签在全文的位置,可是在Tin的容器中,错误回显的位置应该是在容器中的位置,而不是在全文的位置。因此,我们就需要实现一个能够定位错误标签在容器中位置的功能。
TinChecker使用该方法,TinEngine的错误回显显示的是全文位置
一般来说,可以在渲染开始前定义一个int变量为1,然后遍历列表中的标记,每次遍历后将这个变量加1。但是这样有一个大毛病,那就是效率问题,每次都要分别使用 point_file() 来渲染标签行,实在是没必要,为此,我想到了另一个方法——由 point_file() 返回当前渲染标签,在通过标签在容器中的位置,进行定位。
因为当 point_file() 发现标记错误时,会使用 break 退出逐行渲染,退出后,变量 i 就是当前渲染的标签行。因为空行在Tin中会忽略跳过,因此我们可以在要渲染的列表最后加入一个空字符,如果 point_file() 返回的是空字符,那么万事大吉,否则,定位错误。那么对 point_file() 的修改如下:
def point_file(self,unit:list, cls=1, **args):
#...前置操作
for i in unit:#开始处理列表
#...解析渲染操作
#假如发生某个错误,退出循环
return i
接着,对<-pass->标签集的解析代码段更改如下:
pass_t=0
pass_list.append("")#加入空字符最为是否解析玩完的标志
if keypass==True:#如果密码正确,正常渲染
last=self.point_file(pass_list,insert=self.end)
if last!="":#如果最后一个不是空字符
#错误回显操作
pass_list=[]
至此,容器框架基本实现。
结语
容器概念在Tin中十分重要,是Tin实现交互的重要组成部分,也是提高渲染丰富程度的一个优秀解决方案。
现在,可以继续完善你自己的标记语言解析渲染器了。
不只是tkinter,wxpython、pyqt均可以。只不过tkinter更加(十分)小巧