3行代码,为“任意”Python程序生成GUI界面!

文章介绍了PyGUIAdapter,一个Python库,能将任何Python函数快速转化为GUI应用,无需编写复杂的GUI代码。它基于PyQT6,提供易用的接口和丰富的自定义选项,使得开发者能轻松为命令行程序添加图形界面。
摘要由CSDN通过智能技术生成

PyGUIAdapter:一个将“任意”Python程序转换为GUI应用的库

一、背景

在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以及为什么没有选择它

Gooey 是一个python库,目前在 github上20.3K star,是一个比较受欢迎的项目。

它使用wxWidgets作为底层GUI框架,可以将几乎所有python命令行程序转换为GUI程序,它生成的界面如下图所示:

bV3tWrjI5TW4CnmB0PP9Sp-0Eh-xgGpwssUBlev1FbI.png

它的原理简单来说,就是把命令行解析器(argparse)解析到的命令行参数转换成对应的输入控件,然后通过这些控件,接受用户的输入。它的使用也确实足够简单,有时候一个装饰器@Gooey就解决了问题。

但是,我最后还是没有选择使用Gooey,这里有几个原因:一是在我的机器上Gooey生成的界面总感觉有些卡卡的(不知道是不是因为wxWidgets在Windows平台上有些“水土不服”);二是在高分屏上,Gooey界面上的文字总是有些模糊(找了一圈没找到开启high dpi的接口);三是Gooey这个库好久没有更新了,它的最后一次提交已经是在两年前。

除了上面这几个原因,还有一个很重要的因素,那就是,Gooey帮我们摆脱了GUI代码不假,但我们还是要写argparse代码。

不想写GUI代码≠想写命令行代码(嘿嘿,公式做题就是快)

那么,有没有那么一种可能,我是说可能,存在那么一种东西,可以歘的一下,把任意一个函数转换成图形界面,它的参数变成了输入控件,点一下按钮,就可以运这个函数。如果存在这样的东西,那么不就解决了我们既不想写GUI代码,也不想写命令行代码的矛盾了吗?

很遗憾的是,我并没有发现这样的存在。既然如此,本着没有轮子,那就自己造一个的思路,我开启了一个新的项目——PyGUIAdapter

三、自己手搓一个吧:PyGUIAdapter的诞生

从功能上看,PyGUIAdapter`和`Gooey有些类似,但在原理上,二者存在很大区别。

如上文所讲,Gooey是面向命令行的,它主要是做了把命令行参数转化为输入控件的工作。

PyGUIAdapter从一开始就是面向函数的。我想,既然都打算使用图形界面了,那么干嘛还需要argparse这个中间商赚差价呢。直接把要实现的功能封装成函数,把用户输入对应为函数的参数不就行了吗。这样,我们只需要解析函数,提取它的参数,然后生成对应的界面控件就可以了,是不是非常简单呢?

为了实现从函数到控件的映射,我另写了一个库function2widgets,它是PyGUIAdapter的基础,主要的功能就是从函数签名和函数的文档字符串中提取信息,通过一系列规则,为函数每个参数生成对应的控件。它默认从函数参数的类型推断其对应的输入控件,如:

参数类型控件类型
intIntLineEdit
boolCheckBox
floatFloatLineEdit
strLineEdit
listListEditor
dictDictEditor
tupleTupleEditor
datetimeDateTimeEdit
dateDateTime
timeTimeEdit
LiteralComboBox
AnyJsonEditor

除了以上控件,function2widgets还实现了许多其他控件,如Dial、Slider、FilePathEdit、DirPathEdit、CheckBoxGroup、RadioButtonGroup、PlainTextEdit等等。

PyGUIAdapter在设计之初就考虑到了扩展性和灵活性的问题,我们既可以依赖内置的规则,由function2widgets库自动推导函数参数所对应的控件类型;我们也可以通过一些方法,手动指定参数的控件类型,同时配置控件的属性,如我们可以手动设置LineEdit的占位符文本(placeholder)、FilePathEdit的文件名过滤器(filters)等等,function2widgets实现的每种控件都有大量可配置的属性。

对了,PyGUIAdapterGooey的另一个区别是,PyGUIAdapter基于PyQT6,通过它生成的界面对high dpi更加友好,而且从流畅度上看,PyQT6似乎也要更好一些(至少在我的机器上是这样的)。

四、PyGUIAdapter的基本使用

PyGUIAdapter的使用非常简单,最少只需要三行代码就可以把一个python函数转换为GUI应用。

下面,是一个简单的使用指南。

1.安装

从pypi安装PyGUIAdapter最新版本:

使用pip:

pip install pyguiadapter

使用poetry:

poetry add pyguiadapter

2. 将需要提供给用户的功能封装成一个函数

假设我们有这么一个函数,我们忽略它的具体功能,我们只需注意到,它需要输入4个参数,每个参数都用类型标注语法标注了参数类型。

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界面

from pyguiadapter.adapter.adapter import GUIAdapter
gui_adapter = GUIAdapter()
gui_adapter.add(create_file)
gui_adapter.run()

完整代码如下:

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()


运行这个程序,我们就得到了以下界面:

otrxu3LpOcbzsQQseJoDBB34FyVQYR5taVxFEpHUsEA.png

是不是非常简单,我们没有写一行gui代码和argparse就得到了一个不错的gui界面。

4.一些常用的自定义方法

当然,你可能会提出,根据语义,path使用一个专门编辑路径的控件更合适,content用一个多行文本控件会更好,每个参数名称如果可以自定义就好了,如果有详细的说明就更好…

这一切,都是可以实现的,这里提供一种常用的自定义方法(更多自定义的选项和方法会有单独的文章进行介绍,也可以直接阅读PyGUIAdapter仓库中examples/下的示例源代码,这些示例几乎涵盖了PyGUIAdapter使用的每一个方面)。

为了进行自定义配置,我们需要借助函数的文档字符串,就是函数体开头使用三个引号包裹起来的多行字符串:

def foo():
    """                 
    这里就是函数foo的文档字符串
    """
    pass

PyGUIAdapter通过文档字符串中@widgets和@end标记包裹起来的一段toml 格式的文本来对参数的控件进行配置

比如要指定path参数的控件类型和控件属性等,可以像下面这样做:

"""
...
@widgets
# 要应用自定义配置的参数的名称
[path]
# 指定控件类型
widget_class="DirPathEdit"
# 指定界面上显示的参数的名称
label="文件保存目录"
# 指定参数的描述
description="请选择生成的文件的保存目录"
# 配置控件的其他属性
# 具体可配置的属性取决于控件的类型,可参考function2widgets下的XXXArgs类,其中XXX为控件的类名
# 例如DirPathEdit对应的就是DirPathEidtArgs类
placeholder="选择文件保存的目录"
start_dir="./"
....
@end
"""

除了可以在@widgets和@end块中指定参数的描述文本,PyGUIAdapter还会从 ReST、Google、Numpydoc-style以及Epydoc风格的文档注释中提取参数的描述信息,如:

def foo(a: int):
    """
    :param a: 这是参数a                  
    """

下面,我们来改造一下上面的例子,使得生成的界面用户体验更好:

import os.path

def create_file(
    path: str,
    filename: str,
    content: str,
    overwrite: bool = True,
):
    """
    这是一个演示程序,用于演示<b>PyGUIAdapter</b>的功能,这段文字会被提取为函数的Document并显示在界面上。

    :param path: <b>生成文件的保存的目录,若为空则保存到当前路径下</b>
    :param filename: <b>生成文件的文件名称。注意:<font color=red>不可为空!</font></b>
    :param content: <b>生成文件的内容</b>
    :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()


经过配置,界面变成了下面这个样子,相比之前,用户体验提升了不少:

aEDnVfxxewgppTkFpirw7pXOTiRCqLFY6Eq5lpXZtgk.png

create_file() 函数文档字符串中的描述也被正确提取出来,显示在Document区域中:

ykBF-LI-ROzBYeSlv_RiRGiizhJ0eldetKJd3BzJt9c.png

现在,可以在控件内填入参数,然后点击Execute按钮运行这个函数了:

image.png

image.png

可以看到,函数内的异常也被正确地捕获,并通过对话框的方式提示给用户了,没错,这就是PyGUIAdapter进行参数校验的方式,对于不符合要求的参数,直接在函数内部抛出异常就可以了,非常符合我们写程序的直觉。

五、小结

PyGUIAdapter的使用非常简单,但是提供的功能和可配置的选项非常丰富,上面本文提到的那些仅仅是非常小的一部分,除了这些,你可以:

  1. 自定义窗口和控件的外观、样式、图标、文字等
  2. 自定义对话框的显示与否及其内容
  3. 将一个函数配置为可取消的函数并显示一个取消按钮
  4. 配合qt-material等第三方库进行界面美化
  5. 添加菜单和工具栏
  6. 添加多个函数并显示函数选择界面
  7. 在函数中弹出对话框、输入框
  8. 在函数中向Output区域打印文字

这些更高级的内容,如果后续有时间的话,将写一些文章单独讲讲,当然,也可以查看examples/ 示例代码,这些示例几乎涵盖了上面列举的全部主题。

最后,如果你喜欢这个库的话,麻烦给个star,谢谢啦。后续,我会持续维护这个库,如果有什么改进的建议,也欢迎通过issue的方式提出。

关于Python学习指南

学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

包括:Python激活码+安装包、Python web开发,Python爬虫,Python数据分析,人工智能、自动化办公等学习教程。带你从零基础系统性的学好Python!

👉Python所有方向的学习路线👈

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取)

在这里插入图片描述

👉Python学习视频600合集👈

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

在这里插入图片描述

温馨提示:篇幅有限,已打包文件夹,获取方式在:文末

👉Python70个实战练手案例&源码👈

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

👉Python大厂面试资料👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

在这里插入图片描述

在这里插入图片描述

👉Python副业兼职路线&方法👈

学好 Python 不论是就业还是做副业赚钱都不错,但要学会兼职接单还是要有一个学习规划。

在这里插入图片描述

👉 这份完整版的Python全套学习资料已经上传,朋友们如果需要可以扫描下方CSDN官方认证二维码免费领取保证100%免费

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是一个简单的 Python 图形界面万年历示例: ```python import calendar import tkinter as tk class CalendarGUI: def __init__(self, master): self.master = master self.master.title("万年历") self.year = tk.StringVar() self.month = tk.StringVar() self.yearentry = tk.Entry(self.master, textvariable=self.year) self.yearentry.pack(side=tk.LEFT) self.monthentry = tk.Entry(self.master, textvariable=self.month) self.monthentry.pack(side=tk.LEFT) self.showbutton = tk.Button(self.master, text="显示", command=self.show_calendar) self.showbutton.pack(side=tk.LEFT) self.quitbutton = tk.Button(self.master, text="退出", command=self.master.quit) self.quitbutton.pack(side=tk.LEFT) def show_calendar(self): year = int(self.year.get()) month = int(self.month.get()) cal = calendar.monthcalendar(year, month) self.calendar = tk.Toplevel(self.master) self.calendar.title("万年历") for week in cal: for day in week: daystring = str(day) if day != 0 else "" tk.Label(self.calendar, text=daystring, width=4, height=2, relief="groove").grid(row=cal.index(week), column=week.index(day)) root = tk.Tk() calendar_gui = CalendarGUI(root) root.mainloop() ``` 这个示例使用了 tkinter 库来创建图形用户界面,使用了 calendar 库来生成万年历数据。在窗口中输入年份和月份,点击“显示”按钮即可在新窗口中显示该月份的万年历。 ### 回答2: 图形界面万年历Python是一个使用Python编程语言开发的日历应用程序,它具有用户友好的图形界面,在计算机上提供方便的日历功能。 该程序的主要功能包括: 1. 显示当前日期和时间:程序会自动获取当前的系统日期和时间,并在界面上显示出来,用户可以一目了然地了解当前的日期和时间。 2. 显示每月的日历:用户可以选择年份和月份,程序生成相应月份的日历,并将其显示在界面上。用户可以通过点击月份或年份的下拉列表选择特定的月份或年份,方便查看不同的日期。 3. 提供农历信息:除了公历日期,该程序还提供农历日期的显示功能。用户在查看日历时,可以选择显示公历或农历日期,以满足不同用户的需求。 4. 周数显示:程序可以根据用户选择的日期,自动计算并显示对应的周数,方便用户快速确定所在周。 5. 重要节日提醒:该程序会在界面上显示一些常见的重要节日,并在该节日的日期上做出特殊标识,提醒用户注意。用户也可以自定义特殊日期,并在日历上添加标志。 6. 其它功能:该程序还提供一些额外的功能,如显示每天的天气预报、添加备忘录功能等。 使用Python编程语言开发图形界面万年历,可以使得用户在使用过程中更加方便、直观,不需要通过命令输入命令,而是通过点击界面上的按钮和选择框等进操作。图形界面的设计可以根据用户的审美和使用习惯进自定义,使得用户可以更好地适应和使用该万年历应用程序。编程过程中,可以使用Python的图形界面库如Tkinter、PyQT等来实现界面的设计和交互功能的开发。 ### 回答3: 万年历是一个用于显示和管理时间的工具。在图形界面中使用Python编写一个万年历程序,可以使用户简单地查看任意年份和月份的日历。 首先,使用Python的图形界面库(如Tkinter)创建一个窗口。窗口中包含一个输入框用于输入年份和月份,还有一个按钮用于提交查询。 当用户点击提交按钮时,程序会从输入框中获取用户输入的年份和月份。然后,根据输入的年份和月份使用Python的日期和时间库,计算出该月的天数和第一天是星期几。 接下来,程序会在窗口中创建一个表格,用于显示整个月份的日期。程序根据计算得到的结果,依次在表格中填充日期。为了美观和清晰,可以给表格中的周末日期添加特殊的颜色或背景。 同时,程序还可以在表格上方显示当前查询的年份和月份,方便用户查看。另外,可以添加一些按钮用于切换到前一个月份和后一个月份。 最后,当用户点击切换按钮时,程序会根据当前显示的月份的上一个月或下一个月的日期,更新表格中的内容,并更新显示的年份和月份。 通过以上实现,用户可以方便地查看任意年份和月份的日历,实现了一个图形化的万年历程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值