wxpython中的面板布局
背景
作为一个刚接触Python和图形用户界面(GUI)编程的新手,我想创建一个基于wxpython的GUI应用程序。这个应用程序会运行一系列测试,这些测试的内容保存在文本文件里,测试的步骤会在窗口中显示出来。要选择特定的测试,只需点击那个测试旁边的复选框(在下面的图中是CheckListCtrl)。测试的结果会显示在MulilineTextCtrl_2Log中。
窗口应该是这样的:
我的ASCII艺术水平不太好,不过无所谓。
|-------------------------------------------------------------|
|WINDOW TITLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_[]X|
|xxx[SelectAll]xxxxxx[DeselectAll]xxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|[CheckListCtrl] [MultilineTexTCtrl_1]xxxxxxxxxxxxxxxxxxxxxxxx|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx[Refresh]xxx|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx[Execute]xxx|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|xxxxxxx[MulilineTextCtrl_2Log]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|_____________________________________________________________|
我在创建这个窗口的盒子布局时遇到了困难。我很难想象如何创建面板。
TestList = [('Test1', 'Test1Description1', 'base'), ('Test2', 'Test1Description1', 'base'),'Test3', 'Test1Description3', 'base'),]
class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
def __init__(self, parent):
wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
CheckListCtrlMixin.__init__(self)
ListCtrlAutoWidthMixin.__init__(self)
class MainGUI(wx.frame):
panel = wx.Panel(self, -1)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox = wx.BoxSizer(wx.HORIZONTAL)
#topPanel = wx.Panel(panel,-1)
topPanel = wx.Panel(panel, -1,pos=(0,0))
bottomPanel = wx.Panel(panel, -1,pos=(1,0))
self.log = wx.TextCtrl(bottomPanel, -1, style=wx.TE_MULTILINE)
self.list = CheckListCtrl(bottomPanel)
self.list.InsertColumn(0, 'TestID', width=140)
self.list.InsertColumn(1, 'TestDescription')
for i in TestList:
index = self.list.InsertStringItem(sys.maxint, i[0])
self.list.SetStringItem(index, 1, i[1])
self.list.SetStringItem(index, 2, i[2])
vbox2 = wx.BoxSizer(wx.VERTICAL)
sel = wx.Button(topPanel, -1, 'Select All', size=(100, -1))
des = wx.Button(topPanel, -1, 'Deselect All', size=(100, -1))
refresh = wx.Button(bottomPanel, -1, 'Refresh', size=(100, -1))
apply = wx.Button(bottomPanel, -1, 'Execute', size=(100, -1))
self.Bind(wx.EVT_BUTTON, self.OnSelectAll, id=sel.GetId())
self.Bind(wx.EVT_BUTTON, self.OnDeselectAll, id=des.GetId())
self.Bind(wx.EVT_BUTTON, self.OnRefresh, id=refresh.GetId())
self.Bind(wx.EVT_BUTTON, self.OnExecute, id=apply.GetId())
vbox2.Add(sel, 0, wx.TOP, 5)
vbox2.Add(des)
vbox2.Add(apply,wx.RIGHT)
vbox2.Add(refresh)
topPanel.SetSizer(vbox2)
vbox.Add(self.list, 1, wx.EXPAND, 3)
vbox.Add((-1, 10))
vbox.Add(self.log, 0.5, wx.EXPAND)
vbox.Add((-1, 10))
bottomPanel.SetSizer(vbox)
hbox.Add(bottomPanel, 0, wx.EXPAND | wx.RIGHT, 5)
panel.SetSizer(hbox)
self.Centre()
self.Show(True)
def OnSelectAll(self, event):
num = self.list.GetItemCount()
for i in range(num):
self.list.CheckItem(i)
def OnDeselectAll(self, event):
num = self.list.GetItemCount()
for i in range(num):
self.list.CheckItem(i, False)
def OnRefresh(self, event):
num = self.list.GetItemCount()
for i in range(num):
if i == 0: self.log.Clear()
if self.list.IsChecked(i):
self.log.AppendText(self.list.GetItemText(i) + '\n')
def OnExecute(self, event):
num = self.list.GetItemCount()
for i in range(num):
if i == 0: self.log.Clear()
if self.list.IsChecked(i):
self.log.AppendText(self.list.GetItemText(i) + '\n')
app = wx.App()
MainGUI(None, -1, 'Tester')
app.MainLoop()
2 个回答
其实你并不需要那么多额外的面板。实际上,除了像wx.Notebooks这样的情况,你几乎只需要一个面板。对我来说,最简单的方式是先在纸上画个草图,然后给各个部分画个框。这样的话,我会用一个竖着的主BoxSizer。接着,我会用两个横着的BoxSizers来放按钮和其他控件。如果你需要把其他三个单独的控件也这样放,那你可能还需要一些带间隔的BoxSizers。总之,一旦你把这些控件放进不同的BoxSizers里,就可以把它们加到主Sizer里,这样就完成了。
或者你也可以用一个GridSizer,再加几个间隔来处理最后三个控件。
我推荐你使用wxglade来设计你的图形用户界面(GUI)。这个工具既快速又有效,等你有了一些经验后,你可以做大部分手动操作能做到的事情。
wxglade不支持你正在使用的wx.lib小部件,比如CheckListCtrlMixin
或ListCtrlAutoWidthMixin
,也不支持像你的CheckListCtrl
这样的组合。
为了能够使用这些小部件,你可以暂时在GUI中放一个可用的小部件(比如wx.panel),然后在生成代码后,手动把wx.Panel的实例替换成你自己的特殊类。
最后,你的代码有一些主要问题,会导致异常,比如wx.frame(应该是wx.Frame)、错误的缩进或未定义的TestList。你的MainGUI类没有__init__
方法,也没有初始化父类wx.Frame
。另外,bottomPanel
被添加到了hbox
的布局中,但topPanel
却没有。
你写的组织结构并没有产生你描述的GUI,而是两个面板和几个小部件的结构:
为了更接近你的描述,你至少需要再添加几个布局。使用wxglade,它会为你处理所有事情。
这里有一个wxglade生成的代码,适合你的设计(把wx.ListCtrl
替换成你的CheckListCtrl
):
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# generated by wxGlade HG on Tue Oct 04 12:45:03 2011
import wx
# begin wxGlade: extracode
# end wxGlade
class MainGUI(wx.Frame):
def __init__(self, *args, **kwds):
# begin wxGlade: MainGUI.__init__
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.button_3 = wx.Button(self, -1, "Select All")
self.button_4 = wx.Button(self, -1, "DeselectAll")
self.list_ctrl_1 = wx.ListCtrl(self, -1, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
self.text_ctrl_1 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
self.button_1 = wx.Button(self, -1, "Refresh")
self.button_2 = wx.Button(self, -1, "Execute")
self.text_ctrl_2 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
self.__set_properties()
self.__do_layout()
# end wxGlade
def __set_properties(self):
# begin wxGlade: MainGUI.__set_properties
self.SetTitle("frame_1")
self.SetBackgroundColour(wx.Colour(255, 170, 5))
self.list_ctrl_1.SetMinSize((200, 264))
# end wxGlade
def __do_layout(self):
# begin wxGlade: MainGUI.__do_layout
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
sizer_3 = wx.BoxSizer(wx.VERTICAL)
sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
sizer_4.Add(self.button_3, 0, wx.ALL, 5)
sizer_4.Add(self.button_4, 0, wx.ALL, 5)
sizer_4.Add((20, 20), 1, wx.EXPAND, 0)
sizer_1.Add(sizer_4, 0, wx.EXPAND, 0)
sizer_2.Add(self.list_ctrl_1, 0, wx.ALL|wx.EXPAND, 5)
sizer_2.Add(self.text_ctrl_1, 0, wx.ALL|wx.EXPAND, 5)
sizer_3.Add(self.button_1, 0, wx.ALL, 5)
sizer_3.Add(self.button_2, 0, wx.ALL, 5)
sizer_2.Add(sizer_3, 0, wx.EXPAND, 0)
sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
sizer_1.Add(self.text_ctrl_2, 0, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
# end wxGlade
# end of class MainGUI
if __name__ == "__main__":
app = wx.PySimpleApp(0)
wx.InitAllImageHandlers()
frame_1 = MainGUI(None, -1, "")
app.SetTopWindow(frame_1)
frame_1.Show()
app.MainLoop()
还有它的外观:
注意,正如其他回答中提到的,不需要把小部件放在面板中(widget(panel,..)
)再把面板放在布局中。这样是可以的,但更好的做法是直接把小部件放在框架下(widget(self,..)
),然后把它们放入布局中(sizerx.Add(widget,..)
),再把布局放入其他布局中(sizery.Add(sizerx)
)来构建结构。
通常你不需要修改这段代码,而是通过子类化来添加你的功能或其他需要的内容。这样你就可以随时使用wxglade来改变小部件的位置,或者添加额外的小部件或布局,并重新生成完整的文件。