python GUI学习

前言

近来写的python脚本比较多,以自己的随意的性格,那些代码迟早会被自己弄丢的,或者是隔了一段时间,都不知道自己写的是啥了,所以打算写个交互式的界面,帮助自己整理那些代码。顺带学习学习python中GUI的编写,有过MATLAB编写GUI的基础,希望上手能快一些。学习到的内容源自于书籍[1], 书很不错,值得去学习,这里就摘抄一部分自己认为重要的内容。

为Python 添加类型

属性的功能

Traits 库为对象的属性增加了类型定义的功能,此外还提供了如下的额外功能(自己感觉还是比较重要的地方):

初始化:每个 Trait 属性都有自己的默认值。
验证:Trait 属性都有明确的类型定义,只有满足定义的值才能赋给属性。
代理:Trait 属性值可以代理给其他对象的属性。
监听:Trait 属性值发生变化时,可以运行事先指定的函数。
可视化:拥有 Trait 属性的对象可以很方便地生成编辑 Trait 属性的界面。

下面的例子展示了 Trait 属性的上述功能(看的中国慕课里讲的也就是这个例子,一模一样)。

class Parent ( HasTraits ): 
# 初始化: last_name 被初始化为'Zhang'
last_name = Str( 'Zhang' )
class Child ( HasTraits ):
age = Int
# 验证: father 属性的值必须是 Parent 类的实例
father = Instance( Parent )
# 代理: 将 Child 对象的 last_name 属性代理给其 father 属性的 last_name
last_name = Delegate( 'father' )
# 监听: 当 age 属性的值被修改时,下面的函数将被运行
def _age_changed ( self, old, new ):
print 'Age changed from %s to %s ' % ( old, new )

当派生类中定义了init()时,在其中必须调用其父类的init()方法,否则 Trait 属性的一些功能将无效。

轻松制作用户界面

希望快速实现一个够用的界面,让用户能够交互式地处理数据,而又不希望在界面制作上花费过多的精力。以 Traits 库为基础、以 MVC 模式为设计思想的 TraitsUI 库就是实现这一理想的最佳方案。

MVC 的英文全称为 Model-View-Controller,它的目的是实现一种动态的程序设计,简化程序的修改和扩展工作,并且使程序的各个部分能够充分地被重复利用。
Model(模型):程序中存储数据以及对数据进行处理的部分。
View(视图):程序的界面部分,实现数据的显示。
Controller(控制器):起到视图和模型之间的组织作用,控制程序的流程,例如将界面的
操作转换为对模型的处理。

这里运行书中给的第一个例子就报了很多错了,是自己没有安装相关的包导致的,可以通过pip 或者conda安装,这里采用pip的方式安装所需的包

pip3 install traits
pip install traitsui

并将书中的例子改为

from traits.api import HasTraits, Str, Int
class Employee(HasTraits):
name = Str
department = Str
salary = Int
bonus = Int

Employee().configure_traits()

即可出现书中给出的交互式界面了。

没有写一行与界面相关的代码,就能得到一个已经够实用的界面,应该还是很令人满意的。如果想手工控制界面的设计和布局,就需要添加自己的代码了。

定义界面

HasTraits 的派生类用 Trait 属性保存数据,它相当于 MVC 模式中的模型(Model)。当没有指定界面的显示方式时,Traits 库会自动创建一个默认的界面。可以通过视图(View)对象为模型设计更加实用的界面。当然,用书中给的例子,也是通不过编译的,改成如下形式即可。

from traits.api import HasTraits, Str, Int
from traitsui.api import View,Item
class Employee(HasTraits):
name = Str
department = Str
salary = Int
bonus = Int

view = View(
Item('department', label=u"部门", tooltip=u"在哪个部门干活"),
Item('name', label=u"姓名"),
Item('salary', label=u"工资"),
Item('bonus', label=u"奖金"),
title=u"员工资料", width=250, height=150, resizable=True
)
if __name__ == "__main__":
p = Employee()
p.configure_traits()

界面相关的内容都位于 TraitsUI 库中,这里从中载入 View 和 Item。View 是描述界面的视图类,Item 是描述界面中的控件和模型对象的 Trait 属性之间关系的类。除了 Item 之外,TraitsUI 模块还定义了 Item 的几个派生类:Label、Heading 和 Spring。它们只用于辅助界面布局,因此不需要和模型对象的 Trait 属性关联。

同一个模型对象可以通过多个视图对象用不同的界面显示,下面看一个例子:

from traits.api import HasTraits, Str, Int
from traitsui.api import View,Item,Group

g1 = [Item('department', label=u"部门", tooltip=u"在哪个部门干活"),
Item('name', label=u"姓名")]
g2 = [Item('salary', label=u"工资"),
Item('bonus', label=u"奖金")]

class Employee(HasTraits):
name = Str
department = Str
salary = Int
bonus = Int

traits_view = View(
Group(*g1, label=u'个人信息', show_border=True),
Group(*g2, label=u'收入', show_border=True),
title = u"缺省内部视图")
another_view = View(
Group(*g1, label=u'个人信息', show_border=True),
Group(*g2, label=u'收入', show_border=True),
title = u"另一个内部视图")

global_view = View(
Group(*g1, label = u'个人信息', show_border = True),
Group(*g2, label = u'收入', show_border = True),
title = u"外部视图")

p = Employee()
# 使用内部视图 traits_view
p.edit_traits()
# 使用内部视图 another_view
p.edit_traits(view="another_view")
# 使用外部视图 view1
p.configure_traits(view=global_view)

if __name__ == "__main__":
p = Employee()
p.configure_traits()

edit_traits()和 configure_traits()一样,也被用于生成界面,它们的区别在于:edit_traits()显示界面之后不进入后台界面库的消息循环,因此如果直接运行只调用 edit_traits()的程序,界面将在显示之后立即关闭,程序的运行也随之结束。而对于 configure_traits(),将进入消息循环,直到用户关闭所有窗口。因此通常情况下,主界面窗口或模态对话框使用 configure_traits()显示,而无模态窗口或对话框则使用 edit_traits()显示。

用 TraitsUI 库创建的界面可以选择后台界面库,目前支持的有 qt4 和 wx 两种。
在启动程序时添加"-toolkit qt4""-toolkit wx"可选择使用何种界面库生成界面。

在给的例子中,Employee 类用于保存数据,因此它属于 MVC 模式中的模型(Model), 而 View 对象定义了 Employee 的界面显示部分,它属于视图(View)。通过将视图对象传递给模型对象的 configure_traits()方法,将模型对象和视图对象联系起来。在用来定义编辑器的 Item 对象中,不直接引用模型对象的属性,而是通过属性名和模型对象进行联系。这样一来,模型和视图之间的耦合性很弱,只需要属性名匹配,同一个视图对象可以运用到不同的模型对象之上。有时候我们希望模型类知道如何显示它自己,这时可以在模型类的内部定义视图。在模型类中定义的视图可以被其派生类继承,因此派生类能使用父类的视图。在调用 configure_traits()时如果不设置 view 参数,将使用模型对象内部的默认视图对象生成界面。如果在模型类中定义了多个视图对象,默认使用名为 traits_view 的视图对象。

一个模型可以对应多个视图。同样,使用一个视图可以将多个模型对象的数据显示在一个界面窗口中。下面是用一个视图对象同时显示多个模型对象的例子。对于前面的模型类 Employee,我们可以设计如下的复合视图对象 comp_view,它能同时显示两个 Employee 对象:

comp_view = View(
Group(
Group(
Item('p1.department', label=u"部门"),
Item('p1.name', label=u"姓名"),
Item('p1.salary', label=u"工资"),
Item('p1.bonus', label=u"奖金"),
show_border=True
),
Group(
Item('p2.department', label=u"部门"),
Item('p2.name', label=u"姓名"),
Item('p2.salary', label=u"工资"),
Item('p2.bonus', label=u"奖金"),
show_border=True
),
orientation = 'horizontal'
),
title = u"员工对比"
)

接下来,下面的程序生成组成模型对象的两个 Employee 对象——employee1 和 employee2:

employee1 = Employee(department = u"开发", name = u"张三", salary = 3000, bonus = 300) 
employee2 = Employee(department = u"销售", name = u"李四", salary = 4000, bonus = 400)

在显示界面时,使用 context 参数将包含两个模型对象的字典传递给 configure_traits():

HasTraits().configure_traits(view=comp_view, context={"p1":employee1, "p2":employee2})

参考

[1] 《python 科学计算》—— 张若愚等著

CMakeLists Eigen FCPX GNU Gazebo Git Interest KDL Life Linux Matrix ODE ROS Ros UML Ubuntu VcXsrv algorithm algorithms axis-angle bode calibration chrome control cpp data_struct dots figure gdb latex launch life linux mac math matlab memory motor moveit operator optimal algorithm python robot robotics ros ros2 rtb simulation stl thread tools twist urdf velocity vim web work wsl
知识共享许可协议