SQLite
 sql >> Base de Dados >  >> RDS >> SQLite

Crie um livro de contatos com Python, PyQt e SQLite


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:
  1. Adicionar para adicionar um novo contato à lista
  2. Excluir para remover um contato selecionado da lista
  3. 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ões
  • QTableView para fornecer a visualização em forma de tabela que exibe a lista de contatos
  • QAbstractItemView 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 para QAbstractItemView.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 o if bloco de código mostra uma mensagem de erro e retorna False 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() called tableModel .
  • Line 17 associates the model object with the contacts table in your database.
  • Line 18 sets the .editStrategy property of the model to QSqlTableModel.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 , and email . 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() and reject() 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 the if 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 in data 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 data field 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 to QSqlTableModel.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 the QSqlRelationalTableModel , 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’s QSqlQuery and QSqlQueryModel .

  • 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!