引言
在TinUIXml簡易編輯器中,我們通過TinUI搭建了一個簡易的針對TinUIXml布局的編輯器,基本掌握了TinUIXml布局和TinUIXml的導入與導出。現在,就在此基礎上,對編輯器進行升級。
本次升級的功能:
- 更合理的xml編輯與UI展示布局,python代碼導出放到另一個子窗口
- python代碼使用語法高亮顯示
- 在UI展示中,允許打開鼠標十字線坐標定位
- 允許右鍵單擊創建標記坐標點,并能夠通過坐標點管理窗口來操作坐標點
新版TinUIXml簡易編輯器內置到了TinUI包中,
/test/tuxmltest.py
,xml資源文件在/test/xmltestpage
目錄下。
重新布局
新的主界面main.xml
:
<tinui>
<line anchor='w' y='20'><button2 text='導入xml' command='self.funcs["inxml"]'></button2><button2 text='python代碼' command='self.funcs["pycode"]'></button2><checkbutton text='啟用十字線定位' command='self.funcs["if_location"]'></checkbutton><paragraph text='x: y:'>loctext</paragraph>
</line>
<line><ui width='870' height='630' scrollbar='True'>xmlui</ui>
</line>
<line x='710' y='20' anchor='w'><button2 text='打開標記點管理窗口' command='self.funcs["open_markw"]'></button2>
</line>
</tinui>
生成界面:
在新版TinUIXml編輯器中,默認添加了一段來自于TinUI.test.maintest里的
back
介紹界面xml,這樣就可以一目了然明確文本框的交互意義,點擊“導入xml”后如圖:
新python代碼窗口
在新版TinUIXml簡易編輯器中,python代碼部分被劃歸到了新的子窗口,名為pycodew
,資源文件為pytest.xml
:
<!--彈窗顯示python代碼(IDO)部分-->
<tinui>
<line><textbox width='500' height='500' scrollbar='True'>textbox</textbox>
</line>
<line anchor='w' y='530'><button text='復制' command='self.funcs["copy_pycode"]'></button><button text='標注重點' command='self.funcs["highlight"]'></button><label text='※該內容不可更改'></label>
</line>
</tinui>
在tuxmltest.py
中,創建新的窗口:
import idlelib.colorizer as idc
import idlelib.percolator as idp#...#彈窗窗口
pycodew=Toplevel()
pycodew.title("Python代碼")
# 設置窗口大小
window_width = 520
window_height = 550
# 獲取屏幕大小
screen_width = pycodew.winfo_screenwidth()
screen_height = pycodew.winfo_screenheight()
# 計算窗口居中的x和y坐標
x_coordinate = int((screen_width/2) - (window_width/2))
y_coordinate = int((screen_height/2) - (window_height/2))
# 設置窗口的位置和大小,并禁止改變尺寸
pycodew.geometry("{}x{}+{}+{}".format(window_width, window_height,x_coordinate, y_coordinate))
pycodew.resizable(width=False, height=False) # 禁止改變窗口大小
pctinui=BasicTinUI(pycodew)
pctinui.pack(fill='both',expand=True)
pycodew.protocol("WM_DELETE_WINDOW", lambda: pycodew.withdraw()) # 忽略關閉窗口的協議
pycodew.withdraw()
pctinuix=TinUIXml(pctinui)xmlf=open(os.path.dirname(__file__)+r'\xmltestpage\pytest.xml','r',encoding='utf-8')
xml=xmlf.read()
xmlf.close()
pctinuix.environment(globals())
pctinuix.loadxml(xml)
textbox=pctinuix.tags['textbox'][0]
idc.color_config(textbox)
p = idp.Percolator(textbox)
d = idc.ColorDelegator()
p.insertfilter(d)
注意到其中的idc
, idp
等,都是從idlelib中導入的,用來對文本框進行python語法高亮處理。
新xml編輯器對于python代碼的處理,與上一版一樣,都是重新生成一遍TinUI界面,在此過程中生成對應IDO片段部分python代碼,這里不做贅述。新的變化是能夠直接復制python代碼,同時可以通過注釋生成重點內容。
功能代碼如下:
def copy_pycode(e):#復制textbox.clipboard_clear()copyText = textbox.get(1.0,'end')textbox.clipboard_append(copyText)
def highlight(e):#標注funcs,datas等重點havefunc,havedata=False,Falsetextbox.configure(state='normal')write('\n#TinUIXml導入重點:\n')for i in textbox.result:if i[0]=='funcs':if havefunc==False:havefunc=Truewrite('#函數/方法(funcs):\n')write(f'# {i[1]}(...)\n')elif i[0]=='datas':if havedata==False:havedata=Truewrite('#數據(datas):\n')write(f'# {i[1]}=...\n')havetag=Falsefor tag in duixml.tags.keys():if havetag==False:havetag=Truewrite('\n#TinUIXml導出重點:\n')write(f'# {tag}\n')textbox.configure(state='disabled')
十字線定位
TinUIXml只是解決了每塊包含若干控件元素的行元素默認布局問題,但是行元素<line>
的起始位置是可以自定義的,同時,行元素內部也可以嵌套新的行元素,起始位置也可以自定義。如果我們要在TinUIXml布局時也保持絕對坐標布局的靈活性,就需要知道在當前xml布局(新xml布局編寫之前)下,界面元素所占的空間位置狀態。
在新TinUIXml編輯器中,直接使用了BasicTinUI自帶的設計模式,通過display.show_location
控制開啟和關閉。
TinUI(BasicTinUI)設計模式在5.1版本中加入。
loclines=False#坐標十字線是否存在
def if_location(e):#是否顯示坐標十字線global loclinesloclines=eif loclines:tinui.itemconfig(loctext,state='normal')displayui.show_location(command=getloc)else:tinui.itemconfig(loctext,text='x:? y:?',state='hidden')displayui.show_location(False)def getloc(x,y):tinui.itemconfig(loctext,text=f'x:{x} y:{y}')
標記點繪制
在新TinUIXml編輯器中,我們設計右鍵單擊繪制標記點,這個功能非常簡單,就先不在這里給出詳細代碼,稍后會隨標記點的管理給出。不過需要注意的是,當ui界面重繪,也就是inxml()
方法執行后,我們也需要重繪這些點,因此,我們使用mark_points:list=[]
來記錄這些點的位置信息和畫布對象id。
只需要在inxml()
后加入reset_marks()
即可。
def inxml(e):#注入xml界面xml=text.get(1.0,'end')duixml.funcs=dict()duixml.datas=dict()duixml.tags=dict()result=re.findall("self\.(.*?)\[.(.*?).\]'",xml,re.M|re.S)for i in result:if i[0]=='funcs':duixml.funcs[i[1]]=Noneelif i[0]=='datas':duixml.datas[i[1]]=(None,None)duixml.yendy=5duixml.clean()duixml.loadxml(xml)rescroll()reset_marks()#!!!def reset_marks():#重新繪制標記點if len(mark_points)==0:returnindex=1for i in mark_points[1:]:mark=displayui.create_oval((i[0][0],i[0][1],i[0][0]+3,i[0][1]+3),outline='red',fill="red")mark_points[index]=(i[0],mark)index+=1
標記點管理
僅僅有十字線坐標定位是不夠的,有時候,我們需要記錄幾個關鍵標記點,比如窗口大小對角點、預留控件位置等等。在新TinUIXml編輯器中,我們可以通過另一個窗口,來管理這些標記點。
窗口布局marks.xml
:
<!--標記點控制窗口-->
<tinui>
<line y='14' anchor='w'><paragraph text='鼠標右鍵單擊確立標記點'></paragraph><button2 text='刪除標記點' command='self.funcs["del_mark"]'></button2>
</line>
<line><listbox data='("TinUIXml編輯器標記點",)' width="365" height="535" command='self.funcs["sel_mark"]'>listbox</listbox>
</line>
</tinui>
這個窗口中的listbox
列表框,就是我們的主要交互方式,我們也需要保持mark_points
列表與列表框的同步更新。
now_mark=None#mark_index
def open_markw(e):markw.deiconify()
def del_mark(e):#刪除選定標記點global now_markif now_mark==None:returnlistbox.delete(now_mark)displayui.delete(mark_points[now_mark][1])del mark_points[now_mark]now_mark=None
def sel_mark(name):#選定標記點global now_markif name.index==0:now_mark=Noneelse:if now_mark!=None:displayui.itemconfigure(mark_points[now_mark][1],outline='red',fill="red")now_mark=name.indexdisplayui.itemconfigure(mark_points[now_mark][1],outline='blue',fill='blue')
def __set_mark(x,y):mark=displayui.create_oval((x,y,x+3,y+3),outline='red',fill="red")mark_points.append(((x,y),mark))listbox.add(f'({x} , {y})')
def set_mark(e):#繪制標記點__set_mark(e.x,e.y)
注意其中每次
listbox
的變動,都要伴隨mark_points
的變動,UI展示區域則視情況而定。
至此,完成一個新的TinUIXml簡易編輯器。