当前位置:   article > 正文

tkinter-TinUI用xml编写界面-超详细

tinui

一、背景

可拓展标记语言的流行

现在,很多界面库,比如wpf、winui都在使用xml(xaml)作为一种界面载入的重要实现方式。使用XML作为界面编写语言,有很多优点:

  1. 逻辑代码和界面设计分离
  2. 与语言无关
  3. 显示内容更改后可以不需要考虑重新布局
  4. 层次直观
  5. 动态渲染,也可静态

在tkinter的诸多拓展中,也有一些使用xml作为布局依据的,但我忘了其名称。但是,这些tkinter的xml载入功能都是从逻辑方面封装了tkinter的原生布局模式:pack, grid, place,所以,这些很难借鉴到TinUI中。当然这很好,只不过相比较而言tkinter使用Python代码编写布局就够了,无论是手动设计界面还是使用可视化设计器,生成的Python布局代码完全可以直接使用。

TinUI的布局方式单一

接着上面所说,以往的TinUI,因为基于tkinter画布,所以只能够通过编写者输入坐标参数来确定组件绘制的原点。这种布局方式相当于tkinter原生中的place,可是place布局可以使用百分制(相对)坐标作为参数,可是因为画布范围的不确定性,只能够使用确定的坐标。

在TinUI中使用确定坐标布局,就会带来一些麻烦:

  • 需要一遍遍调试到满意的坐标位置
  • 一旦更改某一个显示内容,就可能需要重新调试

别看只有这两条,看看TinUI源码中的示例:

b=TinUI(a,bg='white')
b.pack(fill='both',expand=True)
m=b.add_title((600,0),'TinUI is a test project for futher tin using')
m1=b.add_title((0,680),'test TinUI scrolled',size=2,angle=24)
b.add_paragraph((20,290),'''     TinUI是基于tkinter画布开发的界面UI布局方案,作为tkinter拓展和TinEngine的拓展而存在。目前,TinUI已可应用于项目。''',
angle=-18)
b.add_paragraph((20,100),'下面的段落是测试画布的非平行字体显示效果,也是TinUI的简单介绍')
b.add_button((250,450),'测试按钮',activefg='white',activebg='red',command=test,anchor='center')
b.add_checkbutton((80,430),'允许TinUI测试',command=test1)
b.add_label((10,220),'这是由画布TinUI绘制的Label组件')
b.add_entry((250,330),350,'这里用来输入')
b.add_separate((20,200),600)
b.add_radiobutton((50,480),300,'sky is blue, water is blue, too. So, what is your heart',('red','blue','black'),command=test1)
b.add_link((400,500),'TinGroup知识库','http://tinhome.baklib-free.com/')
_,ok1,_=b.add_waitbar1((500,220),bg='#CCCCCC')
b.add_button((500,270),'停止等待动画',activefg='cyan',activebg='black',command=test2)
bu1=b.add_button((700,200),'停止点状滚动条',activefg='white',activebg='black',command=test3)[1]
bu2=b.add_button((700,250),'nothing button 2')[1]
bu3=b.add_button((700,300),'nothing button 3')[1]
b.add_labelframe((bu1,bu2,bu3),'box buttons')
_,_,ok2,_=b.add_waitbar2((600,400))
b.add_combobox((600,550),text='你有多大可能去珠穆朗玛峰',content=('20%','40%','60%','80%','100%','1000%'))
b.add_button((600,480),text='测试进度条(无事件版本)',command=test4)
_,_,_,progressgoto,_,_=b.add_progressbar((600,510))
b.add_table((180,630),data=(('a','space fans over the\nworld','c'),('you\ncan','2','3'),('I','II','have a dream, then try your best to get it!')))
b.add_paragraph((300,850),text='上面是一个表格')
b.add_onoff((600,100))
b.add_spinbox((680,100))
b.add_scalebar((680,50),command=test5)
scale_text,_=b.add_label((890,50),text='当前选值:2')
b.add_info((680,140),info_text='this is info widget in TinUI')
mtb=b.add_paragraph((0,720),'测试菜单(右键单击)')
b.add_menubar(mtb,cont=(('command',print),('menu',test1),'-',('TinUI文本移动',test)))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

一旦改起来,十分之难受。

TinUI的绝对坐标比较难用

作为TinUI的主要开发者,我自己在写示例的时候(也就是\test中的文件),都需要从以前写好的布局代码中copy过来,再进行删改。可想而知,我自己是由多么难受。

综上,TinUI迫切需要能够使用XML进行布局的方式。

二、TinUI能否使用xml布局

原理层面

这必然是可以的,在TinUI-2.6.0-版本中,我加入了TinUIXml类,允许使用xml进行布局。但是写到这一千多字了,关于TinUIXml的开发之后再说,本文只讲述使用xml编写TinUI界面的具体方式。

我不知道其它库实现的原理如何,TinUI的原理很好解释,这不是重点,放到以后再说。

TinUIXml类

使用类:

x=TinUIXml(ui:Union[BasicTinUI,TinUI])
'''
ui::TinUI类 或 BasicTinUI类
'''
  • 1
  • 2
  • 3
  • 4

编写这篇文章时可用的函数:

TinUIXml.loadxml(xml:str)#载入xml字符串,渲染界面
TinUIXml.clean()#清空界面
  • 1
  • 2

看上去很简单是吧,但是还大可讲解,不妨请接着往下看。

使用xml编写界面后的感受

相比于使用TinUI自带的绝对坐标布局,xml给人带来的感觉是更加简便、易维护,并且还可以使一个程序的界面设计和逻辑代码进行一个分离。即使是像我这种又要写Python后端又要完成UI显示的学生,使用TinUIXml来进行设计也让我“神清气爽”。

很多人感觉使用xml脱离了一种语言的特性,但是有些感受确实是要切身体会。

三、TinUI使用xml的简单示例

不用多么复杂,就用下图,很简单的窗口:

在这里插入图片描述

使用TinUI绝对布局

首先我们看到了这是一个全按钮的界面,其中三个按钮在第一行,显示“two2”的按钮在第二行。

请先允许我这么分析,使用xml编写TinUI界面时不能如此分析。两个“two”按钮实际上是一个整体布局,但是绝对布局可以忽略这些。

以下代码中,u为TinUI。

第一次尝试

随便按照脑中所想,输入绝对坐标:

u.add_button((5,5),text='one')
u.add_button((85,5),text='two1')
u.add_button((85,40),text='two2')
u.add_button((170,5),text='three')
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

额,无论上下间隔,都隔得有点开。

第二次尝试

缩小间距:

u.add_button((5,5),text='one')
u.add_button((45,5),text='two1')
u.add_button((45,30),text='two2')
u.add_button((90,5),text='three')
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

好像,有点挤……

第三次尝试

综合考虑,应该是这样:

u.add_button((5,5),text='one')
u.add_button((55,5),text='two1')
u.add_button((55,40),text='two2')
u.add_button((110,5),text='three')
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

终于,有模有样了。

但是,使用TinUIXml布局的我此刻不屑地呲了一下。

使用xml布局

首先分析一下:这个界面实际上只有一行,只不过第二块纵行有两个按钮。

关于我为什么这么分析,还要看下一个小节。

编写xml:

<tinui>
	<line>
		<button text='one'></button>
		<line>
			<button text='two1'></button>
		</line>
		<line>
			<button text='two2'></button>
        </line>
		<button text='three'></button>
	</line>
</tinui>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述
搞定。

不服?

问:使用绝对布局就进行了三次尝试,有什么大不了的吗?

答:确实,这样的一个简单界面试几次就可以了,但不说特别复杂,就如下这个界面。

在这里插入图片描述

答:你试试?

问:……

答:我只需要一个ui.xml

<tinui>
    <line x='512'>
        <label text='输入一个成语开始游戏:' font='宋体 48' outline='#f0f0f0' anchor='n'>tlabel</label>
    </line>
    <line x='10'>
        <entry width='940' fg='blue' activebg='powderblue' font='微软雅黑 64'>entry</entry>
    </line>
    <line x='512'>
        <button text='确认成语' font='宋体 32' fg='cyan' command='self.funcs["findword"]' anchor='n'></button>
    </line>
    <line x='512'>
        <button text='失败退出' font='宋体 32' fg='red' command='self.funcs["closewindow"]' anchor='n'></button>
    </line>
    <line x='70'>
        <button text='关于编写' font='宋体 18' command='self.funcs["gybx"]'></button>
        <button text='版本说明' font='宋体 18' command='self.funcs["bbsm"]'></button>
        <button text='词库信息' font='宋体 18' command='self.funcs["ckxx"]'></button>
        <button text='词库输入' font='宋体 18' command='self.funcs["cksr"]'>cy-input</button>
        <button text='成语查询' font='宋体 18' command='self.funcs["check_web"]'>cy-web</button>
        <button text='检测帮助' font='宋体 18' command='self.funcs["find_chengyu"]'>cy-help</button>
        <button text='添加词库' font='宋体 18' command='self.funcs["find_newciku"]'></button>
        <button text='开源项目' font='宋体 18' command='self.funcs["find_kaiyuan"]'></button>
    </line>
</tinui>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

答:so easy~

问:Oh, I see!

篇中小结

看了那么多使用感受,是不是已经迫不及待使用xml来给TinUI编写界面呢?使用起来确实是一个字:爽,但是无规矩不成方圆。

接下来讲讲使用xml给TinUI写界面的规矩。

四、使用xml编写TinUI的规则

不支持的组件

因为TinUI中一些组件绘制的特殊性,以下组件不支持使用xml布局:

  • info - 提示框
  • menubar - 菜单
  • labelframe - 标题边框
  • tooltip - 提示窗口

具体不可使用xml的组件见TinUI源码中TinUIXml.noload的值。

TinUIXml的基础属性

self.funcs={}用于存放xml中涉及到的函数,通常使用在command参数中。

xml中可以使用写法:command='self.funcs["func_name"]'表示。

在Python中用:x.funcs["func_name"]=your_function定义。

注意xml中使用self,Python中使用变量名

这个性质可以使界面设计不受逻辑代码更改影响。

self.datas={}用于存放xml中涉及到的数据等结构类型,比如宽度、尺寸、列表内容集合、选值区域或选值内容等。

在xml和Python中的使用同【funcs】。

self.tags={}存放内部组件tag集合,用于有目标文本的xml-TinUI组件元素的回调。

如xml中<button text='test'>bu</button>

Python中可以使用button1=x.tags['bu']获取原组件返回值。

基本规定

使用TinUIXml中的xml布局,要满足如下规定:

  1. 根元素必须是<tinui>

  2. 行元素必须是<line>

  3. 根元素的直接子集不能有除了行元素的其它元素

  4. 行元素可以嵌套。这个内容稍后会详细讲解

  5. 所有xml使用的函数,需要表述为self.funcs[...],注意是self

  6. 若需要,如【5】中类似地使用self.datas[...]

  7. 若需要使用整数定义宽度参数等,也如同width='200'使用

  8. 字体使用如font="微软雅黑 12"的写法

基本语法

看下面的图示和解析:

在这里插入图片描述

根元素

使用<tinui>作为直接根元素,否则TinUIXml是不会进行解析的。

直接子元素

直接子元素是直属于<tinui>下的第一节点,只能够由若干个<line>元素组成,表示最终渲染中TinUI界面的每一行。

行元素

使用<line>作为行元素,表示该区域内容的一行内容。

注意,是该区域,不是整个界面

完整写法:<line x='?' y='?'>。表示该行的起始位置,默认为自动接至上一个渲染组件的位置。比如上几张图中成语接龙的输入框等组件的使用。

一般只需要写<line>即可。

行元素的含义请看下一个小节。

组件元素

TinUIXml允许使用xml绘制的所有组件。

使用<widget_name **kw></widget_name>表示组件元素,其中,widget_name为TinUI所有add_xxx函数中的xxx,表示组件名称。

组件元素的含义请看下一个小节。

组件返回值-键值

为了给逻辑代码提供组件控制接口,TinUIXml设计了self.tags作为存放这些值的专用字典,这些字典可被Python直接调用。

组件元素的文本内容表示了该组件被创造时所有的返回值对应的键值,该键值也就包含了各组件的所有返回值。

比如<button text='two1'>two1</button>中,键值“two1”包括了组成按钮的所有画布对象、可用函数列表以及组件uid。在Python中,使用bt1=x.tags['two1']即可获取所有返回值。

嵌套行元素

在一个行元素中嵌入了一个或若干个新<line>元素,这些行元素代表着父行元素下一个纵行的一行渲染范围。

行元素是可以继续嵌套的。

嵌套行元素的含义请看下一个小节。

以上是在单个行元素中的分析,在根目录下任意行元素都如此。

界面与逻辑代码分离设计

为了实现真正的前后端分离,可以参考以下内容来设计程序编写区域或模块。

我们以成语接龙界面作为例子。

使用xml之前-接口输入

在使用xml之前,需要定义一系列的funcs、datas,也就是从逻辑代码中获取界面展示所需要的所有信息。

这一步,我称之为接口输入

xui.funcs['findword']=findword
xui.funcs['closewindow']=closewindow
xui.funcs['gybx']=gybx
xui.funcs['bbsm']=bbsm
xui.funcs['ckxx']=ckxx
xui.funcs['cksr']=cksr
xui.funcs['check_web']=check_web
xui.funcs['find_chengyu']=find_chengyu
xui.funcs['find_newciku']=find_newciku
xui.funcs['find_kaiyuan']=find_kaiyuan
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可能有人认为,如果写一个复杂界面,那么这一部分的代码实在是太多了。

确实如此,但是,如果使用一个统一的导入方法,则会遇到以下问题:

  1. 标识命名必须要遵循python命名规范
  2. 指代不清晰
  3. 更改时可能只会更改源码文件,忘记更改ui.xml

最有直接影响的就是第三点,按照上面的代码,只需要更改源码文件。

但是尽管如此,我还是提供了方法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/83558
推荐阅读