在Python开发中,GUI程序的开发一直是一个比较难办的问题,为了照顾到那些不习惯使用命令行的用户,我们常常需要为我们的python程序套上一层GUI的壳。Python的GUI框架还算是比较丰富的,有内置的tk,也有像wxWidgets、GTK、QT这样成熟的GUI框架的绑定,借助这些框架,完全可以为任何Python程序构建出合适的甚至是花里胡哨的图形用户界面,但问题在于:如果你是一个GUI方面的新手,学习一种GUI框架无疑将引入巨大的学习成本。很多时候,我们只是想给我们写的小工具套一个图形化的外壳而已,不等于我们真的想去写大量的GUI代码,事实上,手动去处理界面的状态、交互、事件、数据等确实是一件比较折磨人的事情......
那么,有没有一种工具,可以让我们尽可能多地关注功能的实现,而它在背后偷偷地帮帮我们处理好所有(或者是)大部分界面相关的细节,让我们可以“无痛”地为我们的程序套上一层图形界面的“外衣”,(当然,没有那么花里胡哨也可以)。经过一番研究,我发现,嘿还真有。现成的,比较成熟的一个解决方案是Gooey,一个号称可以 “Turn (almost) any Python 3 Console Program into a GUI application with one line” 的Python库。
Gooey 是一个python库,目前在 github上20.3K star,是一个比较受欢迎的项目。
它使用wxWidgets
作为底层GUI框架,可以将几乎所有python命令行程序转换为GUI程序,它生成的界面如下图所示:
它的原理简单来说,就是把命令行解析器(argparse)解析到的命令行参数转换成对应的输入控件,然后通过这些控件,接受用户的输入。它的使用也确实足够简单,有时候一个装饰器@Gooey
就解决了问题。
但是,我最后还是没有选择使用Gooey
,这里有几个原因:一是在我的机器上Gooey生成的界面总感觉有些卡卡的(不知道是不是因为wxWidgets
在Windows平台上有些“水土不服”);二是在高分屏上,Gooey界面上的文字总是有些模糊(找了一圈没找到开启high dpi的接口);三是Gooey这个库好久没有更新了,它的最后一次提交已经是在两年前。
除了上面这几个原因,还有一个很重要的因素,那就是,Gooey
帮我们摆脱了GUI代码不假,但我们还是要写argparse代码。
不想写GUI代码≠想写命令行代码(嘿嘿,公式做题就是快)
那么,有没有那么一种可能,我是说可能,存在那么一种东西,可以歘的一下,把任意一个函数转换成图形界面,它的参数变成了输入控件,点一下按钮,就可以运这个函数。如果存在这样的东西,那么不就解决了我们既不想写GUI代码,也不想写命令行代码的矛盾了吗?
很遗憾的是,我并没有发现这样的存在。既然如此,本着没有轮子,那就自己造一个的思路,我开启了一个新的项目——PyGUIAdapter。
从功能上看,PyGUIAdapter
`和`Gooey
有些类似,但在原理上,二者存在很大区别。
如上文所讲,Gooey
是面向命令行的,它主要是做了把命令行参数转化为输入控件的工作。
而PyGUIAdapter
从一开始就是面向函数的。我想,既然都打算使用图形界面了,那么干嘛还需要argparse
这个中间商赚差价呢。直接把要实现的功能封装成函数,把用户输入对应为函数的参数不就行了吗。这样,我们只需要解析函数,提取它的参数,然后生成对应的界面控件就可以了,是不是非常简单呢?
为了实现从函数到控件的映射,我另写了一个库function2widgets,它是PyGUIAdapter的基础,主要的功能就是从函数签名和函数的文档字符串中提取信息,通过一系列规则,为函数每个参数生成对应的控件。它默认从函数参数的类型推断其对应的输入控件,如:
参数类型
控件类型
int
IntLineEdit
bool
CheckBox
float
FloatLineEdit
str
LineEdit
list
ListEditor
dict
DictEditor
tuple
TupleEditor
datetime
DateTimeEdit
date
DateTime
time
TimeEdit
Literal
ComboBox
Any
JsonEditor
除了以上控件,function2widgets还实现了许多其他控件,如Dial、Slider、FilePathEdit、DirPathEdit、CheckBoxGroup、RadioButtonGroup、PlainTextEdit等等。
PyGUIAdapter在设计之初就考虑到了扩展性和灵活性的问题,我们既可以依赖内置的规则,由function2widgets库自动推导函数参数所对应的控件类型;我们也可以通过一些方法,手动指定参数的控件类型,同时配置控件的属性,如我们可以手动设置LineEdit的占位符文本(placeholder)、FilePathEdit的文件名过滤器(filters)等等,function2widgets实现的每种控件都有大量可配置的属性。
对了,PyGUIAdapter
与Gooey
的另一个区别是,PyGUIAdapter基于PyQT6,通过它生成的界面对high dpi更加友好,而且从流畅度上看,PyQT6似乎也要更好一些(至少在我的机器上是这样的)。
PyGUIAdapter的使用非常简单,最少只需要三行代码就可以把一个python函数转换为GUI应用。
下面,是一个简单的使用指南。
1.安装
从pypi安装PyGUIAdapter最新版本:
使用pip:
pip install pyguiadapter
使用poetry:
poetry add pyguiadapter
2. 将需要提供给用户的功能封装成一个函数
假设我们有这么一个函数,我们忽略它的具体功能,我们只需注意到,它需要输入4个参数,每个参数都用类型标注语法标注了参数类型。
python
代码解读
复制代码
`def create_file(path: str, filename: str, content: str, overwrite: bool = False):
path = os.path.join(path, filename)
if not os.path.isfile(path) or overwrite:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return True
return False`
3.三行代码(忽略import),为这个函数创建GUI界面
python
代码解读
复制代码
`from pyguiadapter.adapter.adapter import GUIAdapter
gui_adapter = GUIAdapter()
gui_adapter.add(create_file)
gui_adapter.run()`
完整代码如下:
python
代码解读
复制代码
`import os.path
def create_file(path: str, filename: str, content: str, overwrite: bool = False):
path = os.path.join(path, filename)
if not os.path.isfile(path) or overwrite:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return True
return False
if name == "__main__":
from pyguiadapter.adapter.adapter import GUIAdapter
gui_adapter = GUIAdapter()
gui_adapter.add(create_file)
gui_adapter.run()`
运行这个程序,我们就得到了以下界面:
是不是非常简单,我们没有写一行gui代码和argparse就得到了一个不错的gui界面。
4.一些常用的自定义方法
当然,你可能会提出,根据语义,path使用一个专门编辑路径的控件更合适,content用一个多行文本控件会更好,每个参数名称如果可以自定义就好了,如果有详细的说明就更好......
这一切,都是可以实现的,这里提供一种常用的自定义方法(更多自定义的选项和方法会有单独的文章进行介绍,也可以直接阅读PyGUIAdapter仓库中examples/下的示例源代码,这些示例几乎涵盖了PyGUIAdapter使用的每一个方面)。
为了进行自定义配置,我们需要借助函数的文档字符串,就是函数体开头使用三个引号包裹起来的多行字符串:
python
代码解读
复制代码
`def foo():
"""
这里就是函数foo的文档字符串
"""
pass`
PyGUIAdapter通过文档字符串中@widgets和@end标记包裹起来的一段toml 格式的文本来对参数的控件进行配置
比如要指定path参数的控件类型和控件属性等,可以像下面这样做:
python
代码解读
复制代码
`"""
...
@widgets
[path]
widget_class="DirPathEdit"
label="文件保存目录"
description="请选择生成的文件的保存目录"
placeholder="选择文件保存的目录"
start_dir="./"
....
@end
"""`
除了可以在@widgets和@end块中指定参数的描述文本,PyGUIAdapter还会从 ReST、Google、Numpydoc-style以及Epydoc风格的文档注释中提取参数的描述信息,如:
python
代码解读
复制代码
`def foo(a: int):
"""
:param a: 这是参数a
"""`
下面,我们来改造一下上面的例子,使得生成的界面用户体验更好:
python
代码解读
复制代码
`import os.path
def create_file(
path: str,
filename: str,
content: str,
overwrite: bool = True,
):
"""
这是一个演示程序,用于演示PyGUIAdapter的功能,这段文字会被提取为函数的Document并显示在界面上。
:param path: 生成文件的保存的目录,若为空则保存到当前路径下
:param filename: 生成文件的文件名称。注意:<font color=red>不可为空!</font>
:param content: 生成文件的内容
:param overwrite:
:return:
@widgets
[path]
widget_class="DirPathEdit"
label="保存路径"
button_text="选择目录"
placeholder="选择文件保存的目录"
[filename]
label="文件名"
placeholder="生成文件的名称"
clear_button=true
[content]
widget_class="PlainTextEdit"
label="文件内容"
placeholder="生成文件的内容"
[overwrite]
label=""
text="是否覆盖现有文件?"
@end
"""
if not path:
path = "./"
if not filename:
raise ValueError("文件名不可为空,请指定文件名称")
path = os.path.join(path, filename)
if not os.path.isfile(path) or overwrite:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return True
return False
if name == "__main__":
from pyguiadapter.adapter.adapter import GUIAdapter
gui_adapter = GUIAdapter()
gui_adapter.add(create_file)
gui_adapter.run()`
经过配置,界面变成了下面这个样子,相比之前,用户体验提升了不少:
create_file() 函数文档字符串中的描述也被正确提取出来,显示在Document区域中:
现在,可以在控件内填入参数,然后点击Execute按钮运行这个函数了:
可以看到,函数内的异常也被正确地捕获,并通过对话框的方式提示给用户了,没错,这就是PyGUIAdapter进行参数校验的方式,对于不符合要求的参数,直接在函数内部抛出异常就可以了,非常符合我们写程序的直觉。
PyGUIAdapter的使用非常简单,但是提供的功能和可配置的选项非常丰富,上面本文提到的那些仅仅是非常小的一部分,除了这些,你可以:
这些更高级的内容,如果后续有时间的话,将写一些文章单独讲讲,当然,也可以查看examples/ 示例代码,这些示例几乎涵盖了上面列举的全部主题。
最后,如果你喜欢这个库的话,麻烦给个star,谢谢啦。后续,我会持续维护这个库,如果有什么改进的建议,也欢迎通过issue的方式提出。
原网址: 访问
创建于: 2024-12-16 12:28:59
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论