Search code examples
pythontextboxdialogwxpython

wxPython - checking for empty text dialogs


I have some python code, using wxpython, that will do the following:

  1. ask the user for a number using a wxpython dialog box
  2. check if that number is currently in use, if it is the box appears asking for a number again
  3. Once an unused number has been entered, a new dialog will appear asking for a name. If in use, it will ask again
  4. Both the new number and name are stored for later use

The code looks something like this:

while True:
            codeNumberPrompt = wx.TextEntryDialog(self, "New code #: ", "Add New Code", str(len(self.codes)), style=wx.OK)
            codeNumberPrompt.ShowModal()
            self.codeNumber = codeNumberPrompt.GetValue()

            #check if number is in use
            if int(self.codeNumber) not in self.numbersInUse:
                #new code name input
                while True:
                    codeNamePrompt = wx.TextEntryDialog(self, "New code name: ", "Add New Code", style=wx.OK)
                    codeNamePrompt.ShowModal()
                    self.codeName = codeNamePrompt.GetValue()

                    #check if name is in use
                    if self.codeName.lower() not in self.namesInUse:
                        break

                    wx.MessageBox('Code name is currently in use', 'Warning', wx.OK | wx.ICON_INFORMATION)
                break
            else:
                wx.MessageBox('Code number is currently in use', 'Warning', wx.OK | wx.ICON_INFORMATION)

My first question, is there a better/cleaner way to do this?

My second question (where I get stuck): I need to be able to handle some exception cases. For example:

  • if the user X's out of the first dialog, I dont want the second dialog box to show up and I want to proceed as if nothing happened (i.e. doesnt try to store the number or name values)
  • if the user leaves the number or name dialog entry blank, I want to ask them for that info again

I cant seem to find out how wxpython handles the X button in dialog boxes. Currently, if the user leaves a default value in the first (number) box, and then X's, the second box (name) will still show up


Solution

  • You should be able to catch the wx.CLOSE event and prevent a dialog from closing. However, I believe you would be better off using a wizard-type approach. In the first step, you would check the number and then hit Next. Then you would accept the name and close out of the dialog. I feel like that would be a more natural flow for the user.

    I put together a fun little example, but you will note that I didn't save the data in any meaningful way. You'll want to add that yourself:

    import wx
    
    ########################################################################
    class MyDialog(wx.Dialog):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self):
            """Constructor"""
            wx.Dialog.__init__(self, None, title="Information")
    
            self.numbers = [1, 2, 3]
            self.names = ['mike', 'nick', 'george']
    
            #--------------------------------------------------
            # create the number panel
            self.number_panel = wx.Panel(self)
            self.number_text = wx.TextCtrl(self.number_panel)
            self.number_text.SetFocus()
            next_btn = wx.Button(self.number_panel, label='Next')
            next_btn.Bind(wx.EVT_BUTTON, self.onNext)
            panel_sizer = wx.BoxSizer(wx.VERTICAL)
            panel_sizer.Add(self.number_text, 0, wx.ALL|wx.EXPAND, 5)
            panel_sizer.Add(next_btn, 0, wx.ALL, 5)
            self.number_panel.SetSizer(panel_sizer)
    
            #--------------------------------------------------
            # create the name panel
            self.name_panel = wx.Panel(self)
            self.name_panel.Hide()
            self.name_text = wx.TextCtrl(self.name_panel)
    
            ok_btn = wx.Button(self.name_panel, label='OK')
            ok_btn.Bind(wx.EVT_BUTTON, self.onOK)
    
            name_sizer = wx.BoxSizer(wx.VERTICAL)
            name_sizer.Add(self.name_text, 0, wx.ALL, 5)
            name_sizer.Add(ok_btn, 0, wx.ALL, 5)
            self.name_panel.SetSizer(name_sizer)
    
            #--------------------------------------------------
            # layout panels
            self.my_sizer = wx.BoxSizer(wx.VERTICAL)
            self.my_sizer.Add(self.number_panel, 1, wx.EXPAND)
            self.my_sizer.Add(self.name_panel, 1, wx.EXPAND)
            self.SetSizer(self.my_sizer)
    
            #--------------------------------------------------
            self.ShowModal()
            wx.CallAfter(self.my_sizer.Layout)
    
        #----------------------------------------------------------------------
        def onNext(self, event):
            """"""
            number = int(self.number_text.GetValue())
            if number in self.numbers:
                wx.MessageBox('Code number is currently in use', 
                              'Warning', wx.OK | wx.ICON_INFORMATION)
                self.number_text.SetFocus()
                return
            else:
                self.my_sizer.Detach(self.number_panel)
                self.name_panel.Show()
                self.Layout()
                self.numbers.append(number)
    
        #----------------------------------------------------------------------
        def onOK(self, event):
            """"""
            name = self.name_text.GetValue()
            if name.lower() in self.names:
                wx.MessageBox('Code name is currently in use', 
                              'Warning', wx.OK | wx.ICON_INFORMATION)
                return
            else:
                self.names.append(name)
                self.Close()
    
    
    ########################################################################
    class MyPanel(wx.Panel):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            """Constructor"""
            wx.Panel.__init__(self, parent)
    
            info_button = wx.Button(self, label="Get Info")
            info_button.Bind(wx.EVT_BUTTON, self.onGetInfo)
    
        #----------------------------------------------------------------------
        def onGetInfo(self, event):
            """"""
            dlg = MyDialog()
            dlg.Destroy()
    
    
    ########################################################################
    class MyFrame(wx.Frame):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self):
            """Constructor"""
            wx.Frame.__init__(self, None, title='Demo')
            panel = MyPanel(self)
            self.Show()
    
    if __name__ == '__main__':
        app = wx.App(False)
        frame = MyFrame()
        app.MainLoop()