Search code examples
pythonwxpython

Panel arrangement in wxpython


Background As a newbie to python as well as to GUI Programming, I would like to create a wxpython based GUI application. This GUI will run a series of tests The tests written in text files will be running and it's steps should be displayed in the Window. For selecting a particular test,click the checkbox (CheckListCtrl in Figure below) for that particular test. The result of the test will be displayed in MulilineTextCtrl_2Log

The window should be like this :

My ASCII art is not so good but anyways

|-------------------------------------------------------------|  
|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|     
|_____________________________________________________________|

I have having trouble over creating the boxsizers for this window. I am having trouble visualizing the creation of panels

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


Solution

  • I recommend to you the use of wxglade to design your GUIs. It is fast and effective and after you have some experience you can do most of the things you can do manually.

    wxglade does not have wx.lib widgets suchs as CheckListCtrlMixin or ListCtrlAutoWidthMixin that you are using or combinations such as your class CheckListCtrl.

    In order to be able to use these, fill temporarily a slot in the GUI with an available widget (p.e. a wx.panel) and then later, after generating the code, substitute manually the wx.Panel instantiation with that of your special or custom class.

    Finally, your code has some major problems that produce exceptions such as wx.frame (for wx.Frame), the bad indentation or the undefined TestList. Your MainGUI class has not __init__ method and does neither initialize the parent wx.Frame. Also the bottomPanel is added to hbox sizer but not the topPanel.

    The organization you wrote does not produce the GUI structure you describe but that of two panels with several widgets:

    enter image description here

    To be closer to your description you need at least to add a couple of additional sizers. Use wxglade and it will do everything for you.

    Here you have the wxglade generated code for a design like yours (substitute the wx.ListCtrl with your 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()
    

    And the look:

    enter image description here

    Note, as said in other answer, there is not need for putting your widgets in panels (widget(panel,..)) and the panels in sizers. It works, but it is better to simply put your widgets (as child of the frame -> widget(self,..)) in the sizers (sizerx.Add(widget,..)) and sizers in sizers (sizery.Add(sizerx)) to build the structure.
    Normally you do not modify this code. You subclass it instead to add your functionality or whatever fancy addition you need. In this way you can always use again wxglade to make changes in the position of the widgets ot to add additional widgets or sizers and regenerate the full file.