Construir projetos é sem dúvida uma das formas mais acessíveis e eficazes de aprender a programar. Projetos reais exigem que você aplique habilidades de codificação diferentes e variadas. Eles também o incentivam a pesquisar tópicos que surgem à medida que você resolve problemas no processo de desenvolvimento. Neste tutorial, você criará um aplicativo de catálogo de contatos com Python, PyQt e SQLite.
Neste tutorial, você aprenderá a:
- Crie uma interface gráfica do usuário (GUI) para seu aplicativo de catálogo de contatos usando Python e PyQt
- Conecte o aplicativo a um banco de dados SQLite usando o suporte a SQL do PyQt
- Gerencie dados de contato usando a arquitetura Model-View do PyQt
Ao final deste projeto, você terá um aplicativo de agenda de contatos funcional que permitirá armazenar e gerenciar suas informações de contato.
Para obter o código-fonte completo do aplicativo, bem como o código de cada etapa que você seguirá neste tutorial, clique no link abaixo:
Obter o código-fonte: Clique aqui para obter o código-fonte que você usará para criar um livro de contatos com Python, PyQt e SQLite neste tutorial.
Demonstração:um livro de contatos com Python
Os livros de contatos são um tipo de aplicativo útil e amplamente utilizado. Eles estão em todos os lugares. Você provavelmente tem uma agenda de contatos em seu telefone e em seu computador. Com um catálogo de contatos, você pode armazenar e gerenciar informações de contato de seus familiares, amigos, colegas de trabalho e assim por diante.
Neste tutorial, você codificará um aplicativo GUI de catálogo de contatos com Python, SQLite e PyQt. Aqui está uma demonstração de como seu catálogo de contatos ficará e funcionará depois que você seguir as etapas deste tutorial:
Sua lista de contatos fornecerá o conjunto mínimo de recursos necessários para esse tipo de aplicativo. Você poderá exibir, criar, atualizar e remover as informações da sua lista de contatos.
Visão geral do projeto
Para construir seu aplicativo de catálogo de contatos, você precisa organizar o código em módulos e pacotes e dar ao seu projeto uma estrutura coerente. Neste tutorial, você usará os seguintes diretórios e estrutura de arquivos:
rpcontacts_project/
│
├── rpcontacts/
│ ├── __init__.py
│ ├── views.py
│ ├── database.py
│ ├── main.py
│ └── model.py
│
├── requirements.txt
├── README.md
└── rpcontacts.py
Aqui está um breve resumo do conteúdo do diretório do seu projeto:
rpcontacts_project/
é o diretório raiz do projeto. Ele conterá os seguintes arquivos:requirements.txt
fornece a lista de requisitos do projeto.README.md
fornece informações gerais sobre o projeto.rpcontacts.py
fornece o script de ponto de entrada para executar o aplicativo.
rpcontacts/
é um subdiretório que fornece o pacote principal do aplicativo. Ele fornece os seguintes módulos:__init__.py
views.py
database.py
main.py
model.py
Você abordará cada um desses arquivos passo a passo neste tutorial. O nome de cada arquivo dá uma ideia de sua função na aplicação. Por exemplo,
views.py
conterá o código para gerar a GUI de janelas e diálogos, database.py
conterá código para trabalhar com o banco de dados e main.py
hospedará o próprio aplicativo. Por fim, model.py
implementará o modelo para gerenciar os dados no banco de dados do aplicativo. Em geral, o aplicativo terá uma janela principal para exibir, adicionar, remover e atualizar contatos. Ele também terá uma caixa de diálogo para adicionar novos contatos ao banco de dados.
Pré-requisitos
Para tirar o máximo proveito deste projeto, alguns conhecimentos prévios de programação GUI com Python e PyQt ajudariam. Nesse sentido, você precisará saber o básico de como:
- Crie aplicativos GUI com PyQt e Python
- Crie e disponha GUIs com PyQt
- Gerenciar bancos de dados SQL com Python e PyQt
- Trabalhar com bancos de dados SQLite
Para aprimorar esses tópicos, você pode conferir os seguintes recursos:
- Python e PyQt:construindo uma calculadora de desktop GUI
- Python e PyQt:criando menus, barras de ferramentas e barras de status
- Layouts PyQt:crie aplicativos GUI com aparência profissional
- Como lidar com bancos de dados SQL com PyQt:o básico
- Introdução às bibliotecas SQL do Python
- Gerenciamento de dados com Python, SQLite e SQLAlchemy
Não se preocupe se você não for um especialista nessas áreas antes de iniciar este tutorial. Você aprenderá através do processo de sujar as mãos em um projeto real. Se você ficar preso, reserve um tempo e revise os recursos vinculados acima. Então volte para o código.
O aplicativo de catálogo de contatos que você construirá neste tutorial tem uma única dependência externa:PyQt.
Observação: Neste tutorial, você usará o PyQt versão 5.15.2 para criar seu aplicativo de catálogo de contatos. A versão 5.15.2 é necessária para que o projeto funcione no macOS Big Sur.
O PyQt versão 6.0 foi lançado em 4 de janeiro de 2021. Este é o primeiro lançamento da biblioteca que se vincula ao Qt versão 6. No entanto, o projeto neste tutorial não foi testado com o PyQt 6.0.
Se você sentir a necessidade de executar o projeto com esta nova versão do PyQt, experimente. Como dica, você deve
pip install PyQt6
e, em seguida, atualize as importações para usar PyQt6
em vez de PyQt5
. Para seguir as melhores práticas em seu processo de desenvolvimento, você pode começar criando um ambiente virtual e depois instalando o PyQt usando
pip
. Depois de instalar o PyQt, você está pronto para começar a codificar! Etapa 1:criando o aplicativo Skeleton do livro de contatos com PyQt
Nesta primeira etapa, você criará um aplicativo PyQt GUI mínimo, mas funcional, para fornecer a base sobre a qual começará a criar o catálogo de contatos. Você também criará a estrutura de projeto mínima necessária, incluindo o pacote principal do projeto e um script de ponto de entrada para executar o aplicativo.
Todo o código e os arquivos que você adicionar ao projeto do catálogo de contatos nesta seção são coletados no
source_code_step_1/
diretório. Você pode baixá-los clicando no link abaixo:Obter o código-fonte: Clique aqui para obter o código-fonte que você usará para criar um livro de contatos com Python, PyQt e SQLite neste tutorial.
Ao final desta seção, você poderá executar o aplicativo GUI esqueleto para sua lista de contatos pela primeira vez.
Estruturando o Projeto de Contatos
Para começar a codificar o aplicativo, vá em frente e crie um novo diretório chamado
rpcontacts_project/
. Este será o diretório raiz do projeto. Agora crie um novo subdiretório chamado rpcontacts/
dentro de rpcontacts_project/
. Este subdiretório conterá o pacote principal do aplicativo. Por fim, inicie seu editor de código ou IDE no diretório raiz. Para transformar um diretório em um pacote, o Python precisa de um
__init__.py
módulo para inicializar o pacote. Crie este arquivo dentro de rpcontacts/
e adicione o seguinte código a ele:# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
Este arquivo informa ao Python que
rpcontacts
é um pacote. O código no arquivo é executado quando você importa o pacote ou alguns de seus módulos. Você não precisa colocar nenhum código em um
__init__.py
arquivo para inicializar o pacote. Um __init__.py
vazio arquivo fará o trabalho. No entanto, neste caso, você define uma constante de nível de módulo chamada __version__
para manter o número da versão do seu aplicativo. Criando a janela principal do aplicativo
Agora é hora de criar a janela principal do seu catálogo de contatos. Para fazer isso, crie um módulo chamado
views.py
em seus rpcontacts
pacote. Em seguida, adicione o seguinte código ao módulo e salve-o:# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtWidgets import (
QHBoxLayout,
QMainWindow,
QWidget,
)
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
Primeiro, você importa as classes necessárias de
PyQt5.QtWidgets
. Então você cria Window
. Esta classe herda de QMainWindow
e fornece o código para gerar a janela principal do aplicativo. No método inicializador, você define o título da janela como "RP Contacts"
, redimensione a janela para 550
por 250
pixels, defina e defina o widget central usando QWidget
e, finalmente, defina um layout para o widget central usando um layout de caixa horizontal. Codificando e executando o aplicativo
Como você já tem uma janela principal para o livro de contatos, é hora de escrever o código para criar um aplicativo PyQt funcional usando
QApplication
. Para fazer isso, crie um novo módulo chamado main.py
em seus rpcontacts
package e adicione o seguinte código a ele:# -*- coding: utf-8 -*-
# rpcontacts/main.py
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Create the main window
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec())
Neste módulo, você importa
sys
para obter acesso a exit()
, que permite sair do aplicativo de forma limpa quando o usuário fecha a janela principal. Então você importa QApplication
de PyQt5.QtWidgets
e Window
de views
. A etapa final é definir main()
como a função principal do seu aplicativo. Dentro de
main()
, você instancia QApplication
e Window
. Então você chama .show()
na Window
e, finalmente, você executa o loop principal do aplicativo , ou loop de evento , usando .exec()
. Agora vá para o diretório raiz do projeto
rpcontacts_project/
e crie um arquivo chamado rpcontacts.py
. Este arquivo fornece o script de ponto de entrada para executar o aplicativo. Adicione o seguinte código ao arquivo e salve-o:#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# rpcontacts_project/rpcontacts.py
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
Este arquivo importa
main()
do seu main.py
módulo. Então você implementa a instrução condicional tradicional que chama main()
se o usuário executar este módulo como um script Python. Agora inicie o aplicativo executando o comando python rpcontacts.py
em seu ambiente Python. Você terá a seguinte janela na sua tela:É isso! Você criou um aplicativo GUI PyQt mínimo, mas funcional, que pode ser usado como ponto de partida para criar seu catálogo de contatos. Neste ponto, seu projeto deve ter a seguinte estrutura:
./rpcontacts_project/
│
├── rpcontacts/
│ ├── __init__.py
│ ├── views.py
│ └── main.py
│
└── rpcontacts.py
Nesta seção, você criou a estrutura mínima necessária para seu projeto de catálogo de contatos usando módulos e pacotes Python. Você construiu a janela principal do aplicativo e juntou o código clichê para criar um aplicativo PyQt GUI. Você também executou o aplicativo pela primeira vez. Em seguida, você começará a adicionar recursos à sua GUI.
Etapa 2:Construindo a GUI do Catálogo de Contatos com Python
Agora que você construiu o esqueleto do seu aplicativo de catálogo de contatos, você pode começar a codificar a GUI da janela principal. No final desta seção, você terá concluído as etapas necessárias para criar a GUI do seu catálogo de contatos usando Python e PyQt. A GUI ficará assim:
No centro da janela, você tem uma visualização de tabela para exibir sua lista de contatos. No lado direito do formulário, você tem três botões:
- Adicionar para adicionar um novo contato à lista
- Excluir para remover um contato selecionado da lista
- Limpar tudo para remover todos os contatos da lista
Todos os códigos e arquivos que você adicionar ou modificar nesta seção são coletados no
source_code_step_2/
diretório. Você pode baixá-los clicando no link abaixo:Obter o código-fonte: Clique aqui para obter o código-fonte que você usará para criar um livro de contatos com Python, PyQt e SQLite neste tutorial.
Volte para o
views.py
módulo e atualize o código de Window
para gerar a GUI acima: 1# -*- coding: utf-8 -*-
2# rpcontacts/views.py
3
4"""This module provides views to manage the contacts table."""
5
6from PyQt5.QtWidgets import (
7 QAbstractItemView,
8 QHBoxLayout,
9 QMainWindow,
10 QPushButton,
11 QTableView,
12 QVBoxLayout,
13 QWidget,
14)
15
16class Window(QMainWindow):
17 """Main Window."""
18 def __init__(self, parent=None):
19 """Initializer."""
20 # Snip...
21
22 self.setupUI()
23
24 def setupUI(self):
25 """Setup the main window's GUI."""
26 # Create the table view widget
27 self.table = QTableView()
28 self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
29 self.table.resizeColumnsToContents()
30 # Create buttons
31 self.addButton = QPushButton("Add...")
32 self.deleteButton = QPushButton("Delete")
33 self.clearAllButton = QPushButton("Clear All")
34 # Lay out the GUI
35 layout = QVBoxLayout()
36 layout.addWidget(self.addButton)
37 layout.addWidget(self.deleteButton)
38 layout.addStretch()
39 layout.addWidget(self.clearAllButton)
40 self.layout.addWidget(self.table)
41 self.layout.addLayout(layout)
Você primeiro importa algumas classes PyQt extras para usar na GUI. Aqui estão alguns dos mais relevantes:
QPushButton
para criar o Adicionar , Excluir e Limpar tudo botõesQTableView
para fornecer a visualização em forma de tabela que exibe a lista de contatosQAbstractItemView
para fornecer acesso à política de comportamento de seleção de exibição de tabela
Neste código, a primeira adição à
Window
é uma chamada para .setupUI()
no final de __init__()
. Essa chamada gera a GUI da janela principal quando você executa o aplicativo. Aqui está o código dentro de
.setupUI()
faz:- Linha 27 cria um
QTableView
instância para exibir a lista de contatos. - Linha 28 define o
.selectionBehavior
propriedade paraQAbstractItemView.SelectRows
. Isso garante que quando um usuário clicar em qualquer célula da visualização de tabela, a linha completa será selecionada. As linhas na visualização de tabela contêm todas as informações relacionadas a um único contato na lista de contatos. - Linhas 31 a 33 adicione os três botões à GUI:Adicionar , Excluir e Limpar tudo . Esses botões ainda não executam nenhuma ação.
- Linhas 35 a 41 crie e defina um layout coerente para todos os widgets na GUI.
Com essas adições à
Window
, você pode executar o aplicativo novamente. A janela na tela se parecerá com a janela que você viu no início da seção. Observação: Os números de linha no código acima e no restante dos exemplos de código neste tutorial destinam-se a facilitar a explicação. Eles não correspondem à ordem das linhas no módulo ou script final.
Nesta seção, você executou todas as etapas necessárias para criar a GUI da janela principal do seu catálogo de contatos. Agora você está pronto para começar a trabalhar em como seu aplicativo gerenciará e armazenará seus dados de contato.
Etapa 3:Configurando o banco de dados do catálogo de contatos
Neste ponto, você criou um aplicativo PyQt e a GUI da janela principal para criar seu projeto de catálogo de contatos. Nesta seção, você escreverá código para definir como o aplicativo se conecta ao banco de dados de contatos. Para concluir esta etapa, você usará o SQLite para lidar com o banco de dados e o suporte SQL do PyQt para conectar o aplicativo ao banco de dados e trabalhar com seus dados de contato.
O código-fonte e os arquivos que você adicionará ou modificará nesta seção são armazenados em
source_code_step_3/
diretório. Você pode baixá-los clicando no link abaixo:Obter o código-fonte: Clique aqui para obter o código-fonte que você usará para criar um livro de contatos com Python, PyQt e SQLite neste tutorial.
Primeiro, volte para
main.py
no rpcontacts/
diretório e atualize o código para criar a conexão com o banco de dados:# -*- coding: utf-8 -*-
# rpcontacts/main.py
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .database import createConnection
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Connect to the database before creating any window
if not createConnection("contacts.sqlite"):
sys.exit(1)
# Create the main window if the connection succeeded
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec_())
Neste caso, você primeiro importa
createConnection()
de database.py
. Esta função conterá código para criar e abrir uma conexão com o banco de dados de contatos. Você criará database.py
e escreva createConnection()
na próxima seção. Dentro de
main()
, a primeira linha destacada é uma tentativa de criar uma conexão com o banco de dados usando createConnection()
. Se por algum motivo o aplicativo não conseguir criar uma conexão, a chamada para sys.exit(1)
fechará o aplicativo sem criar um elemento gráfico e indicará que ocorreu um erro. Você tem que lidar com a conexão dessa forma porque o aplicativo depende do banco de dados para funcionar corretamente. Se você não tiver uma conexão funcional, seu aplicativo não funcionará.
Essa prática permite que você lide com erros e feche o aplicativo de forma limpa se ocorrer um problema. Você também poderá apresentar ao usuário informações relevantes sobre o erro que o aplicativo encontrou ao tentar se conectar ao banco de dados.
Com essas adições em vigor, é hora de mergulhar no código de
createConnection()
. Conectando ao banco de dados com PyQt e SQLite
Conectar seu aplicativo de catálogo de contatos ao banco de dados associado é uma etapa fundamental no desenvolvimento do aplicativo. Para fazer isso, você codificará uma função chamada
createConnection()
, que criará e abrirá uma conexão com o banco de dados. Se a conexão for bem-sucedida, a função retornará True
. Caso contrário, ele fornecerá informações sobre a causa da falha de conexão. Volte para o
rpcontacts/
diretório e crie um novo módulo chamado database.py
dentro dele. Em seguida, adicione o seguinte código a esse módulo: 1# -*- coding: utf-8 -*-
2# rpcontacts/database.py
3
4"""This module provides a database connection."""
5
6from PyQt5.QtWidgets import QMessageBox
7from PyQt5.QtSql import QSqlDatabase
8
9def createConnection(databaseName):
10 """Create and open a database connection."""
11 connection = QSqlDatabase.addDatabase("QSQLITE")
12 connection.setDatabaseName(databaseName)
13
14 if not connection.open():
15 QMessageBox.warning(
16 None,
17 "RP Contact",
18 f"Database Error: {connection.lastError().text()}",
19 )
20 return False
21
22 return True
Aqui, você primeiro importa algumas classes PyQt necessárias. Então você define
createConnection()
. Esta função recebe um argumento:databaseName
contém o nome ou caminho para o arquivo de banco de dados SQLite físico em seu sistema de arquivos. Aqui está o código dentro de
createConnection()
faz:- Linha 11 cria a conexão com o banco de dados usando o
QSQLITE
motorista. - Linha 12 define o nome do arquivo ou o caminho para o banco de dados.
- Linha 14 tenta abrir a conexão. Se ocorrer um problema durante a chamada para
.open()
, então oif
bloco de código mostra uma mensagem de erro e retornaFalse
para indicar que a tentativa de conexão falhou. - Linha 22 retorna
True
se a tentativa de conexão for bem-sucedida.
Você já codificou
createConnection()
. Agora você pode escrever o código para criar os contacts
tabelas no banco de dados. Criando os contacts
Tabela
Com a função que cria e abre a conexão com o banco de dados, você pode prosseguir para codificar uma função auxiliar para criar os
contacts
tabela. Você usará esta tabela para armazenar as informações sobre seus contatos. Aqui está o código que implementa
_createContactsTable()
:# -*- coding: utf-8 -*-
# rpcontacts/database.py
# Snip...
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def _createContactsTable():
"""Create the contacts table in the database."""
createTableQuery = QSqlQuery()
return createTableQuery.exec(
"""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
job VARCHAR(50),
email VARCHAR(40) NOT NULL
)
"""
)
def createConnection(databaseName):
# Snip...
_createContactsTable()
return True
Aqui, você primeiro adiciona uma nova importação. Você importa
QSqlQuery
para executar e manipular instruções SQL. Dentro de
_createContactsTable()
, você cria uma QSqlQuery
instância. Então você chama .exec()
no objeto de consulta com um SQL baseado em string CREATE TABLE
afirmação como argumento. Esta instrução cria uma nova tabela chamada contacts
em seu banco de dados. A tabela tem as seguintes colunas:Coluna | Conteúdo |
---|---|
id | Um inteiro com a chave primária da tabela |
name | Uma string com o nome de um contato |
job | Uma string com o cargo de um contato |
email | Uma string com o e-mail de um contato |
Os
contacts
tabela em seu banco de dados armazenará informações relevantes sobre seus contatos. A etapa final para concluir a codificação de
database.py
é adicionar uma chamada para _createContactsTable()
de dentro de createConnection()
, logo antes do último return
demonstração. Isso garante que o aplicativo crie os contacts
tabela antes de fazer qualquer operação no banco de dados. Depois de criar os
contacts
tabela, você pode executar alguns testes no banco de dados e também adicionar alguns dados de amostra para testes adicionais. Testando o banco de dados do catálogo de contatos
Até agora, você terminou de escrever o código necessário para lidar com a conexão com o banco de dados do catálogo de contatos. Nesta seção, você realizará alguns testes para garantir que esse código e o próprio banco de dados funcionem corretamente. Você também adicionará alguns dados de amostra ao banco de dados para realizar mais testes posteriormente neste tutorial.
Agora abra um terminal ou linha de comando e vá para o diretório raiz do projeto,
rpcontacts_project/
. Uma vez lá, inicie uma sessão interativa do Python e digite o seguinte código:>>>
>>> from rpcontacts.database import createConnection
>>> # Create a connection
>>> createConnection("contacts.sqlite")
True
>>> # Confirm that contacts table exists
>>> from PyQt5.QtSql import QSqlDatabase
>>> db = QSqlDatabase.database()
>>> db.tables()
['contacts', 'sqlite_sequence']
Aqui, você primeiro importa
createConnection()
do database.py
módulo. Em seguida, você chama essa função para criar e abrir uma conexão com o banco de dados de contatos. O nome do arquivo do banco de dados é contacts.sqlite
. Como esse arquivo não existe no diretório raiz do projeto, o SQLite o cria para você. Você pode verificar isso dando uma olhada no seu diretório atual. Em seguida, você confirma que o banco de dados contém uma tabela chamada
contacts
. Para fazer isso, você chama .database()
em QSqlDatabase
. Este método de classe retorna um ponteiro para a conexão de banco de dados atual. Com esta referência à conexão, você pode chamar .tables()
para obter a lista de tabelas no banco de dados. Observe que a primeira tabela da lista é contacts
, então agora você tem certeza de que tudo está funcionando bem. Agora você pode preparar uma consulta SQL para inserir dados de amostra nos
contacts
tabela:>>>
>>> # Prepare a query to insert sample data
>>> from PyQt5.QtSql import QSqlQuery
>>> insertDataQuery = QSqlQuery()
>>> insertDataQuery.prepare(
... """
... INSERT INTO contacts (
... name,
... job,
... email
... )
... VALUES (?, ?, ?)
... """
... )
True
A consulta acima permite inserir valores específicos no
name
, job
e email
atributos e para salvar esses valores no banco de dados. Abaixo segue um exemplo de como fazer isso:>>>
>>> # Sample data
>>> data = [
... ("Linda", "Technical Lead", "[email protected]"),
... ("Joe", "Senior Web Developer", "[email protected]"),
... ("Lara", "Project Manager", "[email protected]"),
... ("David", "Data Analyst", "[email protected]"),
... ("Jane", "Senior Python Developer", "[email protected]"),
... ]
>>> # Insert sample data
>>> for name, job, email in data:
... insertDataQuery.addBindValue(name)
... insertDataQuery.addBindValue(job)
... insertDataQuery.addBindValue(email)
... insertDataQuery.exec()
...
True
True
True
True
True
Neste pedaço de código, você primeiro define
data
para manter as informações de contato de uma lista de pessoas. Em seguida, você usa um for
loop para inserir os dados chamando .addBindValue()
. Então você chama .exec()
no objeto de consulta para executar efetivamente a consulta SQL no banco de dados. Como todas as chamadas para
.exec()
return True
, você pode concluir que os dados foram inseridos com sucesso no banco de dados. Se você quiser confirmar isso, execute o seguinte código:>>>
>>> query = QSqlQuery()
>>> query.exec("SELECT name, job, email FROM contacts")
True
>>> while query.next():
... print(query.value(0), query.value(1), query.value(2))
...
Linda Technical Lead [email protected]
Joe Senior Web Developer [email protected]
Lara Project Manager [email protected]
David Data Analyst [email protected]
Jane Senior Python Developer [email protected]
É isso! Seu banco de dados funciona bem! Agora você tem alguns dados de amostra para testar o aplicativo e pode se concentrar em como carregar e exibir as informações de contato na janela principal do seu catálogo de contatos.
Etapa 4:Exibindo e atualizando contatos existentes
Para exibir seus dados de contato na janela principal do aplicativo, você pode usar
QTableView
. Essa classe faz parte da arquitetura Model-View do PyQt e fornece uma maneira robusta e eficiente de exibir itens de um objeto de modelo PyQt. Os arquivos e o código que você adicionará ou modificará nesta seção são armazenados em
source_code_step_4/
diretório. Para baixá-los, clique no link abaixo:Obter o código-fonte: Clique aqui para obter o código-fonte que você usará para criar um livro de contatos com Python, PyQt e SQLite neste tutorial.
Depois de concluir esta etapa, seu catálogo de contatos ficará assim:
O objeto de exibição de tabela na janela principal fornece a funcionalidade necessária para permitir que você modifique e atualize as informações de contato rapidamente.
Por exemplo, para atualizar o nome de um contato, você pode clicar duas vezes na célula que contém o nome, atualizar o nome e pressionar Enter para salvar automaticamente as alterações no banco de dados. Mas antes de fazer isso, você precisa criar um modelo e conectá-lo à visualização de tabela.
Criando um modelo para lidar com os dados de contato
O PyQt fornece um rico conjunto de classes para trabalhar com bancos de dados SQL. Para seu aplicativo de catálogo de contatos, você usará
QSqlTableModel
, que fornece um modelo de dados editável para uma única tabela de banco de dados. É perfeito para o trabalho, pois seu banco de dados possui uma única tabela, contacts
. Volte ao seu editor de código e crie um novo módulo chamado
model.py
dentro do rpcontacts/
diretório. Adicione o seguinte código ao arquivo e salve-o: 1# -*- coding: utf-8 -*-
2# rpcontacts/model.py
3
4"""This module provides a model to manage the contacts table."""
5
6from PyQt5.QtCore import Qt
7from PyQt5.QtSql import QSqlTableModel
8
9class ContactsModel:
10 def __init__(self):
11 self.model = self._createModel()
12
13 @staticmethod
14 def _createModel():
15 """Create and set up the model."""
16 tableModel = QSqlTableModel()
17 tableModel.setTable("contacts")
18 tableModel.setEditStrategy(QSqlTableModel.OnFieldChange)
19 tableModel.select()
20 headers = ("ID", "Name", "Job", "Email")
21 for columnIndex, header in enumerate(headers):
22 tableModel.setHeaderData(columnIndex, Qt.Horizontal, header)
23 return tableModel
Neste código, você primeiro faz algumas importações necessárias e, em seguida, cria
ContactsModel
. No inicializador de classe, você define um atributo de instância chamado .model
para manter o modelo de dados. Em seguida, você adiciona um método estático para criar e configurar o objeto de modelo. Here’s what the code in
._createModel()
does:- Line 16 creates an instance of
QSqlTableModel()
calledtableModel
. - Line 17 associates the model object with the
contacts
table in your database. - Line 18 sets the
.editStrategy
property of the model toQSqlTableModel.OnFieldChange
. With this, you ensure that the changes on the model get saved into the database immediately. - Line 19 loads the table into the model by calling
.select()
. - Lines 20 to 22 define and set user-friendly headers for the
contacts
table’s columns. - Line 23 returns the newly created model.
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:# -*- coding: utf-8 -*-
# rpcontacts/views.py
# Snip...
from .model import ContactsModel
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
# Snip...
self.contactsModel = ContactsModel()
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setModel(self.contactsModel.model)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
# Snip...
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:
In this example, you double-click Joe’s Job campo. 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.
Step 5:Creating New Contacts
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:Get the Source Code: Click here to get the source code you’ll use to build a contact book with Python, PyQt, and SQLite in this tutorial.
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:# -*- coding: utf-8 -*-
# rpcontacts/views.py
# Snip...
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QAbstractItemView,
QDialog,
QDialogButtonBox,
QFormLayout,
QHBoxLayout,
QLineEdit,
QMainWindow,
QMessageBox,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
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
: 1# -*- coding: utf-8 -*-
2# rpcontacts/views.py
3
4# Snip...
5class AddDialog(QDialog):
6 """Add Contact dialog."""
7 def __init__(self, parent=None):
8 """Initializer."""
9 super().__init__(parent=parent)
10 self.setWindowTitle("Add Contact")
11 self.layout = QVBoxLayout()
12 self.setLayout(self.layout)
13 self.data = None
14
15 self.setupUI()
16
17 def setupUI(self):
18 """Setup the Add Contact dialog's GUI."""
19 # Create line edits for data fields
20 self.nameField = QLineEdit()
21 self.nameField.setObjectName("Name")
22 self.jobField = QLineEdit()
23 self.jobField.setObjectName("Job")
24 self.emailField = QLineEdit()
25 self.emailField.setObjectName("Email")
26 # Lay out the data fields
27 layout = QFormLayout()
28 layout.addRow("Name:", self.nameField)
29 layout.addRow("Job:", self.jobField)
30 layout.addRow("Email:", self.emailField)
31 self.layout.addLayout(layout)
32 # Add standard buttons to the dialog and connect them
33 self.buttonsBox = QDialogButtonBox(self)
34 self.buttonsBox.setOrientation(Qt.Horizontal)
35 self.buttonsBox.setStandardButtons(
36 QDialogButtonBox.Ok | QDialogButtonBox.Cancel
37 )
38 self.buttonsBox.accepted.connect(self.accept)
39 self.buttonsBox.rejected.connect(self.reject)
40 self.layout.addWidget(self.buttonsBox)
There are a lot of things happening in this code. Here’s a summary:
- Line 5 defines a new class that inherits from
QDialog
. - Lines 7 to 15 define the class initializer. In this case, the most relevant addition is
.data
, which is an instance attribute that you’ll use to hold the data your users provide.
In
.setupUI()
, you define the dialog’s GUI:- Lines 20 to 25 add three
QLineEdit
objects:name
,job
, andemail
. You’ll use these line edits to take the user’s input for the name, job description, and email of the contact to add. They represent the corresponding fields in the database. - Lines 27 to 30 create a
QFormLayout
instance that arranges the line edits in a form. This layout manager also provides user-friendly labels for each line edit or field. - Lines 33 to 37 add a
QDialogButtonBox
object that provides two standard buttons:OK and Cancel . The OK button accepts the user’s input and the Cancel button rejects it. - Lines 38 and 39 connect the dialog’s built-in
.accepted()
and.rejected()
signals with the.accept()
andreject()
slots, respectively. In this case, you’ll rely on the dialog’s built-in.reject()
slot, which closes the dialog without processing the input. Other than that, you just need to code the.accept()
slot.
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: 1# -*- coding: utf-8 -*-
2# rpcontacts/views.py
3
4# Snip...
5class AddDialog(QDialog):
6 def __init__(self, parent=None):
7 # Snip...
8
9 def setupUI(self):
10 # Snip...
11
12 def accept(self):
13 """Accept the data provided through the dialog."""
14 self.data = []
15 for field in (self.nameField, self.jobField, self.emailField):
16 if not field.text():
17 QMessageBox.critical(
18 self,
19 "Error!",
20 f"You must provide a contact's {field.objectName()}",
21 )
22 self.data = None # Reset .data
23 return
24
25 self.data.append(field.text())
26
27 if not self.data:
28 return
29
30 super().accept()
The code within
.accept()
does the following:- Line 14 initializes
.data
to an empty list ([]
). This list will store the user’s input data. - Line 15 defines a
for
loop that iterates over the three line edits, or fields, in the dialog. - Lines 16 to 23 define a conditional statement that checks if the user has provided data for each field in the dialog. If not, then the dialog shows an error message that warns the user about the missing data.
- Line 25 adds the user’s input for each field to
.data
. - Line 30 calls the superclass’s
.accept()
slot to provide the standard behavior that closes the dialog after the user clicks OK .
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: 1# -*- coding: utf-8 -*-
2# rpcontacts/views.py
3
4# Snip...
5class Window(QMainWindow):
6 # Snip...
7
8 def setupUI(self):
9 # Snip...
10 self.addButton = QPushButton("Add...")
11 self.addButton.clicked.connect(self.openAddDialog)
12 # Snip...
13
14 def openAddDialog(self):
15 """Open the Add Contact dialog."""
16 dialog = AddDialog(self)
17 if dialog.exec() == QDialog.Accepted:
18 self.contactsModel.addContact(dialog.data)
19 self.table.resizeColumnsToContents()
Here’s a summary of what’s happening in the above code:
- Line 11 connects the
.clicked()
signal of the Add button to the newly created slot,.openAddDialog()
. This way, a click on the button will automatically call the slot. - Line 14 defines the
.openAddDialog()
slot. - Line 16 creates an instance of
AddDialog
. - Lines 17 to 19 define a conditional statement to check if the dialog was accepted. If so, then line 14 calls
.addContact()
on the data model with the dialog’s.data
attribute as an argument. The final statement in theif
code block resizes the table view to fit the size of its updated content.
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: 1# -*- coding: utf-8 -*-
2# rpcontacts/model.py
3
4# Snip...
5class ContactsModel:
6 # Snip...
7
8 def addContact(self, data):
9 """Add a contact to the database."""
10 rows = self.model.rowCount()
11 self.model.insertRows(rows, 1)
12 for column, field in enumerate(data):
13 self.model.setData(self.model.index(rows, column + 1), field)
14 self.model.submitAll()
15 self.model.select()
Inside
.addContact()
, the code does the following:- Line 10 gets the current number of rows in the data model.
- Line 11 inserts a new row at the end of the data model.
- Lines 12 and 13 run a
for
loop that inserts every item indata
into the corresponding cell in the data model. To do this, line 9 calls.setData()
on the model, with the index of the cell and the current datafield
as arguments. - Line 14 submits the changes to the database by calling
.submitAll()
on the model. - Line 15 reloads the data from the database into the model.
If you run the application with these new additions, then you’ll get the following behavior:
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 .
Step 6:Deleting Existing Contacts
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:Get the Source Code: Click here to get the source code you’ll use to build a contact book with Python, PyQt, and SQLite in this tutorial.
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
:# -*- coding: utf-8 -*-
# rpcontacts/model.py
# Snip...
class ContactsModel:
# Snip...
def deleteContact(self, row):
"""Remove a contact from the database."""
self.model.removeRow(row)
self.model.submitAll()
self.model.select()
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
:# -*- coding: utf-8 -*-
# rpcontacts/views.py
# Snip...
class Window(QMainWindow):
# Snip...
def setupUI(self):
"""Setup the main window's GUI."""
# Snip...
self.deleteButton = QPushButton("Delete")
self.deleteButton.clicked.connect(self.deleteContact)
# Snip...
def deleteContact(self):
"""Delete the selected contact from the database."""
row = self.table.currentIndex().row()
if row < 0:
return
messageBox = QMessageBox.warning(
self,
"Warning!",
"Do you want to remove the selected contact?",
QMessageBox.Ok | QMessageBox.Cancel,
)
if messageBox == QMessageBox.Ok:
self.contactsModel.deleteContact(row)
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:
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: 1# -*- coding: utf-8 -*-
2# rpcontacts/model.py
3
4# Snip...
5class ContactsModel:
6 # Snip...
7
8 def clearContacts(self):
9 """Remove all contacts in the database."""
10 self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
11 self.model.removeRows(0, self.model.rowCount())
12 self.model.submitAll()
13 self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
14 self.model.select()
Here’s what each line of code does:
- Line 10 sets the data model’s
.editStrategy
property toQSqlTableModel.OnManualSubmit
. This allows you to cache all the changes until you call.submitAll()
later on. You need to do this because you’re changing several rows at the same time. - Line 11 removes all the rows from the model.
- Line 12 saves changes to the database.
- Line 13 resets the model’s
.editStrategy
property to its original value,QSqlTableModel.OnFieldChange
. If you don’t reset this property to its original value, then you won’t be able to update the contacts directly in the table view. - Line 14 reloads the data into the model.
Once you’ve coded
.clearContacts()
, you can get back to the views.py
file and update Window
with the following code:# -*- coding: utf-8 -*-
# rpcontacts/views.py
# Snip...
class Window(QMainWindow):
# Snip...
def setupUI(self):
"""Setup the main window's GUI."""
# Snip...
self.clearAllButton = QPushButton("Clear All")
self.clearAllButton.clicked.connect(self.clearContacts)
# Snip...
def clearContacts(self):
"""Remove all contacts from the database."""
messageBox = QMessageBox.warning(
self,
"Warning!",
"Do you want to remove all your contacts?",
QMessageBox.Ok | QMessageBox.Cancel,
)
if messageBox == QMessageBox.Ok:
self.contactsModel.clearContacts()
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: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:
- Build the GUI for a contact book application using PyQt
- Use PyQt’s SQL support to connect the application to an SQLite database
- Use PyQt’s Model-View architecture to work with the application’s database
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:
Get the Source Code: Click here to get the source code you’ll use to build a contact book with Python, PyQt, and SQLite in this tutorial.
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:
-
Add new data fields: Adding new data fields to store more information about your contacts would be great. For example, you can add the contact’s photo, phone number, web page, Twitter handle, and so on. To do this, you might need to create new tables and set up relations between them. PyQt provides theQSqlRelationalTableModel
, which defines an editable data model for a single table and provides foreign key support.
-
Provide search capability: Giving your users a way to search for a contact in the database is arguably a must-have feature in this kind of application. To implement it, you can use PyQt’sQSqlQuery
andQSqlQueryModel
.
-
Add back-up capability: Providing a way of backing up contact information is another interesting feature. Users might face problems with their computers and lose their data. You can provide options to upload the data to a cloud service or to back it up to an external disk.
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!