引言
像CSDN的Markdown编辑器一样,tkinter同样可以实现边编写边进行渲染。但由于调用tcl的效率问题,我们还需要考虑取舍。
这篇文章主要是思路。
选择渲染文本(引擎)
tkinter公开的纯拓展只有能够显示HTML3的tkhtmlview,功能还是比较简陋的。为了提高丰富性,我决定使用自己写的Tin标记语言。
Tin标记语言的介绍在我的专栏 Tin标记语言 中。
那么既然选择这个编辑语言,就要有相应的渲染文本框。当然,我已经编译了 TinEngine.pyd 可供快速调用,在 Tin知识库 中有下载、依赖、内部环境和使用的介绍(预览和接口中)。TinEngine是专门解析并渲染Tin标记语言的文本框,完全依靠tkinter自身实现。
基础效果如下:
取舍
排除标签
因为 Tin 在被解析后有一定的交互能力,所以在Tin标记语言中,有一些标签会影响渲染。那么当遇到这类标签时,就跳过不解析。
当然,TinEngine 没有提供这个功能,这需要在即时编辑器的代码中进行处理。
比如:
if obj[0] in ['<-pass->','</-pass>','<key>','<-askyesno->','</-askyesno>','<jit>','<stop>','<webopen>']:#跳过影响解析时间的标签
if obj[0]=='<webopen>':
units.append('<main>暂不显示网址打开请求;center;white-grey')
elif obj[0]=='<img>':
units.append('<main>图片暂不显示;center;white-grey')
elif obj[0]=='<html>':
html=1
units.append('<main>暂不显示HTML文本;center;white-grey')
else:
units.append(i.replace('<pass>','<main>').replace('<askyesno>','<main>'))
选择渲染范围
这也是一个无奈的选择,浏览器由编译类语言编写,即时渲染速度快。但Python本身就是解释类语言,而且还要调用tcl显示窗口,所以不得不限定渲染范围。
我选择的是光标上下30行的范围进行渲染。
具体如下:
insert=int(str(Text.index('insert')).split('.')[0])
#为了加快解析效率,值获取鼠标上下各30行内容解析
lines=len(Text.get(1.0,'end').split('\n'))
if insert<=30:
startline='1.0'
else:
startline=str(insert-30)+'.0'
if lines-insert<=30:
endline='end'
else:
endline=str(insert+30)+'.0'
words=Text.get(startline,endline)
是否不变
如果接收到鼠标或键盘事件时,需要判断是否与之前的内容一样,如果一样,就不渲染了。这个很简单,下面直接给出完整代码。
完整代码
# -*- coding: utf-8 -*-
# 仅作为Tin的一个小工具,因为效率和速度影响,不会运用到实际中
import sys
import tkinter as tk
from tkinter import scrolledtext
import tkinter.ttk as ttk
import re
import PIL#need
import requests#need
import win32gui#need
from TinEngine import TinText,set_name,set_file
old=''
html=0#不显示HTML
def call_back(event):
global old,html
insert=int(str(Text.index('insert')).split('.')[0])
#为了加快解析效率,值获取鼠标上下各30行内容解析
lines=len(Text.get(1.0,'end').split('\n'))
if insert<=30:
startline='1.0'
else:
startline=str(insert-30)+'.0'
if lines-insert<=30:
endline='end'
else:
endline=str(insert+30)+'.0'
words=Text.get(startline,endline)
if words!=old:
old=words
words=words.split('\n')
units=['']
for i in words:
obj=re.findall('^(<.*?>).*$',i)
if len(obj)==0:
if html!=1:
units.append(i)
continue
if obj[0]=='</html>':
html=0
if html==1:
continue
if obj[0] in ['<-pass->','</-pass>','<key>','<-askyesno->','</-askyesno>','<jit>','<stop>','<webopen>']:#跳过影响解析时间的标签
if obj[0]=='<webopen>':
units.append('<main>暂不显示网址打开请求;center;white-grey')
elif obj[0]=='<img>':
units.append('<main>图片暂不显示;center;white-grey')
elif obj[0]=='<html>':
html=1
units.append('<main>暂不显示HTML文本;center;white-grey')
else:
units.append(i.replace('<pass>','<main>').replace('<askyesno>','<main>'))
MyText.point_file(units,1)
else:
MyText.update()
Wheel(Text,[MyText])
def Wheel(text,other:list):
first=text.vbar.get()[0]
for i in other:
i.yview('moveto',first)
top=tk.Tk()
top.geometry("1250x780+30+0")
top.minsize(152, 1)
top.maxsize(1924, 1055)
top.resizable(1, 1)
top.title("Tin即时渲染")
top.configure(background="#d9d9d9")
Text = scrolledtext.ScrolledText(top,font=('宋体',13))
Text.place(relx=0.0, rely=0.0, relheight=1, relwidth=0.481)
Text.configure(background="white")
Text.configure(foreground="black")
Text.configure(highlightbackground="#d9d9d9")
Text.configure(highlightcolor="black")
Text.configure(insertbackground="black")
Text.configure(selectbackground="#c4c4c4")
Text.configure(selectforeground="black")
Text.configure(wrap="word")
Text.bind('<KeyRelease>',call_back)
#Text.bind('<Return>',call_back)
MyText = TinText(top,font=('宋体',13))
MyText.place(relx=0.501, rely=0.0, relheight=1, relwidth=0.501)
MyText.configure(background="white")
MyText.configure(foreground="black")
MyText.configure(highlightbackground="#d9d9d9")
MyText.configure(highlightcolor="black")
MyText.configure(insertbackground="black")
MyText.configure(selectbackground="#c4c4c4")
MyText.configure(selectforeground="black")
MyText.configure(wrap="word")
set_name(MyText)
Text.bind('<MouseWheel>',lambda event:Wheel(Text,[MyText]))
MyText.bind('<MouseWheel>',lambda event:Wheel(MyText,[Text]))
top.iconbitmap('Tin.ico')
top.mainloop()
效果如下:
结语
这篇文章仅给出了思路,如何加快解析效率,“任重而道远”。
☀tkinter创新☀