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

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

 

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

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. *

The following two tabs change content below.
Arquiteto de Software e Desenvolvedor Backend (quase Fullstack), geralmente trabalho com C#, PowerShell, Python, Golang, bash e Unity (esse é mais por hobby). Estou sempre buscando algo novo para aprender, adicionando novas ferramentas ao meu cinto de utilidades.
Posted in Dev, Python and tagged , , , , , , , , .