引言
在前一篇文章中,我们完成了从tkinter文本框中获取富文本描述内容,并且通过简单的解析生成了*.tkr文件。那么,接下来我们将反其道而行之,通过解读tkr描述,将富文本内容重新显示在文本框中。
加载样式
像HTML会先从<style>
元素中加载css,tkr同样需要先解读tag部分描述的样式内容。
匹配样式描述
通过前面的准备我们知道,tag部分中,每一个样式名称对应一个字典。因此,我们可以使用正则匹配,来匹配符合tkr规范的样式内容。
为了方便理解,我们直接获取tag部分的内容。
tags=re.findall( r'=tagon=\n(.*?)\n=tagoff',tkr,re.S)[0]
#tkr::tkr文件内容
经过上面的操作,我们得到了tag部分的全部内容,但是我们需要对每一个样式进行处理。因此,我们要将这部分内容以换行为分界点分成一个列表,再对列表中的每一行内容进行操作。
for i in tags.split('\n'):
if i=='':
continue
#其它操作
通过正则匹配,进一步获取样式名称和json格式的字典:
for i in tags.split('\n'):
if i=='':
continue
tagform=re.findall(r'^(.*?) (.*)$',i)[0]
然后我们要将字典转化为Python中的实例。Python提供了eval函数,可以返回一串Python代码的值。
for i in tags.split('\n'):
if i=='':
continue
tagform=re.findall(r'^(.*?) (.*)$',i)[0]
tag_dict=eval(tagform[1])#转化字典
加载样式描述
考虑的样式的名称在tkinter中以字符串(str)存在,同时,Python也为我们提供了组件运行Python代码的exec函数,我们可以直接使用这个函数对文本框进行样式加载:
exec("tkrt.tag_config('"+tagform[0]+"',**tag_dict)")
渲染文本
因为tkr文件的text部分是通过Text直接生成的,因此这一步也要方便多。但是还有一个要注意的地方,也就是列表中每个元素的第一个子元素代表了这个元素的含义,因此,对不同类型的元素,我们要进行不同的渲染操作。
words=eval(re.findall(r'=texton=\n(.*?)\n=textoff=',tkr,re.S)[0])
#渲染
for mark,u,end in words:
if mark=='tagon':
#...
elif mark=='tagoff':
#...
elif mark=='text':
#...
别看这个部分现在很简单,那是因为tkr目前的版本只是初版,能够解析和渲染的内容是有限的。而且,tkr是描述型文本,无法对tkinter中动态交互内容进行描述,这是tkinter自身的限制。如果需要更加丰富的tkinter富文本功能,甚至能够实现窗口布局,请查看Tin标记语言。
简单的渲染操作
nowtag=[]
for mark,u,end in words:
if mark=='tagon':
nowtag.append(u)
elif mark=='tagoff':
nowtag.remove(u)
elif mark=='text':
tkrt.insert(end,u,tuple(nowtag))
样式叠加
从上面的代码可以看出,nowtag
作为一个动态列表存在,根据tkr描述内容的变化,即时增删样式名称。
这个列表的含义就是当前需要在显示区渲染的样式名称,这样就可以方便地进行样式叠加渲染了。
完整代码
通过以上探究,我们得出以下将tkr转化为Text富文本的方法:
#渲染部分
def render(tkrt:tk.Text,tkr:str):
'''
tkrt::tkinter.Text类
tkr::tkr文本的内容
'''
tags=re.findall( r'=tagon=\n(.*?)\n=tagoff',tkr,re.S)[0]
#在文本框中注册每一个标记
for i in tags.split('\n'):
if i=='':
continue
tagform=re.findall(r'^(.*?) (.*)$',i)[0]
tag_dict=eval(tagform[1])
exec("tkrt.tag_config('"+tagform[0]+"',**tag_dict)")
words=eval(re.findall(r'=texton=\n(.*?)\n=textoff=',tkr,re.S)[0])
#渲染
nowtag=[]#当前的tag样式
for mark,u,end in words:
if mark=='tagon':
nowtag.append(u)
elif mark=='tagoff':
nowtag.remove(u)
elif mark=='text':
tkrt.insert(end,u,tuple(nowtag))
return True
效果
tkr文件内容
=tagon=
title_center#626D79-white0微软雅黑 {'background': 'white', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '微软雅黑 23', 'foreground': '#626D79', 'justify': 'center', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': 'groove', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '1', 'underlinefg': '', 'wrap': ''}
middles {'background': '', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '', 'foreground': '', 'justify': '', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': '', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '', 'underlinefg': '', 'wrap': ''}
middlesorange {'background': '', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '', 'foreground': 'orange', 'justify': '', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': '', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '', 'underlinefg': '', 'wrap': ''}
<word>link>bluetinhome.baklib.com {'background': '', 'bgstipple': '', 'borderwidth': 2, 'elide': '', 'fgstipple': '', 'font': '', 'foreground': 'blue', 'justify': '', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': 'ridge', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '1', 'underlinefg': '', 'wrap': ''}
title_center#626D79-white1宋体 {'background': 'white', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '宋体 21', 'foreground': '#626D79', 'justify': 'center', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': 'groove', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '1', 'underlinefg': '', 'wrap': ''}
title_left#626D79-white2宋体 {'background': 'white', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '宋体 19', 'foreground': '#626D79', 'justify': 'left', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': 'groove', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '1', 'underlinefg': '', 'wrap': ''}
msg_#626D79-white {'background': 'white', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '宋体', 'foreground': '#626D79', 'justify': '', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': '', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '', 'underlinefg': '', 'wrap': ''}
middlecred {'background': '', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '', 'foreground': 'red', 'justify': '', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': '', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '1', 'underlinefg': '', 'wrap': ''}
msg_orange {'background': '', 'bgstipple': '', 'borderwidth': '', 'elide': '', 'fgstipple': '', 'font': '宋体', 'foreground': 'orange', 'justify': '', 'lmargin1': '', 'lmargin2': '', 'lmargincolor': '', 'offset': '', 'overstrike': '', 'overstrikefg': '', 'relief': '', 'rmargin': '', 'rmargincolor': '', 'selectbackground': '', 'selectforeground': '', 'spacing1': '', 'spacing2': '', 'spacing3': '', 'tabs': '', 'tabstyle': '', 'underline': '', 'underlinefg': '', 'wrap': ''}
=tagoff=
=texton=
[('tagon', 'title_center#626D79-white0微软雅黑', '1.0'), ('text', 'TinExpander——Tin拓展介绍\n', '1.0'), ('tagoff', 'title_center#626D79-white0微软雅黑', '2.0'), ('tagon', 'middles', '2.0'), ('text', '由 ', '2.0'), ('tagoff', 'middles', '2.2'), ('tagon', 'middlesorange', '2.2'), ('text', 'TinExpander', '2.2'), ('tagoff', 'middlesorange', '2.13'), ('tagon', 'middles', '2.13'), ('text', ' 开发组编写', '2.13'), ('tagoff', 'middles', '2.19'), ('text', '\n', '2.19'), ('text', 'Tin知识库入口: ', '3.0'), ('tagon', '<word>link>bluetinhome.baklib.com', '3.10'), ('text', 'tinhome.baklib.com', '3.10'), ('tagoff', '<word>link>bluetinhome.baklib.com', '3.28'), ('text', '\n', '3.28'), ('text', '\n', '4.1'), ('tagon', 'title_center#626D79-white1宋体', '5.0'), ('text', '什么是Tin拓展\n', '5.0'), ('tagoff', 'title_center#626D79-white1宋体', '6.0'), ('tagon', 'title_left#626D79-white2宋体', '6.0'), ('text', 'Tin拓展的定义\n', '6.0'), ('tagoff', 'title_left#626D79-white2宋体', '7.0'), ('tagon', 'msg_#626D79-white', '7.0'), ('text', ' Tin拓展是指基于TinExpander提供的运行环境,通过 main.dict 文件解释执行,在expand拓展目录中工作的Python类源文件。\n', '7.0'), ('text', ' Tin拓展实际上是一个类源码文件夹,通过TinExpander而作为一个应用的存在,其主体由相对底层语言的Python编写。该文件夹由拓展应用主体(main.dict)、图标文件(main.png)和各依赖文件组成,存放在data目录中的expand子目录下。当启动TinExpander时,将会自动读取拓展应用信息,以便随后使用。\n', '8.0'), ('tagoff', 'msg_#626D79-white', '9.0'), ('tagon', 'middles', '9.0'), ('text', ' Tin拓展应用', '9.0'), ('tagoff', 'middles', '9.11'), ('tagon', 'middlecred', '9.11'), ('text', '必须和Tin的辅助和延伸有关。', '9.11'), ('tagoff', 'middlecred', '9.26'), ('text', '\n', '9.26'), ('text', '\n', '10.0'), ('tagon', 'title_left#626D79-white2宋体', '11.0'), ('text', 'Tin拓展应用的运行\n', '11.0'), ('tagoff', 'title_left#626D79-white2宋体', '12.0'), ('tagon', 'msg_#626D79-white', '12.0'), ('text', ' Tin拓展的运行依赖于TinExpander。TinExpander提供了expand目录下拓展文件夹必要的运行环境和调用功能,从而使Tin拓展真正为Tin的延伸而工作。\n', '12.0'), ('text', ' Tin拓展应用的主体包含在 main.dict 文件中。该文件实际上是一个字典文件,包含了拓展应用的作者、版本、版权、源码等信息,这将作为该拓展的重要信息被TinExpander读取,并显示到拓展应用框中。当然,这里的源码是受TinExpander的接受和保护的,这个会在后面的“打包Tin拓展”中提到。\n', '13.0'), ('text', ' Tin拓展的图标是 main.png。这个文件通常很小,相较于其它png文件,图标文件的大小一般为200x200左右,在TinExpander中会被缩略为90x90大小。\n', '14.0'), ('text', ' 其它文件则是该拓展的依赖文件,不受TinExpander限制。这将在“打包Tin拓展”中进行详细说明。\n', '15.0'), ('tagoff', 'msg_#626D79-white', '16.0'), ('text', '\n', '16.0'), ('text', '\n', '17.0'), ('text', '\n', '18.1'), ('tagon', 'title_center#626D79-white1宋体', '19.0'), ('text', '为什么需要Tin拓展\n', '19.0'), ('tagoff', 'title_center#626D79-white1宋体', '20.0'), ('tagon', 'title_left#626D79-white2宋体', '20.0'), ('text', 'Tin拓展的目的\n', '20.0'), ('tagoff', 'title_left#626D79-white2宋体', '21.0'), ('tagon', 'msg_#626D79-white', '21.0'), ('text', ' TinGroup的功能组件(也就是Tin组件)是有限的,因为我们只会为一些对Tin的使用有必要的、有关联的、有复杂性的事开发Tin组件,而一些细枝末节的东西,我们也就会忽略了。但是,像软件使用安全、文件管理、可视化的编辑等功能,与Tin的主要作用没有太大关联,但好像又必不可少,这时就需要广大的开发者或者是业余爱好者提供相应的拓展,来完成这些比较细致的任务了。\n', '21.0'), ('tagoff', 'msg_#626D79-white', '22.0'), ('tagon', 'msg_orange', '22.0'), ('text', ' 同时,Tin拓展应用可以提高大家对TinGroup的使用热情和效率,因此推动Tin拓展的发展,也是为了促进人与人之间的交流。\n', '22.0'), ('tagoff', 'msg_orange', '23.0'), ('text', '\n', '23.0'), ('tagon', 'title_left#626D79-white2宋体', '24.0'), ('text', 'Tin拓展的意义\n', '24.0'), ('tagoff', 'title_left#626D79-white2宋体', '25.0')]
=textoff=
渲染效果
结语
tkr文件的开发很可能会在这停留好长一段时间,毕竟我的主要精力放在Tin标记语言上。感兴趣的tkinter爱好者可以到github参与TKR的完善和改进。