Build a Contact Book With Python, PyQt, and SQLite (original) (raw)

Building projects is arguably one of the more approachable and effective ways of learning to program. Real projects require you to apply different and varied coding skills. They also encourage you to research topics that pop up as you’re solving problems in the development process. In this tutorial, you’ll create a contact book application with Python, PyQt, and SQLite.

In this tutorial, you’ll learn how to:

At the end of this project, you’ll have a functional contact book application that will allow you to store and manage your contact information.

To get the complete source code for the application as well as the code for every step you’ll go through in this tutorial, click the link below:

Contact books are a useful and widely used kind of application. They’re everywhere. You probably have a contact book on your phone and on your computer. With a contact book, you can store and manage contact information for your family members, friends, coworkers, and so on.

In this tutorial, you’ll code a contact book GUI application with Python, SQLite, and PyQt. Here’s a demo of how your contact book will look and work after you follow the steps in this tutorial:

Your contact book will provide the minimal required set of features for this kind of application. You’ll be able to display, create, update, and remove the information in your contacts list.

Project Overview

To build your contact book application, you need to organize the code into modules and packages and give your project a coherent structure. In this tutorial, you’ll use the following directories and files structure:

rpcontacts_project/ │ ├── rpcontacts/ │ ├── __init__.py │ ├── views.py │ ├── database.py │ ├── main.py │ └── model.py │ ├── requirements.txt ├── README.md └── rpcontacts.py

Here’s a brief summary of the contents of your project directory:

You’ll cover each of these files step by step in this tutorial. The name of each file gives an idea of its role in the application. For example, views.py will contain the code to generate the GUI of windows and dialogs, database.py will contain code to work with the database, and main.py will host the application itself. Finally, model.py will implement the model to manage the data in the application’s database.

In general, the application will have a main window to display, add, remove, and update contacts. It’ll also have a dialog to add new contacts to the database.

Prerequisites

To get the most out of this project, some previous knowledge of GUI programming with Python and PyQt would help. In this regard, you’ll need to know the basics of how to:

To brush up on these topics, you can check out the following resources:

Don’t worry if you’re not an expert in these areas before starting this tutorial. You’ll learn through the process of getting your hands dirty on a real project. If you get stuck, then take your time and review the resources linked above. Then get back to the code.

The contact book application you’re going to build in this tutorial has a single external dependency: PyQt.

To follow best practices in your development process, you can start by creating a virtual environment and then installing PyQt using pip. Once you’ve installed PyQt, you’re ready to start coding!

In this first step, you’ll create a minimal but functional PyQt GUI application to provide the foundation on which you’ll start building the contact book. You’ll also create the minimal required project structure, including the project’s main package and an entry-point script to run the application.

All the code and files you’ll add to the contact book project in this section are collected under the source_code_step_1/ directory. You can download them by clicking the link below:

By the end of the this section, you’ll be able to run the skeleton GUI application for your contact book for the first time.

Structuring the Contact Book Project

To start coding the application, go ahead and create a new directory called rpcontacts_project/. This will be the project’s root directory. Now create a new subdirectory called rpcontacts/ inside rpcontacts_project/. This subdirectory will hold the application’s main package. Finally, fire up your code editor or IDE within the root directory.

To turn a directory into a package, Python needs an __init__.py module to initialize the package. Create this file within rpcontacts/ and add the following code to it:

This file tells Python that rpcontacts is a package. The code in the file runs when you import the package or some of its modules.

You don’t need to put any code in an __init__.py file to initialize the package. An empty __init__.py file will do the job. However, in this case, you define a module-level constant called __version__ to hold the version number of your application.

Creating the Application’s Main Window

Now it’s time to create your contact book’s main window. To do that, create a module called views.py in your rpcontacts package. Then add the following code to the module and save it:

First, you import the required classes from PyQt5.QtWidgets. Then you create Window. This class inherits from QMainWindow and provides the code to generate the application’s main window. In the initializer method, you set the window’s title to "RP Contacts", resize the window to 550 by 250 pixels, define and set the central widget using QWidget, and finally define a layout for the central widget using a horizontal box layout.

Coding and Running the Application

Since you already have a main window for the contact book, it’s time to write the code for creating a functional PyQt application using QApplication. To do that, create a new module called main.py in your rpcontacts package and add the following code to it:

In this module, you import sys to get access to exit(), which allows you to cleanly exit the application when the user closes the main window. Then you import QApplication from PyQt5.QtWidgets and Window from views. The final step is to define main() as your application’s main function.

Inside main(), you instantiate QApplication and Window. Then you call .show() on Window, and finally you run the application’s main loop, or event loop, using .exec().

Now move up to the project root directory rpcontacts_project/ and create a file called rpcontacts.py. This file provides the entry-point script to run the application. Add the following code to the file and save it:

This file imports main() from your main.py module. Then you implement the traditional conditional statement that calls main() if the user runs this module as a Python script. Now launch the application by running the command python rpcontacts.py in your Python environment. You’ll get the following window on your screen:

Contact Book App Skeleton

That’s it! You’ve created a minimal but functional PyQt GUI application that you can use as a starting point for building your contact book. At this point, your project should have the following structure:

./rpcontacts_project/ │ ├── rpcontacts/ │ ├── __init__.py │ ├── views.py │ └── main.py │ └── rpcontacts.py

In this section, you’ve created the minimal required structure for your contact book project using Python modules and packages. You’ve built the application’s main window and put together the boilerplate code to create a PyQt GUI application. You’ve also run the application for the first time. Next, you’ll start adding features to your GUI.

Now that you’ve built the skeleton of your contact book application, you can start coding the main window’s GUI. At the end of this section, you’ll have completed the required steps to create the GUI of your contact book using Python and PyQt. The GUI will look like this:

Contact Book Main Window

In the center of the window, you have a table view to display your contacts list. At the right side of the form, you have three buttons:

  1. Add to add a new contact to the list
  2. Delete to remove a selected contact from the list
  3. Clear All to remove all the contacts from the list

All the code and files you’ll add or modify in this section are collected under the source_code_step_2/ directory. You can download them by clicking the link below:

Get back to the views.py module and update the code of Window to generate the above GUI:

You first import some extra PyQt classes to use in the GUI. Here are some of the more relevant ones:

In this code, the first addition to Window is a call to .setupUI() at the end of __init__(). This call generates the main window’s GUI when you run the application.

Here’s what the code inside .setupUI() does:

With these additions to Window, you can run the application again. The window on your screen will look like the window you saw at the beginning of the section.

In this section, you’ve run all the required steps to create the GUI of your contact book’s main window. You’re now ready to start working on how your application will manage and store your contact data.

At this point, you’ve created a PyQt application and its main window’s GUI to build your contact book project. In this section, you’ll write code to define how the application connects to the contact database. To complete this step, you’ll use SQLite to handle the database and PyQt’s SQL support to connect the application to the database and to work with your contact data.

The source code and files you’ll add or modify in this section are stored under the source_code_step_3/ directory. You can download them by clicking the link below:

First, get back to main.py in the rpcontacts/ directory and update the code to create the connection to the database:

In this case, you first import createConnection() from database.py. This function will contain code to create and open a connection to the contact database. You’ll create database.py and write createConnection() in the next section.

Inside main(), the first highlighted line is an attempt to create a connection to the database using createConnection(). If for some reason the application isn’t able to create a connection, then the call to sys.exit(1) will close the application without creating a graphical element and will indicate that an error has occurred.

You have to handle the connection this way because the application depends on the database to work properly. If you don’t have a functional connection, then your application won’t work at all.

This practice allows you to handle errors and cleanly close the application if a problem occurs. You’ll also be able to present the user with relevant information about the error that the application ran into when trying to connect to the database.

With these additions in place, it’s time to dive into the code of createConnection().

Connecting to the Database With PyQt and SQLite

Connecting your contact book application to its associated database is a fundamental step in developing the application. To do this, you’ll code a function called createConnection(), which will create and open a connection to the database. If the connection is successful, then the function will return True. Otherwise, it will provide information about the cause of the connection failure.

Get back to the rpcontacts/ directory and create a new module called database.py within it. Then add the following code to that module:

Here, you first import some required PyQt classes. Then you define createConnection(). This function takes one argument: databaseName holds the name or path to the physical SQLite database file in your file system.

Here’s what the code inside createConnection() does:

You already coded createConnection(). Now you can write the code to create the contacts tables in the database.

Creating the contacts Table

With the function that creates and opens the connection to the database in place, you can proceed to code a helper function to create the contacts table. You’ll use this table to store the information about your contacts.

Here’s the code that implements _createContactsTable():

Here, you first add a new import. You import QSqlQuery to execute and manipulate SQL statements.

Inside _createContactsTable(), you create a QSqlQuery instance. Then you call .exec() on the query object with a string-based SQL CREATE TABLE statement as an argument. This statement creates a new table called contacts in your database. The table has the following columns:

Column Content
id An integer with the table’s primary key
name A string with the name of a contact
job A string with the job title of a contact
email A string with the email of a contact

The contacts table in your database will store relevant information about your contacts.

The final step to finish coding database.py is to add a call to _createContactsTable() from inside createConnection(), right before the last return statement. This ensures that the application creates the contacts table before doing any operations on the database.

Once you’ve created the contacts table, you can run some tests on the database and also add some sample data for further testing.

Testing the Contact Book’s Database

So far, you’ve finished writing the required code to handle the connection to the contact book’s database. In this section, you’ll perform some tests to make sure that this code and the database itself work properly. You’ll also add some sample data to the database to perform further testing later in this tutorial.

Now open a terminal or command line and move to the project’s root directory, rpcontacts_project/. Once there, launch a Python interactive session and type in the following code:

Here, you first import createConnection() from the database.py module. Then you call this function to create and open a connection to the contact database. The database filename is contacts.sqlite. Since this file doesn’t exist in the project’s root directory, SQLite creates it for you. You can check this by taking a look at your current directory.

Next, you confirm that the database contains a table called contacts. To do that, you call .database() on QSqlDatabase. This class method returns a pointer to the current database connection. With this reference to the connection, you can call .tables() to get the list of tables in the database. Note that the first table in the list is contacts, so now you’re sure that everything is working well.

Now you can prepare an SQL query to insert sample data into the contacts table:

The above query allows you to insert specific values into the name, job, and email attributes and to save those values to the database. Below is an example of how to do this:

In this piece of code, you first define data to hold the contact information of a list of people. Next, you use a for loop to insert the data by calling .addBindValue(). Then you call .exec() on the query object to effectively run the SQL query on the database.

Since all the calls to .exec() return True, you can conclude that the data was successfully inserted into the database. If you want to confirm this, then run the following code:

That’s it! Your database works fine! Now you have some sample data to test the application with, and you can focus on how to load and display the contact information in your contact book’s main window.

To display your contact data in the application’s main window, you can use QTableView. This class is part of PyQt’s Model-View architecture and provides a robust and efficient way of displaying items from a PyQt model object.

The files and the code you’ll add or modify in this section are stored under the source_code_step_4/ directory. To download them, click the link below:

Once you’ve finished this step, your contact book will look like this:

Contact Book Visualize Data

The table view object in the main window provides the required functionality to allow you to modify and update the contact information quickly.

For example, to update the name of a contact, you can double-click the cell containing the name, update the name, and then press Enter to automatically save the changes to the database. But before you can do this, you need to create a model and connect it to the table view.

Creating a Model to Handle the Contact Data

PyQt provides a rich set of classes for working with SQL databases. For your contact book application, you’ll use QSqlTableModel, which provides an editable data model for a single database table. It’s perfect for the job since your database has a single table, contacts.

Get back to your code editor and create a new module called model.py inside the rpcontacts/ directory. Add the following code to the file and save it:

In this code, you first do some required imports, then you create ContactsModel. In the class initializer, you define an instance attribute called .model to hold the data model.

Next, you add a static method to create and set up the model object. Here’s what the code in ._createModel() does:

At this point, you have your data model ready to use. Now you need to connect the table view widget to the model so you can present your users with the contact information.

Connecting the Model to the View

To display contact data in your contact book’s main window, you need to connect the table view with the data model. To perform this connection, you need to call .setModel() on the table view object and pass the model as an argument:

In this code, you first import ContactsModel from model.py. This class provides the model that manages the data in your contact database.

In the initializer of Window, you create an instance of ContactsModel. Then inside .setupUI(), you call .setModel() on .table to connect the model with the table view. If you run the application after this update, then you’ll get the window you saw at the beginning of step 4.

Displaying and Updating Contacts

PyQt’s Model-View architecture provides a robust and user-friendly way to create GUI applications that manage databases. Models communicate with and access the data in the database. Any change in a model updates the database immediately. Views are responsible for displaying the data to the user and also for providing editable widgets to allow the user to modify the data directly in the view.

If the user modifies the data through the view, then the view internally communicates with and updates the model, which saves the changes to the physical database:

Contact Book Visualize Update Data

In this example, you double-click Joe’s Job field. This gives you access to an editable widget that allows you to modify the value in the cell. Then you update the job description from Senior Web Developer to Web Developer. When you hit Enter, the table view communicates the change to the model, and the model saves the change to the database immediately.

To confirm that the changes were successfully saved into the database, you can close the application and run it again. The table view should reflect your updates.

At this step, your contact book application provides functionality to load, display, and update the information about your contacts. Even though you’re able to modify and update the contact information, you can neither add nor remove contacts from the list.

All the files and the code you’ll add or modify in this section are collected in the source_code_step_5/ directory. To download them, click the link below:

In this section, you’ll provide the required functionality to add new contacts to the database, using a pop-up dialog to enter the new information. The first step is to create the Add Contact dialog.

Creating the Add Contact Dialog

Dialogs are small windows that you can use to communicate with your users. In this section, you’ll code the contact book’s Add Contact dialog to allow your users add new contacts to their current list of contacts.

To code the Add Contact dialog, you’ll subclass QDialog. This class provides a blueprint to build dialogs for your GUI applications.

Now open the views.py module and update the import section like this:

The highlighted lines in the above code import the required classes to build the Add Contact dialog. With these classes in your namespace, add the following class at the end of views.py:

There are a lot of things happening in this code. Here’s a summary:

In .setupUI(), you define the dialog’s GUI:

To code the dialog’s .accept() slot, you need to consider that any user input needs validation to make sure that it’s correct and safe. This is especially true when you’re working with SQL databases because of the risk of an SQL injection attack.

In this example, you’ll add a minimal validation rule just to make sure that the user provides data for each input field in the dialog. However, adding your own, more robust validation rules would be a good exercise.

Without further ado, get back to AddDialog and add the following code for its .accept() slot:

The code within .accept() does the following:

With this code, you’re ready to add a new slot to the contact book’s main window. This slot will launch the dialog, and if the user provides valid input, then the slot will use the model to save the newly added contact to the database.

Launching the Add Contact Dialog

Now that you’ve coded the Add Contact dialog, it’s time to add a new slot to Window so you can launch the dialog by clicking Add and process the user’s input once they click OK.

Go to the definition of Window and add the following code:

Here’s a summary of what’s happening in the above code:

Now that you have a way to launch the Add Contact dialog and to process its data, you need to provide the code for .addContact() in your data model. That’s a topic for the next section.

Processing the Add Dialog’s Data in the Model

In this section, you’ll add a method called .addContact() to your data model, ContactsModel. Open model.py in your code editor, go to the definition of ContactsModel, and add the following code:

Inside .addContact(), the code does the following:

If you run the application with these new additions, then you’ll get the following behavior:

Contact Book Add Contact

Now when you click Add, the Add Contact dialog appears on your screen. You can use the dialog to provide the required information for a new contact and to add the contact to the database by clicking OK.

The final feature you’ll add to the contact book application is the ability to remove contacts from the database using the GUI.

Again, you’ll find all the files and the code added or modified in this section under the source_code_step_6/ directory. You can download them by clicking the link below:

In this section, you’ll first add the capability to delete a single contact at a time. Then you’ll add code to remove all the contacts from the database.

Deleting Selected Contacts

To remove a single contact from the contact database, you need to select the desired contact in the table view on the contact book’s main window. Once you’ve selected the contact, you can click Delete to perform the operation on the database.

Go to the model.py module and add the following code to implement .deleteContact() inside ContactsModel:

This method has three lines of code. The first line removes the selected row. The second line submits the change to the database. Finally, the third line reloads the data into the model.

Next, get back to the views.py module and add the code behind the Delete button in Window:

In the first highlighted line, you connect the .clicked() signal of the Delete button to the .deleteContact() slot. This connection triggers a call to .deleteContact() every time the user clicks the button.

In .deleteContact(), you first get the index of the currently selected row in the table view. The if statement checks if the index is lower than 0, which would mean that there are no contacts in the table view. If so, then the method returns immediately without performing any further actions.

Then the method shows a warning message confirming that the user wants to delete the selected contact. If the user accepts the operation, then .deleteContact(row) gets called. In this case, row represents the index of the currently selected row in the table.

After these additions, you can run the application again to get the following behavior:

Contact Book Delete Contact

Now when you select a contact from the table view and click Delete, you’re presented with a warning message. If you click the message dialog’s OK button, then the application removes the selected contact from the database, updating the table view accordingly.

Clearing the Contact Database

To remove all the contacts from the database, you’ll start by adding a method called .clearContacts() to ContactsModel. Open your model.py module and add the following method at the end of the class:

Here’s what each line of code does:

Once you’ve coded .clearContacts(), you can get back to the views.py file and update Window with the following code:

The first highlighted line in this code connects the .clicked() signal of the Clear All button to the .clearContacts() slot below.

In .clearContacts(), you first create a message dialog, messageBox, to ask the user to confirm the removing operation. If the user confirms the operation by clicking OK, then .clearContacts() gets called on the model to remove all the contacts from the database:

Contact Book Clear All Contacts

That’s it! With this last piece of code, your contact book application is complete. The application provides features that allow your users to display, add, update, and remove contacts from the database.

Conclusion

Building a contact book GUI application with Python, PyQt, and SQLite is an excellent exercise for you to expand your skills with these tools and as a developer in general. Coding projects like this allows you to apply the knowledge and skills you already have and also pushes you to research and learn about new topics every time you encounter a new programming problem.

In this tutorial, you learned how to:

You can download the complete source code for the contact book application and also the code to complete each step in this tutorial by clicking the link below:

Next Steps

At this point, you’ve completed a fully functional contact book project. The application provides minimal functionality, but it’s a good starting point to continue adding features and take your Python and PyQt skills to the next level. Here are some next step ideas that you can implement:

These are just a few ideas for how you can continue adding features to your contact book. Take the challenge and build something amazing on top of this!