各位老铁。欢迎来看打包系统番外篇-桌面打包工具UI设计篇。这篇文章将讲解PackageApkTool工具的UI实现过程。
大家可以先看看整体UI优化的最终版本效果图:
一、UI设计篇
说实话在做UI界面的时候,确实不知道怎么入手,不过幸好易接打包工具可以参考UI样式。如下图
整个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()
第二步,给整体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() # 分割
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])
参数项循环数据,实现渠道配置列表:
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)
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)
好了至此,完整的界面代码就已经实现。
#!/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
如果觉得我的文章对你有帮助,请随意赞赏。您的支持将鼓励我继续创作!