wxPython: How to Create a Login Dialog

I’ve been using wxPython for quite a while now and I see certain questions come up on a fairly frequent basis. One of the popular ones is how to ask the user for their credentials before loading up the rest of the application. There are several approaches to this, but I am going to focus on a simple solution as I believe this solution can be used as the basis for more complex solutions.

Basically what we want to happen is for the user to see a login dialog where they have to enter their username and password. If they enter it correctly, then the program will continue to load and they’ll see the main interface. You see this a lot on websites with a common use case being web email clients. Desktop applications don’t include this functionality as often, although you will see it for Stamps.com’s application and for law enforcement software. We will be creating a dialog that looks like this:

wxLogin.png

Let’s take a look at some code:

import wx

if "2.8" in wx.version():
    import wx.lib.pubsub.setupkwargs
    from wx.lib.pubsub import pub
else:
    from wx.lib.pubsub import pub


########################################################################
class LoginDialog(wx.Dialog):
    """
    Class to define login dialog
    """

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Dialog.__init__(self, None, title="Login")
        
        # user info
        user_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        user_lbl = wx.StaticText(self, label="Username:")
        user_sizer.Add(user_lbl, 0, wx.ALL|wx.CENTER, 5)
        self.user = wx.TextCtrl(self)
        user_sizer.Add(self.user, 0, wx.ALL, 5)
        
        # pass info
        p_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        p_lbl = wx.StaticText(self, label="Password:")
        p_sizer.Add(p_lbl, 0, wx.ALL|wx.CENTER, 5)
        self.password = wx.TextCtrl(self, style=wx.TE_PASSWORD|wx.TE_PROCESS_ENTER)
        p_sizer.Add(self.password, 0, wx.ALL, 5)
        
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(user_sizer, 0, wx.ALL, 5)
        main_sizer.Add(p_sizer, 0, wx.ALL, 5)
        
        btn = wx.Button(self, label="Login")
        btn.Bind(wx.EVT_BUTTON, self.onLogin)
        main_sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
        
        self.SetSizer(main_sizer)
        
    #----------------------------------------------------------------------
    def onLogin(self, event):
        """
        Check credentials and login
        """
        stupid_password = "pa$$w0rd!"
        user_password = self.password.GetValue()
        if user_password == stupid_password:
            print "You are now logged in!"
            pub.sendMessage("frameListener", message="show")
            self.Destroy()
        else:
            print "Username or password is incorrect!"
            
########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
    
########################################################################
class MainFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Main App")
        panel = MyPanel(self)
        pub.subscribe(self.myListener, "frameListener")
        
        # Ask user to login
        dlg = LoginDialog()
        dlg.ShowModal()
        
    #----------------------------------------------------------------------
    def myListener(self, message, arg2=None):
        """
        Show the frame
        """
        self.Show()
        
if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame()
    app.MainLoop()

The majority of this code is taken up by the subclass of wx.Dialog that we are calling LoginDialog. You will notice that we have set the password text control widget to use the wx.TE_PASSWORD style, which will hide the characters that the user types into that control. The event handler is where the real action is. Here we define a silly password that we use to compare to the one that the user enters. In the real world, you would probably take a hash of the password that is entered and compare it to one that is stored in a database. Or you might send the credentials to your authentication server and have it tell you if the user’s credentials are legitimate or not. For demonstration purposes, we opt for the simple approach and just check the password. You will notice that we completely ignore what the user enters for a username. This is not realistic, but again, this is just an example.

Anyway, if the user enters the correct password, the event handler sends a message via pubsub to our MainFrame object telling it to finish loading and then the dialog is destroyed. There are other ways to tell the main frame to continue, such as using a flag in the dialog class that we can check against. Here is an implementation that demonstrates this latter method:

import wx

########################################################################
class LoginDialog(wx.Dialog):
    """
    Class to define login dialog
    """

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Dialog.__init__(self, None, title="Login")
        self.logged_in = False
        
        # user info
        user_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        user_lbl = wx.StaticText(self, label="Username:")
        user_sizer.Add(user_lbl, 0, wx.ALL|wx.CENTER, 5)
        self.user = wx.TextCtrl(self)
        user_sizer.Add(self.user, 0, wx.ALL, 5)
        
        # pass info
        p_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        p_lbl = wx.StaticText(self, label="Password:")
        p_sizer.Add(p_lbl, 0, wx.ALL|wx.CENTER, 5)
        self.password = wx.TextCtrl(self, style=wx.TE_PASSWORD|wx.TE_PROCESS_ENTER)
        self.password.Bind(wx.EVT_TEXT_ENTER, self.onLogin)
        p_sizer.Add(self.password, 0, wx.ALL, 5)
        
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(user_sizer, 0, wx.ALL, 5)
        main_sizer.Add(p_sizer, 0, wx.ALL, 5)
        
        btn = wx.Button(self, label="Login")
        btn.Bind(wx.EVT_BUTTON, self.onLogin)
        main_sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
        
        self.SetSizer(main_sizer)
        
    #----------------------------------------------------------------------
    def onLogin(self, event):
        """
        Check credentials and login
        """
        stupid_password = "pa$$w0rd!"
        user_password = self.password.GetValue()
        if user_password == stupid_password:
            print "You are now logged in!"
            self.logged_in = True
            self.Close()
        else:
            print "Username or password is incorrect!"
            
########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
    
########################################################################
class MainFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Main App")
        panel = MyPanel(self)
        
        # Ask user to login
        dlg = LoginDialog()
        dlg.ShowModal()
        authenticated = dlg.logged_in
        if not authenticated:
            self.Close()
        
        self.Show()
        
if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame()
    app.MainLoop()

In this example, we added a flag in the dialog subclass that we called self.logged_in. If the user enters the correct password, we tell the dialog to close. This causes wxPython to return control back to the MainFrame class where we check that variable to see if the user is logged in or not. If they are not, we close the application. Otherwise we load the application.


Wrapping Up

There are a few enhancements we could add, such as setting the focus to the first text control or adding a cancel button. I’m sure you can think of a few others yourself. Overall though, this should get you started.

2 thoughts on “wxPython: How to Create a Login Dialog”

  1. A slightly amusing anecdote.

    I knew an entrepreneur who had assembled all the materials for a mini-membership site which required people to pay a subscription to view.

    He hadn’t seen this blog post so he put up an entry screen that required the usual

    Username:
    Password:

    What most people didn’t know was that they could type anything into these boxes and the software automatically progressed to the teaching material!

    It’s a sign of how accustomed we are to expecting refusal when we make even tiny errors when trying to log-in to our e-mail accounts, that on just seeing those 2 data-entry boxes we expect them to keep us out without the right credentials!

    Reminds me of the student who returned to college after 11pm when the gates were locked. He looked for a tree to help him climb the 10 foot wall and nearly broke his leg on landing.

    While walking to his room he heard a creaking noise and looked around and saw the gate open as a gust of wind blew.

  2. Pingback: Why does this wxPython GUI freeze when self.Destroy() is used instead of self.Close()? (example attached) – PythonCharm

Comments are closed.