In Chapter 2, we took a look at how to get started in PyQt, set up the main window, and learned how to create and arrange multiple QLabel widgets to create a simple application. However, none of what we did was very interactive. What good is a user interface if all you can do is stare at it?
You’re in luck because this chapter is all about setting you on the path to making interfaces that are more interactive and responsive. We will take a look at some new fundamental widgets that will help us to build our next project, a functional login GUI. To make things clearer and easier for you to follow along, the login GUI will be divided into two parts, the actual login interface and a new user registration window.
- 1.Learn about new kinds of widgets and classes, including
- a.
QPushButton – One of the most common widgets for giving our computer simple commands
- b.
QLineEdit – Which gives the user fields to input information
- c.
QCheckBox – Which can act as a binary switch
- d.
QMessageBox – Useful for displaying alert or information dialog boxes
- a.
- 2.
Find out about event handling with Signals and Slots in PyQt.
- 3.
Understand the differences between windows and dialog boxes when creating UIs.
Project 3.1 – Login GUI
While it might not seem like much, the login GUI, or the login screen, is probably one of the most common interfaces you interact with on a regular basis. Signing into your computer, your online bank account, e-mail, or social media accounts, logging into your phone, or signing up for some new app, the login GUI is everywhere.
The login GUI can appear to be quite a simple user interface. However, it is actually very complex for a number of reasons. First of all, it acts as the interface that allows us to access our own personal data. You want to create a GUI that clearly labels its widgets, differentiates between where to sign in and where to register a new account, and helps users to better navigate through potential errors, such as if caps lock is on or if the username is incorrect. Secondly, the appearance of the login GUI and methods in which we log in to our devices have changed dramatically over the years, allowing users to log in using Touch ID or their social media accounts. This means that there is no single design that will work for every platform.
Allows the user to enter their username and password and calls a function to check if their information matches one that is stored in a text file
Displays appropriate messages depending upon whether login is successful, if an error has occurred, or if we simply want to close the window
Displays or hides the password by clicking a checkbox
Allows the user to create a new account by clicking a “sign up” button that will open a new window
There are two projects in this chapter, the login GUI and the create new user GUI. They are actually one entire project that has been separated into two parts to make it easier for you to follow along, or for those who only need one part of the project and not the other.
Design the Login GUI
Username and password entry fields
Checkboxes that may remember the user’s login information or reveal the password
Buttons that users can click to log in or even register for a new account
For this project, we will focus on trying to implement most of those features. (The “remember me” checkbox that is common in a lot of login GUIs is beyond the scope of this chapter as it involves using cookies or working with PyQt’s QSettings class.)
For the areas where users will enter their information, we create two separate QLineEdit widgets. Under the password line edit widget, there is a checkbox that the user can check if they want to view or hide the password they entered.
There are two QPushButtons, one that the user can click to log in and the other to register a new account. When the user clicks the login button, we will create a function that is called to check if the user exists. If the user information is correct, we will display a QMessageBox which tells the user that login is successful. Otherwise, another QMessageBox is displayed to alert the user to an error.
If the user’s information does not exist, they can click the sign up button and a new window will appear where they can enter their information. This part is covered in the section “Project 3.2 – Create New User GUI.”
Finally, we will learn how to change the event handler for when the user closes the window. Rather than just closing the application, we will first display a dialog box that will confirm whether or not the user really wants to quit.
The QPushButton Widget
Let’s first take a look at a fundamental widget that you will probably use in almost every GUI you create, QPushButton. The QPushButton can be used to command the computer to perform some kind of operation or answer a question. When you click the QPushButton widget, it sends out a signal that can be connected to a function. Common buttons you might encounter are OK, Next, Cancel, Close, Yes, and No.
Buttons are typically displayed with either a text label or an icon that describes its action or purpose. There are a number of different kinds of buttons with different usages that can be created including QToolButtons and QRadioButtons.
Code for learning how to add QPushButton widgets to your application
Explanation
We begin by importing sys and the necessary PyQt classes including QApplication and QWidget for creating our application object and window, respectively. For this program we will also import the QLabel and QPushButton widgets which are also part of the QtWidgets module.
to connect the signal to the action we want the button to perform, in this case self.buttonClicked. A QPushButton widget can also be set to be activated by the spacebar or using a keyboard shortcut. The buttonClicked function calls self.close() to close the application.
In the preceding example, the signal clicked() is connected to our function. There are also other kinds of signals that the QPushButton can send out including pressed() when the button is down, released() when the button is released, or toggled() that can be used like a binary switch.
Events, Signals, and Slots
Before we go on, you should be introduced to an important concept when building GUI applications in PyQt. GUIs are event-driven , meaning that they respond to events that are created by the user, from the keyboard or the mouse, or by events caused by the system, such as a timer or when connecting to Bluetooth. No matter how they are generated, the application needs to listen for these events and respond to them in some way, also known as event handling . For example, when exec_() is called, the application begins listening for events until it is closed.
In PyQt, event handling is performed with signals and slots. Signals are the events that occur when a widget’s state changes, such as when a button is clicked or a checkbox is toggled on or off. Those signals then need to be handled in some way. Slots are the methods that are executed in response to the signal. Slots are simply Python functions or built-in PyQt functions that are connected to an event and executed when the signal occurs.
When we push on the button, a clicked() signal is emitted. In order to make use of that signal, we must connect() to some callable function, in this case buttonClicked() , which is the slot.
Put simply, widgets send out signals and we collect and use them with slots to make our application perform some action.
Many widgets have predefined signals and slots, meaning you only need to call them in order to get the behavior you want for your application.
The topic of signals and slots and how to make some custom signals will be covered in more detail and with examples in Chapter 6.
The QLineEdit Widget
The next widget we are going to take a look at is the QLineEdit widget. For our login GUI, we need to create areas where the user can input the text for their username and password on a single line. QLineEdit also supports normal text editing functions such as cut, copy and paste, and redo or undo if you need to add those features to your program.
The QLineEdit widget also has a number of methods to add more functionality to your GUI, such as hiding text when it is entered, using placeholder text, or even setting a limit on the length of the text that can be input.
In Listing 3-2 we will take a look at how to set up the QLineEdit widget, retrieve the text using the text() function, and see how to clear the text that the user inputs.
If you need multiple lines to enter text in, use QTextEdit.
Code for learning how to add QLineEdit widgets to your application
Explanation
The user can enter their name into the QLineEdit widget and click the “Clear” QPushButton to clear their text. Other features could include clearing multiple widgets’ states when the button is clicked or checking to make sure the text entered fits the guidelines you need in your application.
If you wish to change this, you could change the flag in SetAlignment from Qt.AlignLeft to Qt.AlignRight or Qt.AlignHCenter.
When the clear_button is clicked, it emits a signal that is connected to the clearEntries() function . In order to determine where the source of a signal is coming from in your applications, you could also use the sender() method . Here, the signal is sent from our button when it is clicked, and if the text on the sender is 'Clear', then the name_entry widget reacts to the signal and clears its current text.
The QCheckBox Widget
The QCheckBox widget is a selectable button that generally has two states, on or off. Since checkboxes normally have only two states, they are perfect for representing features in your GUI that can either be enabled or disabled or for selecting from a list of options like in a survey.
The QCheckBox can also be used for more dynamic applications, as well. For example, you could use the checkbox to change the title of the window or even the text of labels when enabled.
Listing 3-3 shows how to set up a window like in a questionnaire. The user is allowed to select all checkboxes that apply to them, and each time the user clicks a box, we call a function to show how to determine the widget’s current state.
The checkboxes in QCheckBox are not mutually exclusive, meaning you can select more than one checkbox at a time. To make them mutually exclusive, add the checkboxes to a QButtonGroup object.
Code for learning how to add QCheckBox widgets to your application
Explanation
Much of this application starts off similar to before, so let’s jump right into the displayCheckBoxes() method within the CheckBoxWindow class .
The checkbox is created by calling the QCheckBox class and then, as parameters, assigning it text that will appear beside the actual checkbox and its parent window. The toggle() method can be used to toggle the checkbox, and uncommenting the code will cause the widget to begin as enabled when starting the program. When a checkbox’s state changes, rather than using clicked() like with the QPushButton, we can use stateChanged() to send a signal and then connect to our function, printToTerminal().
The QMessageBox Dialog Box
Often when a user is going to close an application, save their work, or an error occurs, a dialog box will pop up and display some sort of key information. The user can then interact with that dialog box, often by clicking a button to respond to the prompt.
The QMessageBox dialog box can not only be used to alert the user to a situation but also to allow them to decide how to handle the issue. For example, if you close a document you just modified, you might get a dialog box asking you to Save, Don’t Save, or Cancel.
Four types of QMessageBox widgets in PyQt. Images from www.riverbankcomputing.com
QMessageBox Icons | Types | Details |
---|---|---|
Question | Ask the user a question. | |
Information | Display information during normal operations. | |
Warning | Report noncritical errors. | |
Critical | Report critical errors. |
Windows vs. Dialogs
When creating a GUI application, you will more than likely come across the terms windows and dialogs. However, windows and dialogs are not the same. Using dialog boxes in an application can make it both easier for you to develop your GUI and for the user to better understand and navigate through your application.
The window generally consists of menus, a toolbar, and other kinds of widgets within it that can often act as the main interface in a GUI application.
A dialog box will appear when the user needs to be prompted for additional information in order to continue, often to gather input such as an image or a file. After that information is given, the dialog box is normally destroyed. Dialog boxes can also be used to display options or information while a user is working in the main window. Most kinds of dialog boxes will have a parent window that will be used to determine the position of the dialog with respect to its owner. This also means that communication occurs between the window and the dialog box and allows for updates in the main window.
There are two kinds of dialog boxes, the modal dialog box and the modeless dialog box. Modal dialogs block user interaction from the rest of the program until the dialog box is closed. Modeless dialogs allow the user to interact with both the dialog and the rest of the application.
How dialog boxes appear and are used can often be influenced by the operating system you use and the guidelines set by that OS.
How to Display a QMessageBox
Code for learning how to display QMessageBox dialogs
Explanation
The GUI in this example consists of a few QLabel widgets, a QLineEdit widget, and a single QPushButton. For this example, you will also see how to set placeholder text in the QLineEdit widget using setPlaceholderText() . This can be helpful for a number of reasons, maybe to make the look of the window less cluttered or to give the user extra information to help them understand the format to use to input text.
To create a QMessageBox dialog, we first call QMessageBox and choose one of the predefined types, in this case question. Then we set the dialog title, "Author Not Found", and the text that we want to appear inside the dialog. This should inform the user about the current situation and, if necessary, notify them of actions they could take. This is followed by the types of buttons that will appear in the dialog, and each button is separated by a pipe key, |. Other types of buttons include Open, Save, Cancel, and Reset. Finally, you can specify which button you want to highlight and set as the default button.
On Mac OS X, when a message box appears, the title is generally ignored due to Mac OS X Guidelines. If you are using a Mac and don’t see a title in the dialog boxes, don’t fear! You haven’t done anything wrong.
Login GUI Solution
Code for login GUI
Your GUI should look similar to the window shown in Figure 3-1.
Explanation
After importing the necessary PyQt5 modules including QtWidgets, QtGui, and QtCore, we also need to import our Registration module which will allow new users to create a new account and then return back to the login GUI to sign in. The Registration module is covered in Project 3.2 in this chapter.
In our LoginUI class that inherits from QWidget, we initialize our GUI and then create the widgets. Refer to Figure 3-2 for the layout. We create a few QLabel widgets to hold information about our GUI and labels for the QLineEdit widgets for the username and password. Widgets are arranged in the window using the move() method .
When the sign_in_button is clicked, it sends a signal that is connected to the clickLogin() method . This function opens the users.txt file (and creates one if it doesn’t exist) and stores each line into a Python dictionary, with the keys being the usernames and the values of the dictionary being the passwords. The text() method is then used to retrieve the input from the two QLineEdit widgets and checks them to see if they match any of the key/value pairs in the users dictionary. While it isn’t the most practical method, this example is a very small one and demonstrates how to use simple text files with your applications. Later we will take a look at how to use SQL to search through databases in Chapter 10.
Hiding Input for QLineEdit
If you ever need to make the text in a QLineEdit widget hidden from other’s view, using SetEchoMode() can change the appearance of the text. By default, setEchoMode() is set to QLineEdit.Normal.
How to Open a New Window
If the user wants to create a new account, then they can click the sign_up button at the bottom of the GUI. This button sends a signal that is connected to the createNewUser() method which calls our CreateNewUser class from the Registration module . A new window is then opened up using show() where the user can enter their personal information.
Changing How the Close Event Works
Finally, currently when we want to quit our programs, we just exit by clicking the button in the top corner of the window. However, a good practice is to present a dialog box confirming whether the user really wants to quit or not. In most programs this will prevent the user from forgetting to save their latest work.
When a QWidget is closed in PyQt, it generates a QCloseEvent. So we need to change how the closeEvent() method is handled. To do so we create a new method called closeEvent() that accepts as a parameter an event.
In this function we create a QMessageBox that asks the user if they are sure about quitting. They then can click either a Yes or No button in the dialog box. We then check the value of the variable stored in quit_msg. If quit_msg is Yes, then the close event is accepted and the program is closed. Otherwise, the event is ignored.
Project 3.2 – Create New User GUI
The first time someone uses your applications you may want them to sign up and create their own username and passwords. This, of course, can allow them to personalize their accounts and then save that information for the next time they log in. The kind of information that you need from the user can range from very simple, name and gender, all the way to extremely private, Social Security numbers or bank account information. Making sure that the information that they enter is correct is very important.
Creating a New User GUI Solution
For the following project, we will have the user enter their desired username, their real name, a password, and then reenter that password to double check that it is correct. When the user clicks the sign up button, the text in the password fields will be checked for a match, and if so, the information will be saved to a text file. The user can then return to the login screen and log in.
Code for creating a new user account GUI
The completed create new user GUI can be seen in Figure 3-10.
Explanation
The create new user GUI is mainly comprised of a few QLabel widgets, four QLineEdit widgets, and a QPushButton widget that the user can click when the form is complete as can be seen in Figure 3-10.
Summary
In this chapter we took a look at some new widgets, QPushButton, QLineEdit, QCheckBox, and QMessageBox class. It is important to use dialog boxes in your program when you want the user to collect information outside of the application or to relay important details to the user. But you also should not have a dialog box pop up for every little nuance or with very little helpful information.
The applications in this chapter are by no means complete. They are the framework to get you started making your own GUIs. For example, you could make sure that the user’s password includes capital and lowercase letters and other characters to make it more safe. Another possibility is to let the user know if the username they want to create already exists. Once you learn how to implement menus, you could even have the user search through their files for a profile image. I encourage you to try and implement some of these ideas or even your own ideas.
In the following chapter, we will learn about layout management in PyQt.