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: 169.439 ]

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

Montando o cache de DNS

Top 10 melhores jogos FPS para Linux

Montando partições em memória

Outros recursos no OpenOffice: colunas, fundo e bordas

Linux Terminal Server - LTS

Leitura recomendada

Criando um leitor de RSS com Python

Qu1cksc0pe - All-in-One Static Malware Analysis Tool

Splash Screen para Inkscape

Como criar um bot para curtir e comentar perfis do Instagram

PEP 8 - Guia de estilo para código Python

  
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




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts