Desenvolvendo aplicações GUI simples em Python & Glade (PyGTK) com banco de dados SQLite

O objetivo deste artigo é mostrar de forma objetiva como realizar procedimentos básicos de programação, então vamos abordar a criação da parte gráfica do programa, vamos falar de como usar controles e eventos simples na nossa aplicação, como botões, caixas de textos, janelas, diálogos, listas, o básico necessário da linguagem Python e como fazer as principais operações com banco de dados.

[ Hits: 163.193 ]

Por: Perfil removido em 28/07/2010


Continuando a programar



Como vimos nosso arquivo principal usa outros arquivos do projeto, é claro. Estes arquivos seguem o mesmo modelo, com exceção do funcoes.py que não define nenhuma classe, apenas, como o nome já diz, tem funções e variáveis que usaremos globalmente no aplicativo. A principal função deste arquivo é a conexão com o banco, além das funções auxiliares.

Arquivo: funcoes.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

#Importação das bibliotecas
import pygtk
pygtk.require('2.0')
import gtk
from gtk import glade
from sqlite3 import *


#Conexão com o banco
cnx = connect('/opt/gfpopen/gfp.sqlite')
query = cnx.cursor()

#Variáveis globais
CaminhoGlade = '/opt/gfpopen/glade/'

#Variáveis do usuário logado
usuariologadoid = 0
usuariologadouser = ''
usuariologadonome = ''


#Mostra caixa de mensagem
def Mensagem(pai, S):
  dialog = gtk.MessageDialog(pai,
                             gtk.DIALOG_MODAL |
                             gtk.DIALOG_DESTROY_WITH_PARENT,
                             gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
                             S)
  dialog.set_title("Informação")
  dialog.run()
  dialog.destroy()


#Mostra mensagem de confirmação
def MsgConfirmacao(pai, S):
  dialog = gtk.MessageDialog(pai,
                             gtk.DIALOG_MODAL |
                             gtk.DIALOG_DESTROY_WITH_PARENT,
                             gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
                             S)
  dialog.set_title("Confirmação")
  R = dialog.run()
  dialog.destroy()

  if R == gtk.RESPONSE_YES:
    return True
  else:
    return False


#Coloca edit em maiúsculo
def StrToMais(edt):
  new_text = TiraAspa(edt.get_text().upper())
  edt.set_text(new_text)


#Limpa espaços em branco
def StrToTrim(edt):
  new_text = TiraAspa(edt.get_text().strip())
  edt.set_text(new_text)


def TiraAspa(s):
return s.replace("'", "") Este arquivo é simples e creio que com a ajuda dos comentários ele se auto-explica. Só vou detalhar sobre a conexão com o banco, como ela é simples, começa pela importação da biblioteca pro SQLite:

from sqlite3 import *

A conexão é simples. O meio de campo é feito através de um cursor, que funciona semelhante a uma mysql_query do PHP, por exemplo. Eu inclusive usei o nome da variável como query pra ficar mais cômodo.

#Conexão com o banco
cnx = connect('/opt/gfpopen/gfp.sqlite')
query = cnx.cursor()

Como ler os resultados retornados e como executar comandos SQL no banco, embora já tenha aparecido no arquivo principal, vai ser mostrado logo em seguida.

Cadastro de usuários

Quando executamos o nosso programa logo na tela de login temos um botão pra chamar a tela para cadastrar um usuário, tela esta que é chamada la no gfp.py na função AbreCadastroUsu através do comando:

usuarioscadastro.UsuariosCadastro(widget, True, 0, winLogin, self)

Estamos chamando diretamente a classe e executando seu método construtor. Note que os parâmetros que passamos são os mesmos que lá na classe UsuariosCadastro são declarados em:

def __init__(self, widget, novo, id, pai, paiclasse):

Agora vamos direto ao arquivo completo.

Arquivo: usuarioscadastro.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

#Importação das bibliotecas
import pygtk
pygtk.require('2.0')
import gtk
from gtk import glade
import gobject
from sqlite3 import *

#Importa arquivos do projeto
import funcoes as f
import usuarioslista

class UsuariosCadastro(gtk.Window):
#Construtor
  def __init__(self, widget, novo, id, pai, paiclasse):
   #Inicializar
    super(UsuariosCadastro, self).__init__()
    self.AbreCadUser(widget, novo, id, pai, paiclasse)

#Cria a tela
  def AbreCadUser(self, widget, novo, id, pai, paiclasse):
   #Associa arquivo glade
    GLADE_FILE = f.CaminhoGlade + 'usuarioslista.glade'
    gui = glade.XML(GLADE_FILE, 'winUserCadastro')
  
   #Associa controles
    self.win = gui.get_widget('winUserCadastro')
    self.edtSenha =  gui.get_widget('edtSenha')
    self.edtConfirmar =  gui.get_widget('edtConfirmar')
    self.edtNome =  gui.get_widget('edtNome')
    self.lblDicaUser = gui.get_widget('lblSomenteLetras')
    self.edtUsuario =  gui.get_widget('edtUsuario')
    self.btnSair = gui.get_widget('btnCancelar')
    self.imgSenha = gui.get_widget('imgAlertaSenha')
    self.btnSalvar = gui.get_widget('btnOk')
  
    self.myNovo = novo
    self.MyId = id
    self.MyPai = pai
    self.MyPaiClasse = paiclasse
  
   #Formata controles
    self.lblDicaUser.set_markup("(somente letras, sem espaços ou acentos)")
  
   #Associa evento dos controles
    self.edtUsuario.connect("changed", lambda *a: f.StrToMais(self.edtUsuario))
    self.edtUsuario.connect("focus_out_event", lambda *a: f.StrToTrim(self.edtUsuario))
    self.edtNome.connect("focus_out_event", lambda *a: f.StrToTrim(self.edtNome))
    self.edtSenha.connect("focus_out_event", lambda *a: self.AlertaSenha(widget))
    self.edtSenha.connect("changed", lambda *a: self.edtSenha.set_text(f.TiraAspa(self.edtSenha.get_text())))
    self.edtConfirmar.connect("focus_out_event", lambda *a: self.AlertaSenha(widget))
    self.edtConfirmar.connect("changed", lambda *a: f.TiraAspa(self.edtConfirmar.get_text()))
    self.btnSair.connect('clicked', lambda *a: self.Fechar(widget))
    self.btnSalvar.connect('clicked', lambda *a: self.VerificaDigitacao(widget))
  
   #Configura modo de edição do form
    if self.myNovo == False:
      self.edtUsuario.set_editable(False)
      self.edtUsuario.set_can_focus(False)
      self.lblDicaUser.set_markup("(não pode ser alterado)")

     #Executa query
      f.query.execute('SELECT user, nome, senha FROM usuarios WHERE id =' + str(self.MyId))

     #Preenche os controles
      for row in f.query:
        self.edtUsuario.set_text(row[0])
        self.edtNome.set_text(row[1])
        self.edtSenha.set_text(row[2])
        self.edtConfirmar.set_text(row[2])

      self.win.set_focus(self.edtSenha)

   #Abre janela
    self.win.show_all()
    self.imgSenha.set_visible(False)

    return True


#Fechar janela
  def Fechar(self, widget):
    self.win.destroy() == True


#Alerta de senha
  def AlertaSenha(self, widget):
    if self.edtConfirmar.get_text() != '':
      if self.edtSenha.get_text() != self.edtConfirmar.get_text():
        self.imgSenha.set_visible(True)
      else:
        self.imgSenha.set_visible(False)


#Verificação das digitações
  def VerificaDigitacao(self, widget):
   #Verifica usuário
    if self.edtUsuario.get_text() == '':
      f.Mensagem(self.win, 'Informe o usuário.')
      self.win.set_focus(self.edtUsuario)
      return
  
   #Verifica a senha
    if self.edtSenha.get_text() == '' or self.edtSenha.get_text() != self.edtConfirmar.get_text():
      f.Mensagem(self.win, 'Verifique a senha.')
      self.win.set_focus(self.edtSenha)
      return
  
   #Verifica nome
    if self.edtNome.get_text() == '':
      f.Mensagem(self.win, 'Informe o nome completo.')
      self.win.set_focus(self.edtNome)
      return
  
   #Verifica se o usuário já existe
    if self.myNovo == True:
      f.query.execute("SELECT id, user, nome FROM usuarios WHERE user = '" + self.edtUsuario.get_text() + "'")

    userExiste = False
    for row in f.query:
      userExiste = True

    if userExiste:
      f.Mensagem(self.win, 'Já existe um usuário ' + self.edtUsuario.get_text() + ' cadastrado.')
      self.win.set_focus(self.edtUsuario)
    else:
      if self.myNovo == True:
        sAcao = 'cadastrado'
        f.query.execute("INSERT INTO usuarios (user, nome, senha)\
                        VALUES('" + self.edtUsuario.get_text() + "',\
                        '" + self.edtNome.get_text() + "',\
                        '" + self.edtSenha.get_text() + "')")
      else:
        sAcao = 'alterado'
        f.query.execute("UPDATE usuarios\
                        SET nome = '" + self.edtNome.get_text() + "',\
                        senha = '" + self.edtSenha.get_text() + "'\
                        WHERE id = " + str(self.MyId))
      
      f.cnx.commit()

      f.Mensagem(self.MyPai, 'Usuário ' + sAcao + ' com sucesso.')
      self.MyPaiClasse.ListaUsers()
      self.win.destroy() == True

Demonstrado por inteiro, através dos comentários também vemos o que cada bloco faz. Mas aqui quero destacar as instruções com o banco de dados. Logo após a seção inicial do arquivo onde montamos e chamamos a tela, por ser um cadastro verificamos se o parâmetro chamamos para inserção ou edição, e se for edição selecionamos o registro a editar e exibimos os campos nos controle. Isso nós fazemos na instrução abaixo. Note que lemos o resultados da query num for e "pegamos" o valor dos campos de acordo com o índice referente à posição que passamos os campos no SQL.

Esse método usando loop é o mesmo que usamos na listagem de todos os usuários (conforme veremos adiante), e mesmo sendo apenas um registro (como fica óbvio) não tem problema usar o for pois ele também só vai executar as instruções pra atribuir os valores aos campos uma vez, pois só um registro é retornado no nosso SQL.

    #Configura modo de edição do form
    if self.myNovo == False:
      self.edtUsuario.set_editable(False)
      self.edtUsuario.set_can_focus(False)
      self.lblDicaUser.set_markup("(não pode ser alterado)")

     #Executa query
      f.query.execute('SELECT user, nome, senha FROM usuarios WHERE id =' + str(self.MyId))

     #Preenche os controles
      for row in f.query:
        self.edtUsuario.set_text(row[0])
        self.edtNome.set_text(row[1])
        self.edtSenha.set_text(row[2])
        self.edtConfirmar.set_text(row[2])

E na instrução mais abaixo vamos como dar INSERT e UPDATE (o mesmo serve pro DELETE, conforme vermos no arquivo seguinte). A diferença é que estas instruções precisam de um commit no final, pois alteram registros no banco.

      if self.myNovo == True:
        sAcao = 'cadastrado'
        f.query.execute("INSERT INTO usuarios (user, nome, senha)\
                        VALUES('" + self.edtUsuario.get_text() + "',\
                        '" + self.edtNome.get_text() + "',\
                        '" + self.edtSenha.get_text() + "')")
      else:
        sAcao = 'alterado'
        f.query.execute("UPDATE usuarios\
                        SET nome = '" + self.edtNome.get_text() + "',\
                        senha = '" + self.edtSenha.get_text() + "'\
                        WHERE id = " + str(self.MyId))

      f.cnx.commit()

Página anterior     Próxima página

Páginas do artigo
   1. Introdução
   2. Por que Python?
   3. Preparando o ambiente
   4. Dando forma à aplicação
   5. Agora vamos programar
   6. Continuando a programar
   7. Finalizando com a listagem
Outros artigos deste autor

XL - Ferramenta de gerenciamento Xen - Parte I

Instalação e configuração do Ubuntu Gusty Gibbon na linha de notebooks HP/Compaq

Alterando a imagem do xsplash nos Ubuntu-like

Como se comunicar com outros usuários da rede

Configurando uma pasta compartilhada para os usuários do seu Linux

Leitura recomendada

PEP 8 - Guia de estilo para código Python

Trabalhando com permutações em ordem lexicográfica crescente

Construindo um portscanner TCP com Python

Como criar um bot para curtir e comentar perfis do Instagram

RapidScan - Multi-Tool WEB Vulnerability Scanner

  
Comentários
[1] Comentário enviado por fonini em 29/07/2010 - 08:27h

Parabéns pelo excelente artigo!

[2] Comentário enviado por balani em 29/07/2010 - 09:40h

Excelente artigo, estou iniciando em Python, seu artigo me ajudará muito, nesse aprendizado.

Parabens!

Abraços


Adriano R. Balani

[3] Comentário enviado por gtuxed em 29/07/2010 - 12:34h

Bom artigo, parabéns.

Estes dias fiz meu primeiro aplicativo com PyGTK e apesar de também ser iniciante em python, não achei muito difícil exceto por ainda achar as estruturas liststore/treestore um pouco "overkill" rsrs.

Falando mais sobre estas estruturas, acho que apesar da flexibilidade que elas oferecem, deveria haver uma interface mais simples para coisas mais pragmáticas.

Mas isso é mais um problema com GTK do que com PyGTK (que é apenas um binding).

hehe, olha só como é complexo criar uma simples lista e apresentar...

##

# Criamos um modelo ListStore(<tipo>,<tipo>,...)
liststore = gtk.ListStore(str)

# Baseado no modelo criamos uma treeview
treeview = gtk.TreeView(liststore)

# Precisamos criar um "renderizador" (será que estou usando blender, engine 3d, etc.? rsrs)
textrenderer = gtk.CellRendererText()

# Adicionamos a coluna ao treeview
column = gtk.TreeViewColumn('Nome da coluna', textrenderer, text=0)
treeview.append_column(column)

# Adicionamos valores à lista
for row in ['a','b','c']:
itt = liststore.append([row])

##

Poderia ser assim:

##

# Criamos a lista
list = gtk.List(str)

# Adicionamos valores à ela
list.append('teste')

##

Da pra fazer isso facilmente, mas acho que isso já deveria existir rsrs

[4] Comentário enviado por gomes-fdr em 29/07/2010 - 16:26h

Muito bom o seu artigo, parabens.

Para mim servirá como material de apoio para o inicio de aplicativos baseados em Python(desktop).

Saudações.

[5] Comentário enviado por albfneto em 01/08/2010 - 18:48h

émuitobom esse artigo.! 10

[6] Comentário enviado por Lisandro em 21/12/2010 - 09:05h

Parabéns pelo artigo. Muito Bom.

[7] Comentário enviado por quemsoueu em 06/07/2012 - 08:18h

Cara seu artigo abriu meus olhos eu tava quase desistindo de desenvolver o software.
A dúvida é com o tkinter eu consigo rodar ele no windows como java, entretanto como faço para rodar ele no windows ele foi escrito com o pygtk e glade, e preciso rodar numa estação Win, acaso você já fez isso?

[8] Comentário enviado por leo523 em 10/06/2013 - 10:17h

Ótimo post, mas atualmente o modo é um pouco diferente, ao inves de usar a biblioteca glade.XML para chamar o xml, usa-se o gtk.builder.

Respondendo o comentário acima, para distribuir no Windows vc deve instalar o pygtk completo que esta neste link :
http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/

[9] Comentário enviado por wladimir58 em 15/06/2016 - 16:27h

Nunca consegui usar as interfaces do Glade nos meus programas em Python




Contribuir com comentário