WxPython跨平台开发框架之参数配置管理界面的设计和实现

我曾经在2014年在随笔《Winform开发框架之参数配置管理功能实现-基于SettingsProvider.net的构建》介绍过基于.NET开发的参数配置管理界面,本篇随笔基于类似的效果,介绍在WxPython跨平台开发框架上使用LabelBook 控件实现配置管理界面的效果。

1、参数配置管理界面的特点和 .NET 实现回顾

参数配置管理界面的特点主要体现在以下几个方面:

界面按照不同的功能模块或参数类别划分为多个部分,常见形式包括选项卡。

每个模块包含相关参数的设置控件,便于用户快速定位和修改特定设置。

界面逻辑清晰,便于维护。避免信息过于集中,提高可读性。

涉及不同类型的参数,例如布尔值(复选框)、枚举值(下拉菜单)、数值(文本框或滑块)、路径(文件选择器)等。可根据参数的用途选择适当的控件以提高用户体验。

提供保存和加载配置的功能,将配置保存到文件、数据库或云端,便于多次使用和分享。

一个好的参数配置管理界面会综合考虑这些特点,提供高效、直观且安全的操作体验,同时满足系统可扩展性的需求。

随笔《Winform开发框架之参数配置管理功能实现-基于SettingsProvider.net的构建》介绍过基于.NET开发的参数配置管理功能,如下界面所示。

它的实现是通过一个主窗体容器,如下所示,

然后再提供各个面板的整合形成一个多面板的配置管理界面。

2、基于WxPython跨平台开发框架的配置管理界面实现

在WxPython组件模块中,我们可以使用 wx.lib.agw.labelbook 的 LabelBook 控件实现配置管理界面的功能。

LabelBook 是 wxPython AGW 库中的一个增强选项卡控件,类似于标准的 wx.Notebook,但提供了更丰富的外观和功能。使用它来实现配置管理界面有以下优势:

LabelBookwx.lib.agw.labelbook 模块的一部分。

我们来看看一个简单的使用案例,如下代码所示。

复制代码
import wx
import wx.lib.agw.labelbook as LB
from wx.lib.agw.fmresources import *


class ConfigApp(wx.Frame):
    def __init__(self, parent=None):
        super().__init__(parent, title="配置管理", size=(800, 600))

        # 创建 LabelBook 控件
        panel = wx.Panel(self)
        labelbook = LB.LabelBook(panel, agwStyle=LB.INB_LEFT | INB_FIT_LABELTEXT)

        # 创建布局
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(labelbook, 1, wx.EXPAND)
        panel.SetSizer(sizer)

        # 添加配置页面
        self.add_pages(labelbook)
        self.Layout()
        self.SendSizeEvent()

    def add_pages(self, labelbook: LB.LabelBook):

        # 创建图像列表
        self.image_list = wx.ImageList(32, 32)
        self.image_list.Add(
            wx.ArtProvider.GetBitmap(wx.ART_NEW_DIR, wx.ART_OTHER, (32, 32))
        )
        self.image_list.Add(
            wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK, wx.ART_OTHER, (32, 32))
        )
        self.image_list.Add(
            wx.ArtProvider.GetBitmap(wx.ART_CDROM, wx.ART_OTHER, (32, 32))
        )
        labelbook.AssignImageList(self.image_list)

        # 示例页面 1: 基础设置
        page1 = wx.Panel(labelbook)
        wx.StaticText(page1, label="基础设置内容", pos=(20, 20))
        labelbook.AddPage(page1, "基础设置", select=True, imageId=0)

        # 示例页面 2: 网络设置
        page2 = wx.Panel(labelbook)
        wx.StaticText(page2, label="网络设置内容", pos=(20, 20))
        labelbook.AddPage(page2, "网络设置", select=True, imageId=1)

        # 示例页面 3: 高级设置
        page3 = wx.Panel(labelbook)
        wx.StaticText(page3, label="高级设置内容", pos=(20, 20))
        labelbook.AddPage(page3, "高级设置", select=True, imageId=2)


if __name__ == "__main__":
    app = wx.App(False)
    frame = ConfigApp()
    frame.Show()
    app.MainLoop()

简单的界面效果如下所示。

我们有一个主要的结构后,就可以设计它们的样式和面板的内容了。我们可以通过一个函数来设置它的相关颜色效果。

复制代码
    def SetUserColours(self):
        """设置LabelBook用户自定义颜色"""

        self.background = wx.Colour(132, 164, 213)
        self.activetab = wx.Colour(255, 255, 255)
        self.tabsborder = wx.Colour(0, 0, 204)
        self.textcolour = wx.Colour(0, 0, 0)
        self.activetextcolour = wx.Colour(0, 0, 0)
        self.hilite = wx.Colour(191, 216, 216)

        self.book.SetColour(INB_TAB_AREA_BACKGROUND_COLOUR, self.background)
        self.book.SetColour(INB_ACTIVE_TAB_COLOUR, self.activetab)
        self.book.SetColour(INB_TABS_BORDER_COLOUR, self.tabsborder)
        self.book.SetColour(INB_TEXT_COLOUR, self.textcolour)
        self.book.SetColour(INB_ACTIVE_TEXT_COLOUR, self.activetextcolour)
        self.book.SetColour(INB_HILITE_TAB_COLOUR, self.hilite)

我们让每个面板的创建独立一个函数来创建,如下所示。

复制代码
        # 添加页面
        self.email_panel = self.create_email_panel(self.book)
        self.system_panel = self.create_system_panel(self.book)

        self.book.AddPage(self.email_panel, "邮箱配置", True, imageId=0)
        self.book.AddPage(self.system_panel, "系统设置", True, imageId=1)

其中系统设置简单如下所示。

复制代码
    def create_system_panel(self, parent):
        panel = wx.Panel(parent)
        sizer = wx.BoxSizer(wx.VERTICAL)

        # 系统参数1
        param1_sizer = wx.BoxSizer(wx.HORIZONTAL)
        param1_label = wx.StaticText(panel, label="参数1:")
        self.param1_input = wx.TextCtrl(panel)
        param1_sizer.Add(param1_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
        param1_sizer.Add(self.param1_input, 1, wx.EXPAND | wx.ALL, 10)

        # 系统参数2
        param2_sizer = wx.BoxSizer(wx.HORIZONTAL)
        param2_label = wx.StaticText(panel, label="参数2:")
        self.param2_input = wx.TextCtrl(panel)
        param2_sizer.Add(param2_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
        param2_sizer.Add(self.param2_input, 1, wx.EXPAND | wx.ALL, 10)

        # 添加到面板
        sizer.Add(param1_sizer, 0, wx.EXPAND)
        sizer.Add(param2_sizer, 0, wx.EXPAND)

        panel.SetSizer(sizer)
        return panel

通过上面的各司其职,就可以很好的创建对应的界面控件输入及处理了,创建的界面效果如下所示。

由于配置页面可能有很多个,不同的业务参数设置处理可能也有所不同。我们设计一个配置界面基类:MyConfigDialog

其他业务配置类,继承它即可具有默认的常规处理效果和实现,如我们有一个 FrmConfigSettings 的子类,关系图如下所示。

我们只需要再配置界面基类MyConfigDialog中定义创建添加页面的方法,留给子类实现即可,如下所示。

复制代码
    def create_papges(self, parent: LB.LabelBook):
        """创建页面 - 子类重写"""

如我们在子类FrmConfigSettings 实现了添加页面的方法,如下所示。

复制代码
    def create_papges(self, parent: LB.LabelBook):
        """创建页面 - 子类重写"""
        # 设置图像列表
        image_list = wx.ImageList(32, 32)
        image_list.Add(get_bitmap("email", 32))
        image_list.Add(get_bitmap("computer_key", 32))  # ART_INFORMATION
        image_list.Add(get_bitmap("book", 32))
        self.set_image_list(image_list)

        self.email_panel = self.create_email_panel(self.book)
        self.book.AddPage(self.email_panel, "邮箱配置", True, imageId=0)

        self.env_panel = self.create_env_panel(self.book)
        self.book.AddPage(self.env_panel, "应用程序信息", False, imageId=1)

        self.params_panel = self.create_params_panel(self.book)
        self.book.AddPage(self.params_panel, "系统参数配置", False, imageId=2)

我们添加界面引入只定义的辅助类GridBagUtil 来简化添加的处理,如下代码是创建一个邮箱配置的界面。

复制代码
    def create_email_panel(self, parent):
        """创建邮箱配置页面"""
        panel = wx.Panel(parent, wx.ID_ANY)
        # 创建一个 GridBagSizer
        grid_sizer = wx.GridBagSizer(2, 2)  # 行间距和列间距为 5
        util = GridBagUtil(panel, grid_sizer, 4)

        self.email = ctrl.MyTextCtrl(panel)
        self.pop3_server = ctrl.MyTextCtrl(panel)
        self.pop3_port = ctrl.MyTextCtrl(panel)
        self.smtp_server = ctrl.MyTextCtrl(panel)
        self.smtp_port = ctrl.MyTextCtrl(panel)
        self.username = ctrl.MyTextCtrl(panel)
        self.password = ctrl.MyTextCtrl(panel, style=wx.TE_PASSWORD)
        self.user_ssl = ctrl.MyCheckBox(panel, label="使用SSL加密")

        util.add_control("邮件账号", self.email, is_expand=True, is_span=True, border=10)
        util.add_control("POP3服务器:", self.pop3_server, is_expand=True, border=10)
        util.add_control("POP3端口号:", self.pop3_port, is_expand=True, border=10)
        util.add_control("SMTP服务器:", self.smtp_server, is_expand=True, border=10)
        util.add_control("SMTP端口号:", self.smtp_port, is_expand=True, border=10)
        util.add_control( "登录账号:", self.username, is_expand=True, is_span=True, border=10)
        util.add_control("登录密码:", self.password, is_expand=True, is_span=True, border=10 )
        util.add_control("是否SSL加密:", self.user_ssl, is_expand=True, is_span=True, border=10)

        # 让控件跟随窗口拉伸
        grid_sizer.AddGrowableCol(1)  # 允许第二列拉伸
        grid_sizer.AddGrowableCol(3)  # 允许第三行拉伸
        panel.SetSizer(grid_sizer)
        panel.Layout()
        return panel

最终界面效果如下所示。

对比一下之前的界面效果,整体效果各有千秋吧。

上面的WxPython的参数配置管理界面中,我设计了几个不同的参数管理,包括对ini文件的处理,.env配置文件的读取,以及基于系统在后台数据库的参数管理界面实现多个不同内容的管理。

如对于邮箱的相关配置信息,我们存储在INI文件中,因此创建一个INI文件存取信息的辅助类来处理即可。

显示数据的时候,通过调用辅助类来实现数据的显示,如下是邮件参数的读取显示函数。

复制代码
    def display_email_data(self):
        """显示邮箱配置数据"""
        # print(settings.AppDir)
        filepath = settings.AppDir + "/" + self.filepath
        ini_setting = IniSettingUtil(filepath)

        self.email.SetValue(ini_setting.get(self.section, "email", ""))
        self.pop3_server.SetValue(ini_setting.get(self.section, "pop3_server", ""))
        self.pop3_port.SetValue(ini_setting.get_int(self.section, "pop3_port", 0))
        self.smtp_server.SetValue(ini_setting.get(self.section, "smtp_server", ""))
        self.smtp_port.SetValue(ini_setting.get_int(self.section, "smtp_port", 0))
        self.username.SetValue(ini_setting.get(self.section, "username", ""))
        self.password.SetValue(ini_setting.get(self.section, "password", ""))
        self.user_ssl.SetValue(ini_setting.get_bool(self.section, "ssl", False))

而程序的相关参数信息,我们通过Pydantic_Setting的进行加载到系统的配置类中的,那么直接读取即可。

复制代码
    def display_env_data(self):
        """显示应用程序信息数据"""

        self.app_name.SetValue(settings.APP_NAME)
        self.app_version.SetValue(settings.APP_VERSION)
        self.app_desc.SetValue(settings.DESCRIPTION)
        self.app_baseapi.SetValue(settings.API_BASE_URL)
        self.app_unit.SetValue(settings.APP_UNIT)
        self.app_author.SetValue(settings.App_Author)
        self.app_email.SetValue(settings.App_Email)

关于settings是如何来的,可以了解一下Pydantic_Setting的处理方式,它是一个参数类的实例,可以读取目录下的.env配置参数到类里面作为属性处理。

复制代码
class Settings(BaseSettings):
    """系统信息配置类"""

    model_config = SettingsConfigDict(
        env_file=f"{BasePath}/.env",  # 加载env文件
        extra="ignore",  # 加载env文件,如果没有在Settings中定义属性,也不抛出异常
        env_file_encoding="utf-8",
        env_prefix="",
        case_sensitive=False,
    )

    # 应用程序信息(项目名称、版本、描述等),从.env文件中读取
    APP_NAME: str = "wxpython-Project"
    APP_VERSION: str = "1.0.0"
    DESCRIPTION: str = "本项目是基于 wxPython 开发的 GUI 应用。"
    API_BASE_URL: str = "http://localhost:8000/api/"
    APP_UNIT: str = "广州爱奇迪软件科技有限公司"  # 单位名称
    App_Author: str = "伍华聪"  # 项目作者
    App_Email: str = ""  # 项目作者邮箱

如果.env没有值,那么就使用这里面的默认值,如有,则加载.env中的参数值。

另外,我们系统提供了一个常用的参数管理模块,也可以整合在其中进行显示。

这个模块直接是通过API进行远程读取获取数据显示的。

复制代码
    async def display_params_data(self):
        """显示参数配置数据"""
        company_name = await api_systemparam.get_by_name("公司名称")
        address = await api_systemparam.get_by_name("公司地址")
        contact = await api_systemparam.get_by_name("公司联系人")
        invoice = await api_systemparam.get_by_name("开票信息")

        self.param_company.SetValue(company_name.content)
        self.param_address.SetValue(address.content)
        self.param_contact.SetValue(contact.content)
        self.param_invoice.SetValue(invoice.content)

以上就是我们在做WxPython跨平台的配置管理界面中,实现的思路和处理过程,通过抽象基类的方式,减少常用的代码和逻辑,并提供很好的扩展。

相关推荐
伍华聪14 天前
一问一答学习PyQT6,对比WxPython和PyQt6的差异
python开发
伍华聪16 天前
WxPython跨平台开发框架之使用PyInstaller 进行打包处理
python开发
伍华聪21 天前
WxPython跨平台开发框架之模块字段权限的管理
python开发
伍华聪22 天前
WxPython跨平台开发框架之动态菜单的管理和功能权限的控制
python开发
伍华聪23 天前
WxPython跨平台开发框架之前后端结合实现附件信息的上传及管理
python开发
伍华聪24 天前
WxPython跨平台开发框架之图标选择界面
python开发
伍华聪1 个月前
WxPython跨平台开发框架之列表数据的通用打印处理
python开发
伍华聪1 个月前
WxPython跨平台开发框架之复杂界面内容的分拆和重组处理
python开发
伍华聪1 个月前
WxPython跨平台开发框架之表格数据导出到Excel并打开
python开发