Tutorial: Aplicação em Python + SQLite (Parte 01)

Tutorial: Aplicação em Python + SQLite (Parte 01)

Overview

Bem-vindos a um passeio guiado pelo mundo da programação com Python! Neste tutorial, vamos mergulhar na criação de uma aplicação CRUD de clientes usando Python e SQLite. Com passos claros e um exemplo prático, transitaremos do frontend básico à compilação final para Windows (.exe) e Mac (.app). Prepare seu ambiente Python, e vamos juntos dar vida a essa aplicação!

Neste tutorial, vou demonstrar como construir uma aplicação simples em Python e persistir os dados utilizando SQLite. A aplicação que será criada é um CRUD de clientes bem simples, mas que servirá para a demonstração.

Ao final do tutorial, demonstrarei como compilar a aplicação criada para Windows (.exe) e para Mac (.app).

A aplicação irá se parecer com a imagem abaixo:

Cadastro de Clientes em Python

Cadastro de Clientes em Python

Cadastro de Clientes em Python

Não vou entrar muito na teoria sobre qual o melhor lugar para se começar: Frontend ou Backend. Acredito que, para este tutorial, o melhor será começar pelo Frontend.

Pré-requisito:

  • Ter Python instalado na maquina;
  • Possuir conhecimentos básicos em Python;

Índice:

Parte 01_ Frontend;

Primeiro, crie um arquivo chamado GUI.py e realize a importação da biblioteca tkinter (utilizada para criar interface gráfica).

from tkinter import *

Agora defina uma classe chamada Gui:

class Gui():
    """Classe que define a interface gráfica da aplicação
    """

Como você sabe, o Python utiliza endentação para controlar o conteúdo de classes e funções. Com isso em mente todo o código a seguir fará parte dele. Vou manter a endentação, mas não sei se eles serão exibidos corretamente.

A primeira coisa que vamos definir é o objeto da janela e um título para ela:

    #Criando a janela...
    window          = Tk()
    window.wm_title("Cadastro de Clientes")

No Python, os objetos de input (entry) precisam de uma variável para fazer um bind com o valor, ou seja, o campo de input vai sempre jogar o valor que for inserido nele em uma variável.

Como você pode perceber pela imagem de exemplo, vamos ter quatro campos para o cadastro de clientes: Nome, Sobrenome, Email e CPF. Então vamos definir as variáveis que vamos precisar para esta aplicação:

    #Criando variáveis que armazenarão o texto inserido pelo usuário...
    txtNome         = StringVar()
    txtSobrenome    = StringVar()
    txtEmail        = StringVar()
    txtCPF          = StringVar()

Agora vamos criar as Labels que ficarão ao lado dos campos:

    #Criando os objetos que estarão na janela...
    lblnome        = Label(window, text="Nome")
    lblsobrenome   = Label(window, text="Sobrenome")
    lblemail       = Label(window, text="Email")
    lblcpf         = Label(window, text="CPF")

No fonte acima, instanciamos 4 labels. O primeiro argumento (window) define a qual janela este label pertence. Todos os objetos criados precisam desta variável para definir e o segundo (text=“Nome”) define o texto que será exibido nele.

Agora que temos os Labels, vamos definir os campos de input (entry):

    entNome        = Entry(window, textvariable=txtNome)
    entSobrenome   = Entry(window, textvariable=txtSobrenome)
    entEmail       = Entry(window, textvariable=txtEmail)
    entCPF         = Entry(window, textvariable=txtCPF)

No fonte acima, o primeiro argumento funciona da mesma forma que nos labels. Isso vai ser comum para todos os componentes (widget), então não vou mais comentar sobre ele. O segundo argumento (textvariable=txtNome) indica qual a variável que fará o bind com o valor do campo, ou seja, qual é a variável que receberá sempre o valor inserido no campo.

O próximo passo é criar o ListBox que listará todos os clientes cadastrados.

    listClientes   = Listbox(window)

E também um ScrollBar para funcionar em conjunto com o ListBox.

    scrollClientes = Scrollbar(window)

Neste primeiro momento, estamos apenas criando o ScrollBar, mas precisaremos realizar outras ações neste objeto.

Agora vamos criar os botões que estarão disponíveis na interface:

    btnViewAll     = Button(window, text="Ver todos")
    btnBuscar      = Button(window, text="Buscar")
    btnInserir     = Button(window, text="Inserir")
    btnUpdate      = Button(window, text="Atualizar Selecionados")
    btnDel         = Button(window, text="Deletar Selecionados")
    btnClose       = Button(window, text="Fechar")

Assim como nos Labels, o argumento text (text=“Ver todos”) serve para definir qual o valor exibido no botão.

Todos os objetos que precisaremos estão criados, precisamos posicioná-los na janela. Para isso, vamos utilizar o posicionamento por grid. Esta operação é parecida com a forma que posicionamos elementos em uma grid no XAML, com a diferença que não precisamos definir quantidade de linhas ou colunas.

    #Associando os objetos a grid da janela...
    lblnome.grid(row=0,column=0)
    lblsobrenome.grid(row=1,column=0)
    lblemail.grid(row=2,column=0)
    lblcpf.grid(row=3, column=0)
    entNome.grid(row=0, column=1, padx=50, pady=50)
    entSobrenome.grid(row=1, column=1)
    entEmail.grid(row=2, column=1)
    entCPF.grid(row=3, column=1)
    listClientes.grid(row=0, column=2, rowspan=10)
    scrollClientes.grid(row=0, column=6, rowspan=10)
    btnViewAll.grid(row=4, column=0, columnspan=2)
    btnBuscar.grid(row=5, column=0, columnspan=2)
    btnInserir.grid(row=6, column=0, columnspan=2)
    btnUpdate.grid(row=7, column=0, columnspan=2)
    btnDel.grid(row=8, column=0, columnspan=2)
    btnClose.grid(row=9, column=0, columnspan=2)

No código acima, definidos em qual linha (row) cada objeto está posicionado e em qual coluna (column). Quando necessário, utilizamos o columnspan para fazer com que um objeto ocupe mais de uma coluna ou o rowspan para fazer com que o objeto ocupe mais de uma linha.

O próximo passo é ligar o ListBox ao ScrollBar:

    #Associando a Scrollbar com a Listbox...
    listClientes.configure(yscrollcommand=scrollClientes.set)

No fonte acima, o argumento yscrollcommand é responsável por definir qual é o objeto do tipo scrollbar que será o responsável por acompanhar o rolamento vertical. Note que é necessário utilizar a propriedade .set do objeto scrollbar para que ele seja definido na ListBox.

Ainda precisamos de mais uma configuração mas, desta vez, na scrollbar.

    scrollClientes.configure(command=listClientes.yview)

O argumento command (command=listClientes.yview) define qual o função será chamada quando a scrollbar for ativada. Neste caso, estamos falando que a posição vertical será alterada sempre que o ScrollBar for manipulado.

Os campos agora estão inseridos na janela e posicionados, mas temos um problema….

Cadastro de Clientes sem Swag

Cadastro de Clientes sem Swag

Cadastro de Clientes sem Swag

Esta janela está absurdamente feia. Precisamos acrescentar um pouco de swag nela…

A primeira coisa que precisamos fazer é criar variáveis para definir o padding dos elementos, a largura dos elementos de input (entry).

    x_pad = 5
    y_pad = 3
    width_entry = 30

Precisamos aplicar o padding para quase todos os elementos, definir a largura dos botões e mais alguns pontos estéticos. Isso poderia ser feito manualmente, repetindo o código para todos os elementos. Todavia, não vamos fazer assim.

Faremos a mudança de estilo da seguinte forma: Uma iteração por todos os elementos da janela, realizando as alterações conforme passamos pelos elementos.

    #Adicionando um pouco de SWAG a interface...
    for child in window.winfo_children():
        widget_class = child.__class__.__name__
        if widget_class == "Button":
            child.grid_configure(sticky='WE', padx=x_pad, pady=y_pad)
        elif widget_class == "Listbox":
            child.grid_configure(padx=0, pady=0, sticky='NS')
        elif widget_class == "Scrollbar":
            child.grid_configure(padx=0, pady=0, sticky='NS')
        else:
            child.grid_configure(padx=x_pad, pady=y_pad, sticky='N')

No código acima, fazemos as seguintes ações:

  • Recuperamos todos os elementos contidos na janela, através do método .winfo_children();
  • Depois fazemos uma iteração (for) pelos elementos;
  • Na variável widget_class é armazenado o nome da classe (child.__class__.__name__) do elemento da iteração;
  • Dependendo do nome da classe (armazenado na variável widget_class) serão realizadas alterações em algumas propriedades:
    • padx e pady: Padding para o eixo X e Y do elemento. É referente ao espaço entre a borda deste elemento e a borda dos outros elementos da janela;
    • sticky: Indica em qual ponto da janela (norte – N, sul – S, leste – E ou oeste W) o objeto estará ancorado. Se você combinar o ponto leste e oeste (EW), o elemento ocupará todo o espaço horizontal da coluna em que está localizado. O mesmo ocorre se colocarmos NS (norte-sul), o elemento ocupará todo o espaço vertical.

Para o ListBox e o ScrollBar, vamos definir padding zero para que eles fiquem colados, parecendo que são apenas um elemento.

Para finalizar esta seção, vamos criar uma função para ativar a janela. No tkinter, devemos chamar a função para que a janela comece a funcionar. Ele irá receber os eventos da janela e redirecioná-los para os widgets (elementos/objetos) dela.

Uma vez que a função mainloop() é chamada, não é mais possível realizar alterações visuais (incluindo criar/destruir objetos).

Pensando no futuro, caso “amanhã” seja necessário realizar mais alguma alteração antes de chamar a mainloop(), vamos encapsular esta chamada em uma função…

    def run(self):
        Gui.window.mainloop()

Esta função, quando chamada, não requer argumentos e ela faz referencia para a janela que estamos utilizando. O Python não usa a keyword this, ou seja, para referenciarmos um elemento contido na classe, utilizamos o proprio nome da classe. Por isso a referência foi feita desta forma: Gui.window.mainloop().

Que tal testar o que acabamos de fazer?

Crie outro arquivo, chamado application.py, nele vamos importar o arquivo Gui.py… A partir de agora, o código exemplificado faz parte de outro arquivo (application.py).

from GUI import *

No comando acima, estamos importando tudo do arquivo GUI.py, mas este processo é case sensitive, ou seja, se você salvou seu arquivo como gui.py ou Gui.py, deve adaptar o comando acima. (exemplo: Gui.py -> from Gui import * // gui.py -> from gui import *)

Agora vamos declarar e instanciar uma variável da classe Gui…

app = None
app = Gui()

Para finalizar, vamos “rodar” a janela…

app.run()

Ao executar este script, você conseguirá visualizar a janela, com todas as configurações que fizemos. O próximo passo é codificar os comportamentos dela, mas isso é para a próxima seção.

*Update 21/11/2018: Criei um repositório no Github com este fonte completo. *

Postagens nesta série