手游SDK — 第八篇(游戏打包番外篇- 桌面UI设计)

各位老铁。欢迎来看打包系统番外篇-桌面打包工具UI设计篇。这篇文章将讲解PackageApkTool工具的UI实现过程。

大家可以先看看整体UI优化的最终版本效果图:


打包.gif

一、UI设计篇

说实话在做UI界面的时候,确实不知道怎么入手,不过幸好易接打包工具可以参考UI样式。如下图

image.png

整个UI设计设计大概分四个模块信息:

  • 菜单栏:菜单选项,主要用于配置游戏打包过程的文件信息
  • 渠道信息栏:分两部分,渠道管理列表和渠道配置框。渠道管理列表管理已下载的渠道资源;渠道配置框显示当前选择需要打包的渠道打包配置信息,包括渠道参数和编译参数。
  • 提示信息栏:提示打包的提前条件及包体信息。如:包体需先接入聚合SDK或包体导入后包体的错误信息等
  • 日志信息栏:动态显示打包过程的日志信息。

PS:还有一个渠道资源下载弹框。主要是跟渠道资源管理列表类似。不过多描述了。

二、代码实现篇

第一步,wx.SplitterWindow实现窗体分割:

整体分割整个布局,最上面是工具菜单栏。窗体Frame水平分割和垂直分割动态分配,水平分割底部窗口占用frame框架高度的1/2。垂直分割模式左边窗口占用frame框架宽度的2/5。可以通过wx.SplitterWindow控件实现。

import wx

TOOL_TITLE = u'游戏打渠道包工具'

# 显示主页面
class GuiMainFrame(wx.Frame):

    def __init__(self, frame_size, flag=True):
        wx.Frame.__init__(self, parent=None, id=-1, title=TOOL_TITLE, size=frame_size)
        self.Center()  # 窗口居中

        # 将窗体传递到子控件
        self.windowFrame = self

        self.first = 0
        self.flag = flag

        self.width = self.Size.width
        self.height = self.Size.height

        self.up = self.height/2  # 上面窗口高度
        self.left = (self.width/5)*2  # 嵌套窗口左窗口宽度

        self.spWindow = wx.SplitterWindow(self, size=(self.width, self.height))  # 创建一个主分割窗体, parent是frame, 区分上下两部分
        self.up_panel = wx.Panel(self.spWindow)  # 创建上半部分面板
        self.down_panel = wx.Panel(self.spWindow, style=wx.SUNKEN_BORDER)  # 创建下半部分面板
        self.down_panel.SetBackgroundColour("#FFFFFF")
        wx.StaticText(self.down_panel, -1, "日志显示框", (600, 200), (160, -1), wx.ALIGN_CENTER)

        self.child_spWindow = wx.SplitterWindow(self.up_panel)  # 创建一个子分割窗, parent是up_panel, 区分上半区左右部分
        self.child_layout = wx.BoxSizer(wx.VERTICAL)  # 创建一个垂直布局
        self.child_layout.Add(self.child_spWindow, 1, wx.EXPAND)  # 将子分割窗布局延伸至整个p1空间
        self.up_panel.SetSizer(self.child_layout)

        self.resourcePanel = wx.Panel(self.child_spWindow, style=wx.SUNKEN_BORDER)  # 在子分割窗上创建左面板
        wx.StaticText(self.resourcePanel, -1, "资源下拉框", (180, 180), (160, -1), wx.ALIGN_CENTER)
        self.resourcePanel.SetBackgroundColour("#FFFFFF")

        self.channelPanel = wx.Panel(self.child_spWindow, style=wx.SUNKEN_BORDER)  # 在子分割窗上创建右面板
        wx.StaticText(self.channelPanel, -1, "渠道配置框", (250, 180), (160, -1), wx.ALIGN_CENTER)
        self.channelPanel.SetBackgroundColour("#FFFFFF")

        self.spWindow.SplitHorizontally(self.up_panel, self.down_panel, 0)
        self.child_spWindow.SplitVertically(self.resourcePanel, self.channelPanel, self.left)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase_back)

        self.ToolBar = wx.ToolBar(self, wx.ID_ANY, style=wx.TB_HORZ_LAYOUT | wx.TB_TEXT)  # 创建工具栏对象
        self.ToolBar.Realize()  # 提交工具栏设置

    def on_erase_back(self, event):
        if self.first < 2 or self.flag:
            self.spWindow.SetSashPosition(0)
            self.child_spWindow.SetSashPosition(self.left)
            self.first = self.first + 1
        self.Refresh()


# 程序运行入口
if __name__ == "__main__":
    app = wx.App()
    green_size = wx.DisplaySize()
    a = green_size[0]*0.7
    b = green_size[1]*0.85
    frame = GuiMainFrame((a, b))
    frame.Show()
    app.MainLoop()
image.png

第二步,给整体Frame窗体各个模块添加布局和控件:

wx.ToolBar,实现菜单栏按钮:
def add_bar_tools(self):

    toolbar_size = (30, 28)  # 设置工具栏图标大小

    # 创建图标
    open_game_apk_bmp = wx.ArtProvider.GetBitmap(wx.ART_PLUS, wx.ART_TOOLBAR, toolbar_size)
    set_output_dir_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, toolbar_size)
    down_sdk_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR, toolbar_size)
    set_sign_file_bmp = wx.ArtProvider.GetBitmap(wx.ART_FIND_AND_REPLACE, wx.ART_TOOLBAR, toolbar_size)
    tool_help_bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, toolbar_size)

    # 将这图标放入工具栏
    self.ToolBar.AddTool(200, u'导入游戏', open_game_apk_bmp, u'导入游戏')
    self.ToolBar.AddTool(201, u'设置输出目录', set_output_dir_bmp, u'设置输出目录')
    self.ToolBar.AddTool(202, u'设置签名', set_sign_file_bmp, u'设置签名')
    self.ToolBar.AddTool(203, u'下载渠道SDK', down_sdk_bmp, u'下载渠道SDK')
    self.ToolBar.AddTool(204, u'帮助说明', tool_help_bmp, u'帮助说明')

    self.ToolBar.AddSeparator()  # 分割
image.png
ULC.UltimateListCtrl,自定义列表,实现渠道资源列表:
def add_sdk_list(self):
    list_layout = wx.BoxSizer(wx.VERTICAL)
    sdkLabel = wx.StaticBox(self.resourcePanel, -1, u'已下载SDK列表')
    sdkBox = wx.StaticBoxSizer(sdkLabel, wx.VERTICAL)
    # 创建排序列表列标题
    list_width = (self.width / 5) * 2
    list_heigt = self.height / 2
    space_length = list_width / 10

    test_data = [{'sdk_id': '1', 'sdk_path': 'WorkSpace\\DownSdk\\测试\\1.0.0\\leixang_1_1.0.0.zip', 'sdk_version': '1.0.0', 'sdk_name': '测试'}]

    main_list_columns = [("渠道ID", space_length * 1.2), ("图标", space_length), ("SDK名称", space_length * 3.3),
                         ("SDK版本", space_length * 2), ("选择", space_length * 1.8)]
    main_sdk_list = MySDKList(self.resourcePanel, (list_width, list_heigt), space_length, main_list_columns, test_data)
    sdkBox.Add(main_sdk_list, 1, wx.ALL | wx.EXPAND, 0)
    list_layout.Add(sdkBox, 0, wx.EXPAND | wx.ALL, 5)
    self.resourcePanel.SetSizer(list_layout)

自定义渠道资源列表

SDK_ID = 'sdk_id'  # 渠道id
SDK_NAME = 'sdk_name'  # 渠道名
SDK_VERSION = 'sdk_version'  # 渠道别名
SDK_PATH = 'sdk_path'  # 资源路径

class MySDKList(ULC.UltimateListCtrl):

    def __init__(self, parent, list_size, space_length, columns, channel_data):
        """
        list_size 为 (750, 700), 定义列表大小
        columns 为数组形式[("AA", 100), ("BB", 100)], 定义表头名称及大小
        """
        ULC.UltimateListCtrl.__init__(self, parent, -1, size=list_size, style=wx.LC_REPORT,
                                      agwStyle=ULC.ULC_REPORT | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT
                                      | wx.LC_VRULES | wx.LC_HRULES | ULC.ULC_NO_HIGHLIGHT)
        self.set_columns(columns)

        # 转化数据格式, 进行排序, 数据必须得有索引
        # 数据格式为:{0:("a","abc","ABC"),  1:("1","123","一二三"),
        new_channel_data = {}
        for item in range(len(channel_data)):
            # 获取数据
            item_data = channel_data[item]
            sdk_id = item_data.get(SDK_ID, '')
            sdk_name = item_data.get(SDK_NAME, '')
            sdk_version = item_data.get(SDK_VERSION, '')
            sdk_path = item_data.get(SDK_PATH, '')

            # 添加图标
            icon_bmp = wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_TOOLBAR)
            sdk_icon = wx.StaticBitmap(self, -1, icon_bmp, (35, 35))

            # 添加下载按钮
            sdk_choose_button = wx.Button(self, label='选择', size=((space_length*1.8)-5, 28))

            values = (str(sdk_id), sdk_icon, str(sdk_name), str(sdk_version), sdk_choose_button)

            new_channel_data[item] = values

        # 显示列表数据
        self.show_channel_list(new_channel_data)

    def set_columns(self, columns):
        """添加表头信息"""
        i = 0
        for name, width in columns:
            self.InsertColumn(i, name, wx.LIST_FORMAT_CENTRE, width)
            i += 1

    def show_channel_list(self, data):
        """显示列表数据"""

        for key, values in data.items():
            index = self.InsertStringItem(99999999999, values[0])  # 插入一行
            for i in range(len(values[1:])):  # 为这一行的列设置值

                if i == 0:  # 设置渠道图标
                    self.SetItemWindow(index, i + 1, values[i + 1], expand=True)

                elif i == 3:  # 设置选择按钮
                    self.SetItemWindow(index, i + 1, values[i + 1], expand=False)

                else:  # 设置文字
                    self.SetStringItem(index, i + 1, values[i + 1])
image.png
参数项循环数据,实现渠道配置列表:
def add_channel_config(self):

    # 渠道参数配置布局
    channelLayout = wx.BoxSizer(wx.VERTICAL)
    channelLabel = wx.StaticBox(self.channelPanel, -1,  u'渠道默认配置')
    channelBox = wx.StaticBoxSizer(channelLabel, wx.HORIZONTAL)

    # 参数列
    channelParamsLayout = wx.BoxSizer(wx.VERTICAL)
    # 图标列
    channelIconLayout = wx.BoxSizer(wx.VERTICAL)
    channelBox.Add(channelParamsLayout, 5, wx.ALL | wx.EXPAND, 0)
    channelBox.Add(channelIconLayout, 2, wx.ALL | wx.EXPAND, 0)

    # 参数配置界面
    channel_configs = OrderedDict([(u'app_id', ''), (u'app_key', ''), (u'app_secret', ''), (u'game_name', '')])
    for config_key, config_value in channel_configs.items():
        channelConfig = wx.BoxSizer(wx.HORIZONTAL)
        channelConfigText = wx.StaticText(self.channelPanel, -1, config_key)
        channelConfigValue = wx.TextCtrl(self.channelPanel, -1, style=wx.ALIGN_LEFT | wx.EXPAND)
        channelConfigValue.SetValue(config_value)
        channelConfig.Add(channelConfigText, 0, wx.ALL | wx.CENTER, 5)
        channelConfig.Add(channelConfigValue, 1, wx.ALL | wx.CENTER, 5)
        channelParamsLayout.Add(channelConfig, 0, wx.ALL | wx.EXPAND, 0)

    channelConfigAddButton = wx.Button(self.channelPanel, label=u'添加配置项')
    channelParamsLayout.Add(channelConfigAddButton, 0, wx.EXPAND | wx.ALL | wx.CENTER, 0)

    # Icon配置界面
    channel_icon = OrderedDict([(u'游戏图标', {u'选择图标': ''}), (u'游戏角标', {u'选择角标': ''}), (u'游戏闪屏', {u'选择闪屏': ''})])
    for icon_key, icon_object in channel_icon.items():
        for icon_value, icon_path in icon_object.items():
            channelIcon = wx.BoxSizer(wx.HORIZONTAL)
            channelIconText = wx.StaticText(self.channelPanel, -1, icon_key)
            channelIconButton = wx.Button(self.channelPanel, -1, label=icon_value, size=(50, 24))
            channelIcon.Add(channelIconText, 0, wx.ALL | wx.CENTER, 5)
            channelIcon.Add(channelIconButton, 1, wx.ALL | wx.CENTER, 5)
            channelIconLayout.Add(channelIcon, 0, wx.ALL | wx.EXPAND, 0)

    channelLayout.Add(channelBox, 0, wx.EXPAND | wx.ALL, 6)
    self.channelPanel.SetSizer(channelLayout)
image.png
wx.TextCtrl 配置style=wx.HSCROLL | wx.TE_MULTILINE样式实现列表数据:
def add_log_text(self):
    # 提示信息栏
    packageLayout = wx.BoxSizer(wx.VERTICAL)
    WarnMessageLabel = wx.StaticBox(self.down_panel, -1, u'')
    WarnMessageBox = wx.StaticBoxSizer(WarnMessageLabel, wx.VERTICAL)

    packageFrame = wx.BoxSizer()
    packageMessageText = wx.StaticText(self.down_panel, -1, "温馨提示:xxxx")
    packageButton = wx.Button(self.down_panel, label=u'开始打包')

    packageFrame.Add(packageMessageText, 1, wx.ALL | wx.CENTER, 3)
    packageFrame.Add(packageButton, 0, wx.ALL | wx.CENTER, 3)
    WarnMessageBox.Add(packageFrame, 1, wx.ALL | wx.EXPAND, 0)

    logLabel = wx.StaticBox(self.down_panel, -1, u'日志信息')
    logBox = wx.StaticBoxSizer(logLabel, wx.VERTICAL)
    logFrame = wx.BoxSizer()
    logText = wx.TextCtrl(self.down_panel, size=(1000, 800), style=wx.HSCROLL | wx.TE_MULTILINE)
    logFrame.Add(logText, proportion=1, flag=wx.EXPAND | wx.ALL)
    logBox.Add(logFrame, 1, wx.ALL | wx.EXPAND, 3)

    packageLayout.Add(WarnMessageBox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
    packageLayout.Add(logBox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
    self.down_panel.SetSizer(packageLayout)
image.png

好了至此,完整的界面代码就已经实现。

#!/usr/bin/env python
# -*-coding:utf-8 -*-

import wx
import wx.lib.agw.ultimatelistctrl as ULC
from collections import OrderedDict


TOOL_TITLE = u'游戏打渠道包工具'

SDK_ID = 'sdk_id'  # 渠道id
SDK_NAME = 'sdk_name'  # 渠道名
SDK_VERSION = 'sdk_version'  # 渠道别名
SDK_PATH = 'sdk_path'  # 资源路径


# 自定义排序列表(可以添加控件的列表)
class MySDKList(ULC.UltimateListCtrl):

    def __init__(self, parent, list_size, space_length, columns, channel_data):
        """
        list_size 为 (750, 700), 定义列表大小
        columns 为数组形式[("AA", 100), ("BB", 100)], 定义表头名称及大小
        """
        ULC.UltimateListCtrl.__init__(self, parent, -1, size=list_size, style=wx.LC_REPORT,
                                      agwStyle=ULC.ULC_REPORT | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT
                                      | wx.LC_VRULES | wx.LC_HRULES | ULC.ULC_NO_HIGHLIGHT)
        self.set_columns(columns)

        # 转化数据格式, 进行排序, 数据必须得有索引
        # 数据格式为:{0:("a","abc","ABC"),  1:("1","123","一二三"),
        new_channel_data = {}
        for item in range(len(channel_data)):
            # 获取数据
            item_data = channel_data[item]
            sdk_id = item_data.get(SDK_ID, '')
            sdk_name = item_data.get(SDK_NAME, '')
            sdk_version = item_data.get(SDK_VERSION, '')
            sdk_path = item_data.get(SDK_PATH, '')

            # 添加图标
            icon_bmp = wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_TOOLBAR)
            sdk_icon = wx.StaticBitmap(self, -1, icon_bmp, (35, 35))

            # 添加选择按钮
            sdk_choose_button = wx.Button(self, label='选择', size=((space_length*1.8)-5, 28))

            values = (str(sdk_id), sdk_icon, str(sdk_name), str(sdk_version), sdk_choose_button)

            new_channel_data[item] = values

        # 显示列表数据
        self.show_channel_list(new_channel_data)

    def set_columns(self, columns):
        """添加表头信息"""
        i = 0
        for name, width in columns:
            self.InsertColumn(i, name, wx.LIST_FORMAT_CENTRE, width)
            i += 1

    def show_channel_list(self, data):
        """显示列表数据"""

        for key, values in data.items():
            index = self.InsertStringItem(99999999999, values[0])  # 插入一行
            for i in range(len(values[1:])):  # 为这一行的列设置值

                if i == 0:  # 设置渠道图标
                    self.SetItemWindow(index, i + 1, values[i + 1], expand=True)

                elif i == 3:  # 设置选择按钮
                    self.SetItemWindow(index, i + 1, values[i + 1], expand=False)

                else:  # 设置文字
                    self.SetStringItem(index, i + 1, values[i + 1])


# 显示主页面
class GuiMainFrame(wx.Frame):

    def __init__(self, frame_size, flag=True):
        wx.Frame.__init__(self, parent=None, id=-1, title=TOOL_TITLE, size=frame_size)
        self.Center()  # 窗口居中

        # 将窗体传递到子控件
        self.windowFrame = self

        self.first = 0
        self.flag = flag

        self.width = self.Size.width
        self.height = self.Size.height

        self.up = self.height/2  # 上面窗口高度
        self.left = (self.width/5)*2  # 嵌套窗口左窗口宽度

        self.spWindow = wx.SplitterWindow(self, size=(self.width, self.height))  # 创建一个主分割窗体, parent是frame, 区分上下两部分
        self.up_panel = wx.Panel(self.spWindow)  # 创建上半部分面板
        self.down_panel = wx.Panel(self.spWindow, style=wx.SUNKEN_BORDER)  # 创建下半部分面板
        self.add_log_text()
        self.down_panel.SetBackgroundColour("#FFFFFF")

        self.child_spWindow = wx.SplitterWindow(self.up_panel)  # 创建一个子分割窗, parent是up_panel, 区分上半区左右部分
        self.child_layout = wx.BoxSizer(wx.VERTICAL)  # 创建一个垂直布局
        self.child_layout.Add(self.child_spWindow, 1, wx.EXPAND)  # 将子分割窗布局延伸至整个p1空间
        self.up_panel.SetSizer(self.child_layout)

        self.resourcePanel = wx.Panel(self.child_spWindow, style=wx.SUNKEN_BORDER)  # 在子分割窗上创建左面板
        self.add_sdk_list()
        self.resourcePanel.SetBackgroundColour("#FFFFFF")

        self.channelPanel = wx.Panel(self.child_spWindow, style=wx.SUNKEN_BORDER)  # 在子分割窗上创建右面板
        self.add_channel_config()
        self.channelPanel.SetBackgroundColour("#FFFFFF")

        self.spWindow.SplitHorizontally(self.up_panel, self.down_panel, 0)
        self.child_spWindow.SplitVertically(self.resourcePanel, self.channelPanel, self.left)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase_back)

        self.ToolBar = wx.ToolBar(self, wx.ID_ANY, style=wx.TB_HORZ_LAYOUT | wx.TB_TEXT)  # 创建工具栏对象
        self.add_bar_tools()
        self.ToolBar.Realize()  # 提交工具栏设置

    def on_erase_back(self, event):
        if self.first < 2 or self.flag:
            self.spWindow.SetSashPosition(0)
            self.child_spWindow.SetSashPosition(self.left)
            self.first = self.first + 1
        self.Refresh()

    def add_bar_tools(self):
        toolbar_size = (30, 28)  # 设置工具栏图标大小

        # 创建图标
        open_game_apk_bmp = wx.ArtProvider.GetBitmap(wx.ART_PLUS, wx.ART_TOOLBAR, toolbar_size)
        set_output_dir_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, toolbar_size)
        down_sdk_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR, toolbar_size)
        set_sign_file_bmp = wx.ArtProvider.GetBitmap(wx.ART_FIND_AND_REPLACE, wx.ART_TOOLBAR, toolbar_size)
        tool_help_bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, toolbar_size)

        # 将这图标放入工具栏
        self.ToolBar.AddTool(200, u'导入游戏', open_game_apk_bmp, u'导入游戏')
        self.ToolBar.AddTool(201, u'设置输出目录', set_output_dir_bmp, u'设置输出目录')
        self.ToolBar.AddTool(202, u'设置签名', set_sign_file_bmp, u'设置签名')
        self.ToolBar.AddTool(203, u'下载渠道SDK', down_sdk_bmp, u'下载渠道SDK')
        self.ToolBar.AddTool(204, u'帮助说明', tool_help_bmp, u'帮助说明')

        self.ToolBar.AddSeparator()  # 分割

    def add_sdk_list(self):
        list_layout = wx.BoxSizer(wx.VERTICAL)
        sdkLabel = wx.StaticBox(self.resourcePanel, -1, u'已下载SDK列表')
        sdkBox = wx.StaticBoxSizer(sdkLabel, wx.VERTICAL)
        # 创建排序列表列标题
        list_width = (self.width / 5) * 2
        list_heigt = self.height / 2
        space_length = list_width / 10

        test_data = [{'sdk_id': '1', 'sdk_path': 'WorkSpace\\DownSdk\\测试\\1.0.0\\leixang_1_1.0.0.zip', 'sdk_version': '1.0.0', 'sdk_name': '测试'}]

        main_list_columns = [("渠道ID", space_length * 1.2), ("图标", space_length), ("SDK名称", space_length * 3.3),
                             ("SDK版本", space_length * 2), ("选择", space_length * 1.8)]
        main_sdk_list = MySDKList(self.resourcePanel, (list_width, list_heigt), space_length, main_list_columns, test_data)
        sdkBox.Add(main_sdk_list, 1, wx.ALL | wx.EXPAND, 0)
        list_layout.Add(sdkBox, 0, wx.EXPAND | wx.ALL, 5)
        self.resourcePanel.SetSizer(list_layout)

    def add_channel_config(self):

        # 渠道参数配置布局
        channelLayout = wx.BoxSizer(wx.VERTICAL)
        channelLabel = wx.StaticBox(self.channelPanel, -1,  u'渠道默认配置')
        channelBox = wx.StaticBoxSizer(channelLabel, wx.HORIZONTAL)

        # 参数列
        channelParamsLayout = wx.BoxSizer(wx.VERTICAL)
        # 图标列
        channelIconLayout = wx.BoxSizer(wx.VERTICAL)
        channelBox.Add(channelParamsLayout, 5, wx.ALL | wx.EXPAND, 0)
        channelBox.Add(channelIconLayout, 2, wx.ALL | wx.EXPAND, 0)

        # 参数配置界面
        channel_configs = OrderedDict([(u'app_id', ''), (u'app_key', ''), (u'app_secret', ''), (u'game_name', '')])
        for config_key, config_value in channel_configs.items():
            channelConfig = wx.BoxSizer(wx.HORIZONTAL)
            channelConfigText = wx.StaticText(self.channelPanel, -1, config_key)
            channelConfigValue = wx.TextCtrl(self.channelPanel, -1, style=wx.ALIGN_LEFT | wx.EXPAND)
            channelConfigValue.SetValue(config_value)
            channelConfig.Add(channelConfigText, 0, wx.ALL | wx.CENTER, 5)
            channelConfig.Add(channelConfigValue, 1, wx.ALL | wx.CENTER, 5)
            channelParamsLayout.Add(channelConfig, 0, wx.ALL | wx.EXPAND, 0)

        channelConfigAddButton = wx.Button(self.channelPanel, label=u'添加配置项')
        channelParamsLayout.Add(channelConfigAddButton, 0, wx.EXPAND | wx.ALL | wx.CENTER, 0)

        # Icon配置界面
        channel_icon = OrderedDict([(u'游戏图标', {u'选择图标': ''}), (u'游戏角标', {u'选择角标': ''}), (u'游戏闪屏', {u'选择闪屏': ''})])
        for icon_key, icon_object in channel_icon.items():
            for icon_value, icon_path in icon_object.items():
                channelIcon = wx.BoxSizer(wx.HORIZONTAL)
                channelIconText = wx.StaticText(self.channelPanel, -1, icon_key)
                channelIconButton = wx.Button(self.channelPanel, -1, label=icon_value, size=(50, 24))
                channelIcon.Add(channelIconText, 0, wx.ALL | wx.CENTER, 5)
                channelIcon.Add(channelIconButton, 1, wx.ALL | wx.CENTER, 5)
                channelIconLayout.Add(channelIcon, 0, wx.ALL | wx.EXPAND, 0)

        channelLayout.Add(channelBox, 0, wx.EXPAND | wx.ALL, 6)
        self.channelPanel.SetSizer(channelLayout)

    def add_log_text(self):
        # 提示信息栏
        packageLayout = wx.BoxSizer(wx.VERTICAL)
        WarnMessageLabel = wx.StaticBox(self.down_panel, -1, u'')
        WarnMessageBox = wx.StaticBoxSizer(WarnMessageLabel, wx.VERTICAL)

        packageFrame = wx.BoxSizer()
        packageMessageText = wx.StaticText(self.down_panel, -1, "温馨提示:xxxx")
        packageButton = wx.Button(self.down_panel, label=u'开始打包')

        packageFrame.Add(packageMessageText, 1, wx.ALL | wx.CENTER, 3)
        packageFrame.Add(packageButton, 0, wx.ALL | wx.CENTER, 3)
        WarnMessageBox.Add(packageFrame, 1, wx.ALL | wx.EXPAND, 0)

        logLabel = wx.StaticBox(self.down_panel, -1, u'日志信息')
        logBox = wx.StaticBoxSizer(logLabel, wx.VERTICAL)
        logFrame = wx.BoxSizer()
        logText = wx.TextCtrl(self.down_panel, size=(1000, 800), style=wx.HSCROLL | wx.TE_MULTILINE)
        logFrame.Add(logText, proportion=1, flag=wx.EXPAND | wx.ALL)
        logBox.Add(logFrame, 1, wx.ALL | wx.EXPAND, 3)

        packageLayout.Add(WarnMessageBox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
        packageLayout.Add(logBox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
        self.down_panel.SetSizer(packageLayout)


# 程序运行入口
if __name__ == "__main__":
    app = wx.App()
    green_size = wx.DisplaySize()
    a = green_size[0]*0.7
    b = green_size[1]*0.85
    frame = GuiMainFrame((a, b))
    frame.Show()
    app.MainLoop()

第三步,控件的监听及界面的刷新

这部分代码比较多,大家可以到开源控件PackageApkToolUI模块看具体实现。

这里重点讲解下几个地方:

自定义列表中的列表按钮监听:

这里可以通过 lambda 表达式实现每一列控件的每个按钮监听,同时把点击事件的参数传递进去。但是这里注意的是,参数传递只有在列表第一次创建时有效。

# 添加选择按钮
sdk_choose_button = wx.Button(self, label='选择', size=((space_length*1.8)-5, 28))

button_marks = {}  # 存储渠道信息
button_marks[SDK_ID] = str(sdk_id)
button_marks[SDK_NAME] = str(sdk_name)
button_marks[SDK_VERSION] = str(sdk_version)
button_marks[SDK_PATH] = str(sdk_path)

sdk_choose_button.Bind(wx.EVT_BUTTON, lambda evt, mark=button_marks: self.button_checked(evt, mark))

def button_checked(self, event, mark):
    """点击按钮事件"""
    sdk_id = mark[SDK_ID]
    sdk_name = mark[SDK_NAME]
    sdk_version = mark[SDK_VERSION]
    sdk_path = mark[SDK_PATH]
    self.func(sdk_name, sdk_id, sdk_version, sdk_path)
自定义列表中的按钮选择后,动态刷新对应的渠道配置信息:

可以通过在上层窗体自定义一个更新方法,在子窗体创建时作为参数项传递进去,在子窗体控件完成对应事件后将信息回调到父窗体刷新,然后父窗体重新构建子窗体对象。不过要注意的是数据的保存。

示范案例可以看
PackageApkTool/UIMain.py项目中UI主入口分割窗体的具体实现。


# 选择渠道资源后,更新为对应的配置界面
def up_data_ui(self, channel_name, channel_id, channel_version, channel_path):

    # 将每次选择的SDK信息,写到配置文件中
    dir_config = os.path.join(DIR_WorkSpace, DIR_UIConfig)
    config_str = {}
    if channel_path:
        config_str['channel_file_path'] = channel_path
        self.tool_bar_listener.write_config_data(dir_config, UI_CONFIG_PARAMS, config_str)

    if self.new_panel is not None:
        self.old_panel.Destroy()
        self.old_panel = self.new_panel

    self.new_panel = JChannelPanel(self.child_spWindow, self.windowFrame, self.up_data_ui,
                                   channel_name, channel_id, channel_version)  # 创建新的面板
    self.child_spWindow.ReplaceWindow(self.old_panel, self.new_panel)

    self.old_panel.Hide()

# 下载完成后,更新列表
def up_data_list(self):

    if self.new_list_panel is not None:
        self.old_list_panel.Destroy()
        self.old_list_panel = self.new_list_panel

    self.new_list_panel = JChannelSDKListPanel(self.child_spWindow, self.windowFrame, self.up_data_ui)  # 创建新的面板
    self.child_spWindow.ReplaceWindow(self.old_list_panel, self.new_list_panel)

    self.old_list_panel.Hide()
        self.resourcePanel = JChannelSDKListPanel(self.child_spWindow, self.windowFrame, self.up_data_ui)  # 在子分割窗上创建左面板
        self.channelPanel = JChannelPanel(self.child_spWindow, self.windowFrame, self.up_data_ui)  # 在子分割窗上创建右面板
自定义Dialog,弹出后,父窗体设置为不可编辑,销毁父窗体后才可以编辑实现。
dlg = JChannelSignDialog(frame, self.write_sign_info)
if dlg.IsEnabled():
    frame.Enable(enable=False)
    if not dlg.ShowModal() == wx.ID_OK:
        frame.Enable(enable=True)

但是有个坑点就是Windows和Mac机子上表现不一样。销毁父窗体后Mac需要设置属性:self.EndModal(wx.ID_CANCEL)

def cancel_event(self, event):
    system = platform.system()
    if not system == 'Windows':
        self.EndModal(wx.ID_CANCEL)
    self.Destroy()  # 销毁隐藏Dialog

这部分参考
PackageApkTool/JChannelSignUI.py

日志库的日志信息动态刷问题,可通过wx.CallAfter解决:

子线程刷新主线程UI需要注意线程安全问题。

class TextHandler(logging.Handler):

    def __init__(self):
        logging.Handler.__init__(self)
        self.ctrl = None

    def set_ctrl(self, ctrl):
        self.ctrl = ctrl

    def emit(self, record):
        if not self.ctrl == None:
            wx.CallAfter(self.ctrl.write, self.format(record) + "\n")

三、总结

好了,各位老铁,关于UI部分及个人踩坑的实践讲解到这里。详细的代码信息可以到开源库查看。
PackageApkTool,欢迎大伙star及提意见。

关于UI的API,给大伙推荐下地址:https://www.wxpython.org/Phoenix/docs/html/index.html

如果觉得我的文章对你有帮助,请随意赞赏。您的支持将鼓励我继续创作!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342