飞道的博客

程序员告诉你七个 Python 必备的 GUI 库,这次一定要学会!

240人阅读  评论(0)

GUI(图形用户界面),顾名思义就是用图形的方式,来显示计算机操作的界面,更加方便且直观。

与之相对应的则是CUI,就是常见的Dos命令行操作,需要记忆一些常用的命令,对于普通人而言,操作起来学习难度还是蛮高的。

01. PyQt5

PyQt5由Riverbank Computing开发。基于Qt框架构建,是一个跨平台框架,可以给各种平台创建应用程序,包括:Unix、Windows、Mac OS。

PyQt将Qt和Python结合在一起。它不只是一个GUI工具包。还包括了线程,Unicode,正则表达式,SQL数据库,SVG,OpenGL,XML和功能完善的Web浏览器,以及许多丰富的GUI小部件集合。

使用pip安装一下。

安装成功后,来个Hello Word简单示例。

结果如下。

 

文档地址:

https://riverbankcomputing.com/software/pyqt/intro

教程链接:

https://www.guru99.com/pyqt-tutorial.html

02. Tkinter

Tkinter是Python中最受欢迎的GUI库之一。由于它简单易学的语法,成为GUI开发初学者的首选之一。

Tkinter提供了各种小部件,例如标签,按钮,文本字段,复选框和滚动按钮等。

支持Grid(网格)布局,由于我们的程序大多数都是矩形显示,这样即使是复杂的设计,开发起来也变得简单些。 

现在我要把这个 data 快速转成 User 组成的数组,变成这样:

你会怎么来实现?

或者我有了上面的列表内容,想要转成一个 JSON 字符串,变成这样

你又会怎么操作呢?

另外如果 JSON 数据里面有各种各样的脏数据,你需要在初始化时验证这些字段是否合法,另外 User 这个对象里面 name、age 的数据类型不同,如何针对不同的数据类型进行针对性的类型转换,这个你有更好的实现方案吗?

初步思路

之前我介绍过 attrs 和 cattrs 这两个库,它们二者的组合可以非常方便地实现对象的序列化和反序列化。

譬如这样:

from attr import attrs,

  
  1. from cattr import structure, unstructure
  2. @attrs
  3. class User(object):
  4. name = attrib()
  5. age = attrib()
  6. data = {
  7. 'name': 'Germey',
  8. 'age': 23
  9. }
  10. user = structure(data, User)
  11. print( 'user', user)
  12. json = unstructure(user)
  13. print( 'json', json)

运行结果:


  
  1. user User(name= 'Germey', age=23)
  2. json { 'name': 'Germey', 'age': 23}

好,这里我们通过 attrs 和 cattrs 这两个库来实现了单个对象的转换。

首先我们要肯定一下 attrs 这个库,它可以极大地简化 Python 类的定义,同时每个字段可以定义多种数据类型。

但 cattrs 这个库就相对弱一些了,如果把 data 换成数组,用 cattrs 还是不怎么好转换的,另外它的 structure 和 unstructure 在某些情景下容错能力较差,所以对于上面的需求,用这两个库搭配起来并不是一个最优的解决方案。

另外数据的校验也是一个问题,attrs 虽然提供了 validator 的参数,但对于多种类型的数据处理的支持并没有那么强大。

所以,我们想要寻求一个更优的解决方案。

更优雅的方案

这里推荐一个库,叫做 marshmallow,它是专门用来支持 Python 对象和原生数据相互转换的库,如实现 object -> dict,objects -> list, string -> dict, string -> list 等的转换功能,另外它还提供了非常丰富的数据类型转换和校验 API,帮助我们快速实现数据的转换。

要使用 marshmallow 这个库,需要先安装下:

pip3 install marshmallow

好了之后,我们在之前的基础上定义一个 Schema,如下:


  
  1. class UserSchema(Schema):
  2. name = fields.Str()
  3. age = fields.Integer()
  4. @post_load
  5. def make(self, data, **kwargs):
  6. return User(**data)

还是之前的数据:


  
  1. data = [{
  2. 'name': 'Germey',
  3. 'age': 23
  4. }, {
  5. 'name': 'Mike',
  6. 'age': 20
  7. }]

这时候我们只需要调用 Schema 的 load 事件就好了:


  
  1. schema = UserSchema()
  2. users = schema.load(data, many= True)
  3. print(users)

输出结果如下:

[User(name='Germey', age=23), User(name='Mike', age=20)]

这样,我们非常轻松地完成了 JSON 到 User List 的转换。

有人说,如果是单个数据怎么办呢,只需要把 load 方法的 many 参数去掉即可:


  
  1. data = {
  2. 'name': 'Germey',
  3. 'age': 23
  4. }
  5. schema = UserSchema()
  6. user = schema.load( data)
  7. print(user)

输出结果:

User(name='Germey', age=23)

当然,这仅仅是一个反序列化操作,我们还可以正向进行序列化,以及使用各种各样的验证条件。

下面我们再来看看吧。

更方便的序列化

上面的例子我们实现了序列化操作,输出了 users 为:

[User(name='Germey', age=23), User(name='Mike', age=20)]

有了这个数据,我们也能轻松实现序列化操作。

序列化操作,使用 dump 方法即可


  
  1. result = schema.dump(users, many= True)
  2. print( 'result', result)

运行结果如下:

result [{'age': 23, 'name': 'Germey'}, {'age': 20, 'name': 'Mike'}]

由于是 List,所以 dump 方法需要加一个参数 many 为 True。

当然对于单个对象,直接使用 dump 同样是可以的:


  
  1. result = schema. dump(user)
  2. print( 'result', result)

运行结果如下:

result {'name': 'Germey', 'age': 23}

这样的话,单个、多个对象的序列化也不再是难事。

经过上面的操作,我们完成了 object 到 dict 或 list 的转换,即:


  
  1. object <-> dict
  2. objects <-> list

验证

当然,上面的功能其实并不足以让你觉得 marshmallow 有多么了不起,其实就是一个对象到基本数据的转换嘛。但肯定不止这些,marshmallow 还提供了更加强大啊功能,比如说验证,Validation。

比如这里我们将 age 这个字段设置为 hello,它无法被转换成数值类型,所以肯定会报错,样例如下:


  
  1. data = {
  2. 'name': 'Germey',
  3. 'age': 'hello'
  4. }
  5. from marshmallow import ValidationError
  6. try:
  7. schema = UserSchema()
  8. user, errors = schema.load(data)
  9. print(user, errors)
  10. except ValidationError as e:
  11. print( 'e.message', e.messages)
  12. print( 'e.valid_data', e.valid_data)

这里如果加载报错,我们可以直接拿到 Error 的 messages 和 valid_data 对象,它包含了错误的信息和正确的字段结果,运行结果如下:


  
  1. e. message { 'age': [ 'Not a valid integer.']}
  2. e. valid_data { 'name': 'Germey'}

因此,比如我们想要开发一个功能,比如用户注册,表单信息就是提交过来的 data,我们只需要过一遍 Validation,就可以轻松得知哪些数据符合要求,哪些不符合要求,接着再进一步进行处理。

当然验证功能肯定不止这一些,我们再来感受一下另一个示例:


  
  1. from pprint import pprint
  2. from marshmallow import Schema, fields, validate, ValidationError
  3. class UserSchema(Schema):
  4. name = fields.Str(validate=validate.Length(min= 1))
  5. permission = fields.Str(validate=validate.OneOf([ 'read', 'write', 'admin']))
  6. age = fields.Int(validate=validate.Range(min= 18, max= 40))
  7. in_data = { 'name': '', 'permission': 'invalid', 'age': 71}
  8. try:
  9. UserSchema().load(in_data)
  10. except ValidationError as err:
  11. pprint(err.messages)

比如这里的 validate 字段,我们分别校验了 name、permission、age 三个字段,校验方式各不相同。

如 name 我们要判断其最小值为 1,则使用了 Length 对象。permission 必须要是几个字符串之一,这里又使用了 OneOf 对象,age 又必须是介于某个范围之间,这里就使用了 Range 对象。

下面我们故意传入一些错误的数据,看下运行结果:


  
  1. { 'age': [ 'Must be greater than or equal to 18 and less than or equal to 40.'],
  2. 'name': [ 'Shorter than minimum length 1.'],
  3. 'permission': [ 'Must be one of: read, write, admin.']}

可以看到,这里也返回了数据验证的结果,对于不符合条件的字段,一一进行说明。

另外我们也可以自定义验证方法:


  
  1. from marshmallow import Schema, fields, ValidationError
  2. def validate_quantity(n):
  3. if n < 0:
  4. raise ValidationError( 'Quantity must be greater than 0.')
  5. if n > 30:
  6. raise ValidationError( 'Quantity must not be greater than 30.')
  7. class ItemSchema(Schema):
  8. quantity = fields.Integer(validate=validate_quantity)
  9. in_data = { 'quantity': 31}
  10. try:
  11. result = ItemSchema().load(in_data)
  12. except ValidationError as err:
  13. print(err.messages)

通过自定义方法,同样可以实现更灵活的验证,运行结果:

{'quantity': ['Quantity must not be greater than 30.']}

对于上面的例子,还有更优雅的写法:


  
  1. from marshmallow import fields, Schema, validates, ValidationError
  2. class ItemSchema(Schema):
  3. quantity = fields.Integer()
  4. @validates('quantity')
  5. def validate_quantity(self, value):
  6. if value < 0:
  7. raise ValidationError( 'Quantity must be greater than 0.')
  8. if value > 30:
  9. raise ValidationError( 'Quantity must not be greater than 30.')

通过定义方法并用 validates 修饰符,使得代码的书写更加简洁。

必填字段

如果要想定义必填字段,只需要在 fields 里面加入 required 参数并设置为 True 即可,另外我们还可以自定义错误信息,使用 error_messages 即可,例如:


  
  1. from pprint import pprint
  2. from marshmallow import Schema, fields, ValidationError
  3. class UserSchema(Schema):
  4. name = fields.String(required= True)
  5. age = fields.Integer(required= True, error_messages={ 'required': 'Age is required.'})
  6. city = fields.String(
  7. required= True,
  8. error_messages={ 'required': { 'message': 'City required', 'code': 400}},
  9. )
  10. email = fields.Email()
  11. try:
  12. result = UserSchema().load({ 'email': 'foo@bar.com'})
  13. except ValidationError as err:
  14. pprint(err.messages)

默认字段

对于序列化和反序列化字段,marshmallow 还提供了默认值,而且区分得非常清楚!如 missing 则是在反序列化时自动填充的数据,default 则是在序列化时自动填充的数据。

例如:


  
  1. from marshmallow import Schema, fields
  2. import datetime as dt
  3. import uuid
  4. class UserSchema(Schema):
  5. id = fields.UUID(missing=uuid.uuid1)
  6. birthdate = fields.DateTime(default=dt.datetime(2017, 9, 29))
  7. print(UserSchema().load({}))
  8. print(UserSchema().dump({}))

这里我们都是定义的空数据,分别进行序列化和反序列化,运行结果如下:


  
  1. { 'id': UUID( '06aa384a-570c-11ea-9869-a0999b0d6843')}
  2. { 'birthdate': '2017-09-29T00:00:00'}

可以看到,在没有真实值的情况下,序列化和反序列化都是用了默认值。

这个真的是解决了我之前在 cattrs 序列化和反序列化时候的痛点啊!

指定属性名

在序列化时,Schema 对象会默认使用和自身定义相同的 fields 属性名,当然也可以自定义,如:


  
  1. class UserSchema(Schema):
  2. name = fields.String()
  3. email_addr = fields.String(attribute='email')
  4. date_created = fields.DateTime(attribute='created_at')
  5. user = User('Keith', email='keith@stones.com')
  6. ser = UserSchema()
  7. result, errors = ser.dump(user)
  8. pprint(result)

运行结果如下:


  
  1. { 'name': 'Keith',
  2. 'email_addr': 'keith @stones.com',
  3. 'date_created': '2014 -08 -17T14: 58: 57.600623+ 00: 00'}

反序列化也是一样,例如:


  
  1. class UserSchema(Schema):
  2. name = fields.String()
  3. email = fields.Email(load_from= 'emailAddress')
  4. data = {
  5. 'name': 'Mike',
  6. 'emailAddress': 'foo@bar.com'
  7. }
  8. s = UserSchema()
  9. result, errors = s.load( data)

运行结果如下:


  
  1. { 'name': u'Mike',
  2. 'email': 'foo@bar.com'}

嵌套属性

对于嵌套属性,marshmallow 当然也不在话下,这也是让我觉得 marshmallow 非常好用的地方,例如:


  
  1. from datetime import date
  2. from marshmallow import Schema, fields, pprint
  3. class ArtistSchema(Schema):
  4. name = fields.Str()
  5. class AlbumSchema(Schema):
  6. title = fields.Str()
  7. release_date = fields.Date()
  8. artist = fields.Nested(ArtistSchema())
  9. bowie = dict(name='David Bowie')
  10. album = dict(artist=bowie, title='Hunky Dory', release_date=date(1971, 12, 17))
  11. schema = AlbumSchema()
  12. result = schema.dump(album)
  13. pprint(result, indent=2)

这样我们就能充分利用好对象关联外键来方便地实现很多关联功能。

需要相关python资料的可以通过扫一扫备注【python】

 


转载:https://blog.csdn.net/lh9987/article/details/116673344
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场