Search code examples
wxpython

Difficulty trying to use flags to move button widget in wxPython


I am having some difficulty placing 2 buttons centered in the bottom using flags. The output is this when I try, and I don't understand why they don't align to the bottom center.:

enter image description here

With the following code:

#!/usr/bin/python

# gui.py

import wx
import webbrowser

class PanelOne(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent)

class PanelTwo(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent)

class GUI(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(GUI, self).__init__(*args, **kwargs)

        self.poo()

    def poo(self):
        self.CreateStatusBar()
        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        self.add_entry = wx.MenuItem(fileMenu, -1, '&Add Entry\tShift+A', 'Add employee')
        quit = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tEsc', 'Quit application')
        fileMenu.AppendItem(self.add_entry)
        fileMenu.AppendItem(quit)
        menubar.Append(fileMenu, '&Options')
        self.SetMenuBar(menubar)

        self.panel_one = PanelOne(self)
        self.panel_two = PanelTwo(self)
        self.panel_two.Hide()

        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.panel_one, 1, wx.EXPAND)
        vbox.Add(self.panel_two, 1, wx.EXPAND)
        self.SetSizer(vbox)

        pv1box = wx.BoxSizer(wx.HORIZONTAL)
        self.panel_one.SetSizer(pv1box)

        self.chrome_button = wx.Button(self.panel_one, id=2, label='Gmail')
        self.outlook_button = wx.Button(self.panel_one, id=1, label='Outlook')

        pv1box.Add(self.chrome_button, flag=wx.ALIGN_BOTTOM | wx.ALIGN_CENTER | wx.ALIGN_RIGHT)
        pv1box.Add(self.outlook_button, flag=wx.ALIGN_BOTTOM | wx.ALIGN_CENTER | wx.ALIGN_RIGHT)


        self.SetSize((450, 300))
        self.SetTitle('Prototype')
        self.Centre()
        self.Show(True)


        self.Bind(wx.EVT_MENU, self.onQuit, quit)
        self.Bind(wx.EVT_KEY_DOWN, self.onESCQuit, quit)
        self.Bind(wx.EVT_MENU, self.onSwitchPanels, self.add_entry)
        self.chrome_button.Bind(wx.EVT_BUTTON, self.onClick)
        self.outlook_button.Bind(wx.EVT_BUTTON, self.onClick)


    def onQuit(self, e):

        dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
        input = dial.ShowModal()

        if input == wx.ID_YES:
            self.Close()

    def onESCQuit(self, e):
        esc = e.GetKey()

        if esc == wx.WXK_ESCAPE:
            dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
            input = dial.ShowModal()

            if input == wx.ID_YES:
                self.Close()

    def onSwitchPanels(self, event):
        if self.panel_one.IsShown():
            self.add_entry.SetText('Notifications')
            self.panel_one.Hide()
            self.panel_two.Show()
        else:
            self.add_entry.SetText('Add Entry')
            self.panel_one.Show()
            self.panel_two.Hide()

    def onClick(self, event):
        if event.Id == self.chrome_button.Id: 

            chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'
            # Without this path it will open in Internet Explorer
            webbrowser.get(chrome_path).open('https://mail.google.com')
        else:
            chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'
            # Without this path it will open in Internet Explorer
            webbrowser.get(chrome_path).open('https://outlook.live.com')


def main():

    app = wx.App()
    GUI(None)
    app.MainLoop()

if __name__ == '__main__':
    main()

Solution

  • There is a single sizer set to the frame, the two panels are parented to the frame so this sizer is positioning them on the frame ok. The two buttons are parented to each of the panels but the frame sizer is trying to position them on the frame when they are on the panels. The panels will need a sizer each so they can position the buttons on the panels.

    you have

    Frame
    --->vbox -> set to frame
    --->panel_one -> added to vbox
    ------->chrome_button -> added to vbox
    --->panel_two -> added to vbox
    ------->outlook_button -> added to vbox
    

    you need

    Frame
    --->vbox -> set to frame
    --->panel_one -> added to vbox
    ------->p1vbox(new sizer) -> set to panel_one
    ------->chrome_button -> added to p1vbox
    --->panel_two -> added to vbox
    ------->p2vbox(new sizer) -> set to panel_two
    ------->outlook_button -> added to p2vbox
    

    With the new code add a stretch spacer either side of the buttons to make them position in the center.

        pv1box.AddStretchSpacer()
        pv1box.Add(self.chrome_button, flag=wx.ALIGN_BOTTOM | wx.ALIGN_CENTER | wx.ALIGN_RIGHT)
        pv1box.Add(self.outlook_button, flag=wx.ALIGN_BOTTOM | wx.ALIGN_CENTER | wx.ALIGN_RIGHT)
        pv1box.AddStretchSpacer()