PyGTK 01: Conversor de temperatura

Já faz algum tempo que tenho me divertindo PyGTK e pretendo compartilhar um pouco desta experiência neste texto. O programa que vou apresentar aqui é um conversor de Temperaturas: Célsius para Fahrenheit e vise-versa. Pois é nada de “Hello World“, mas isto é fácil de se entender, afinal sou Físico.

Evidente que também fiz o tradicional “Hello World”, mas sei que esta cheio deles pela internet, e por isto não acrescentaria muito apresentar mais um. Um outro motivo é que o conversor de temperaturas permite explorar bem mais que um simples Hélio World.

O objetivo aqui não é ensinar o uso do Python e sim apresentar algumas funcionalidades e a praticidade de se construir programas com o PyGTK, um bind do GTK para a linguagem Python. Para aprender o Python sugiro a leitura dos livros:

  • Aprenda Programar, Luciano Ramalho – apresenta os conceitos básicos de programação para iniciantes em linguagens de programação;
  • Python na Prática, Christian Reis – é um dos responsáveis pelo Stoq, uma suíte de aplicativos para gestão comercial desenvolvida pela Async Open Source. Ótimo texto introdutório para a linguagem;
  • Tutorial Python, GuidoVanRossum – criador do Python, Guido dá uma visão geral da linguagem.

Os dois primeiros textos estão em português, o último possui um aversão em português. A ordem acima pode facilitar o aprendizado aos iniciantes em programação.

O problema

O Conversor de Temperaturas consiste em uma caixa de dialogo bem simples com dois campos de texto para por as temperaturas em Célsius e Fahrenheit. A transformação será feita pelas equações abaixo:

  • TF = 9/5*TC + 32
  • TC = 5/9(TF – 32)
conversor de temperatura Py-GTK
Janela do conversor de temperatura.

O Conversor irá calcular a nova temperatura, pelas equações acima, sempre que o foco de uma das caixas de texto mudar para qualquer outro objeto na janela. Isto pode acontecer ao se pressionar a tecla TAB ou clicar ao em algum outro objeto com o mouse. A tecla ENTER irá mudar o foco para o outra outra caixa de texto não selecionado, o que irá acionar o cálculo de uma nova conversão automaticamente.

Apenas uma pequena observação para os iniciantes em programação em ambientes gráficos: todos os objetos, com um botão, label, checkbox, janela, … são chamados de widgets.

Nas linhas seguintes irei explorar todas as linhas do código, para deixá-lo bem claro. O código completo do programa se encontra ao final do texto.

Iniciando o programa

A primeira linha do código estabelece o Python como sendo o interpretador para o script. Em seguida coloco algumas linhas de comentário. Em Python, comentários são feitos tal como em Bash e em outras linguagens, adicionando-se uma tralha (#) no inicio do comentário.

#!/usr/bin/env python
# Conversor de Temperatura
# por Rudson Alves

Em seguida, um import adiciona o bind do GTK ao Python. As duas primeiras linhas poderiam ser omitidas. Elas apenas lhe garante que irá importar a versão ‘2.0’ do PyGTK. O último import é quem de fato importa as funcionalidades do GTK2 para fica acessível ao seu programa.

import pygtk
pygtk.require('2.0')
import gtk

O próximo passo é construir as funções para o cálculo das conversões de temperatura desejadas.

def calc_Fahrenheit(temp):
    return round((9./5.)*temp + 32.)

def calc_Celsius(temp):
    return round((5./9.)*(temp - 32.))

As funções expressão as equações apresentadas na seção anterior, com uma pequena limitação: este conversor trabalha apenas temperaturas inteiras. A função round retorna a divisão com apenas dois algarismos.

Construindo a janela do conversor

A janela do conversor de temperaturas é construída pelos comandos dentro do método __init__ da classe TempConv. Este método é padrão usado para iniciar uma classe no Python.

Removi dois métodos da classe TempConv neste momento e os substitui por “…”. Estes métodos serão apresentados mais adiante.

# Classe TempConv
class TempConv:
    ...
    def __init__(self):
        win_main = gtk.Window(gtk.WINDOW_TOPLEVEL)
        win_main.set_position(gtk.WIN_POS_CENTER)
        win_main.set_title('TempConv')
        win_main.set_border_width(10)
        win_main.connect('destroy', gtk.main_quit)

Na primeira linha do método __init__, é instanciado a classe gtk.Window para o objeto win_main, nossa janela de fato. Embora seja passado a constante gtk.WINDOW_TOPLEVEL, este não era necessário, pois é o tipo padrão para a janela. Uma janela normal, destas utilizadas em caixas de diálogos e programas em geral. O outro valor possível para instanciar esta classes é gtk.WINDOW_POPUP, usada para janelas como tooltip.

O método set_position() diz onde a janela será colocada em sua tela. Os valores possíveis para este método são:

  • gtk.WIN_POS_NONE – nenhuma influência será feita no posicionamento;
  • gtk.WIN_POS_CENTER – a janela será colocada no centro do screen;
  • gtk.WIN_POS_CENTER_ALWAYS – sempre no centro, mesmo após um resize, …;
  • gtk.WIN_POS_MOUSE – a janela será aberta na posição corrente do mouse;
  • gtk.WIN_POS_CENTER_ON_PARENT – centra a janela em seu parente (veja o método gtk.Window.set_transient_for()).

O método set_title() define o texto que irá aparecer na parte superior da janela, e o set_border_width() a largura da borda da janela em pixels.

O método connect, a seguir, conecta o sinal “destroy” a gtk.main_quit(), saída do programa. Esta função (gtk.main_quit()) termina o loop principal iniciado por uma função gtk.main() que veremos mais adiante.

O sinal “destroy” ocorre ao clicar no botão “x”, da aba da janela, ativando o evento destroy da widget janela. Se nenhuma ação for definida para este sinal, embora a janela seja fachada, o loop principal continuara rodando, necessitando de um CONTROL+C para interromper o programa.

Desenhando os widgets da janela

Agora vem a parte divertida, desenhar os elementos da janela. O processo é bem mais simples que aparenta. Nada que um pouco de imaginação e alguns traços em uma folha branca não resolva.

A idéia é imaginar sua janela como sendo a soma de várias caixas verticais e horizontais, arranjadas de forma a suportarem os objetos desejados de forma compreensiva.

Se observar a figura ao lado, desenhei quatro caixas em linha vermelha. Três internas e uma externa, maior. Vou iniciar definindo esta caixa externa, instanciando a classe gtk.VBox() em vbox.

        vbox = gtk.VBox(False, 0)

Esta é uma caixa para armazenar objetos na vertical. Cada nova widget adicionada a esta caixa é arranjada em uma nova linha. O primeiro argumento em gtk.VBox(), diz ao construtor que as linhas não serão arranjadas homogeneamente, “False“. O segundo argumento é o espaçamento vertical, em pixels, entre os objetos. Os valores padrões são “False” e “0”, como inicializados nesta linha, por isto poderíamos tê-los omitido, aqui.

Em seguida tenho que adicionar esta caixa à janela principal. Isto é feito com o método add() da janela win_main:

        win_main.add(vbox)

Continuando a criação dos elementos da janela, vou construir duas caixas de texto para as temperaturas. Uma boa classe para este trabalho é o gtk.Entry(),

        Temp_C = gtk.Entry()
        Temp_F = gtk.Entry()

Para identificar estas caixas em tempo de execução, vou mudar o atributo name com o método set_name() e específica nomes distintos para as widgets. Adiante uso este atributo para identificar a conversão de temperatura adequada.

        Temp_C.set_name('Temp_C')
        Temp_F.set_name('Temp_F')

Em seguida inicio estas caixas com valores padrões (0C e 32F), usando o método set_text():

        Temp_C.set_text('0.00')
        Temp_F.set_text('32.00')

Para enquadra corretamente as caixas na janela, aconselho redimensionar ambas as widgets para a dimensão de 60 pixels de comprimento por 32 de atura. O método para este serviço é o set_size_request(),

        Temp_C.set_size_request(60,23)
        Temp_F.set_size_request(60,23)

Agora vou criar dois Labels com os conteúdos “Célsius” e “Fahrenheit”, para acompanhar nossas caixas de texto, acima:

        Label_C = gtk.Label("Celsius")
        Label_F = gtk.Label("Fahrenheit")

O próximo passo é colocá-los na janela. Primeiro vou criar uma caixa horizontal para colocar o Label_C com a caixa de texto Temp_C. Isto é feito com a classe gtk.HBox(), com os mesmos argumentos que o gtk.VBox() acima. Ao contrário da VBox, uma HBox arranja os objetos em colunas, deixando o Label_C e o Temp_C lado a lado em uma linha.

        hbox = gtk.HBox(True, 0)

Para adicionar o label e a caixa de texto, utilize o método pack_start():

        hbox.pack_start(Label_C, False, False, 3)
        hbox.pack_start(Temp_C, False, False, 3)

A sintaxe para o pack_start é bem simples:

pack_start(child, expand=True, fill=True, padding=0)

onde:

  • child – é a widget, que será adicionado a caixa (horizontal ou vertical);
  • expand – se True expande o espaço ocupado pela widget, com espaços ao redor;
  • fill – se True expande toda a widget para ocupar todo o espaço a ela destinado;
  • padding – é o espaço colocado entre as childs adicionadas a HBox.

Com a primeira caixa horizontal pronta, basta apenas adicioná-la à caixa vertical principal, vbox,

        vbox.pack_start(hbox, True, True, 0)

Repita o processo para a próxima linha horizontal, com as widgets Label_F e Temp_F,

        hbox = gtk.HBox(True, 0)
        hbox.pack_start(Label_F, False, False, 3)
        hbox.pack_start(Temp_F, False, False, 3)
        vbox.pack_start(hbox, True, True, 0)

A última widget que irei adicionar à janela principal, é o o botão “Close”. Um botão em PyGTK é criado, instanciando um gtk.Button(),

        Close_button = gtk.Button("Close")

Em seguida este botão será adicionado a uma caixa horizontal e a caixa vertical vbox, como segue:

        hbox = gtk.HBox(False, 0)
        hbox.pack_start(Close_button, True, True, 5)
        vbox.pack_start(hbox, False, False, 5)

Não se pode esquecer de conectar este botão à função gtk.main_quit(),

        button_Close.connect("clicked", gtk.main_quit)

Desta forma o sinal “clicked”, gerado ao clicar no botão, irá desligar o programa.

Os comandos a seguir são para conectar os sinais às subrotinas que irão processar as transformações de temperatura desejadas. Estas transformações serão feitas com dois métodos da classe TempConv, que ainda não os apresentei. O que eles farão é chamar a transformação adequada e apresentar o resultado na correspondente caixa de diálogo. Primeiro, irei associar os sinais aos correspondentes métodos:

        Temp_C.connect("focus-out-event", self.calcule_temp, Temp_F)
        Temp_F.connect("focus-out-event", self.calcule_temp, Temp_C)
        Temp_C.connect("key-press-event", self.change_focus, Temp_F)
        Temp_F.connect("key-press-event", self.change_focus, Temp_C)

O primeiro sinal conectado ao método self.calcule_temp é o “focus-out-event”. Este sinal será emitido sempre que as caixas de textos Temp_C e Temp_F perderem o foco.

O segundo sinal é conectado ao método self.change_focus é o “key-press-event”. Este sinal é gerado a cada tecla pressionada nas caixas de texto Temp_C e Temp_F.

Para terminar a inicialização desta classe, falta adicionar a vbox principal a janela e apresentar tudo. Isto é feito com os dois comandos seguintes:

        win_main.add(vbox)
        win_main.show_all()

Agora as funções change_focus e calcule_temp. A função change_focus irá mudar o foco para o widget seguinte, sempre que uma tecla ENTER, de código 65293, for pressionada:

    def change_focus(self, widget, event, Temp_out):
        if event.keyval == 65293:
            Temp_out.grab_focus()

Observe que esta função é usado para conectar o evento “key-press-event” da caixa Temp_C resultando como saída, mover o foco para a caixa Temp_F, e vice-versa. Na prática, isto significa que sempre que se pressionar a tecla ENTER, será gerado um sinal “focus-out-event”, processado pela função calcule_temp:

    def calcule_temp(self, widget, event, Temp_out):
        name = widget.get_name()
        temp = float(widget.get_text())
        if name == 'Temp_C':
            Temp_out.set_text('%0.2f' % calc_Fahrenheit(temp))
        else:
            Temp_out.set_text('%0.2f' % calc_Celsius(temp))

Nas duas primeiras linhas da função calcule_temp, são lidos o nome da widget que a chamou e a sua temperatura, para o cálculo da transformação. Em seguida é verifico a nome da widget que chamou a função, para calcular a transformação correta, chamando uma das funções de transformação (calc_Fahrenheit ou calc_Celsius) correspondente.

A saída é passada para a caixa de texto passada como Temp_out. Para terminar o programa, adicione as linhas a seguir:

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    TempConv()
    main()

Estes são procedimentos padrões em programas PyGTK.

Conclusão

O programa completo pode ser baixado da url abaixo:

http://localhost/linux/python/conversor.py

Este programa é apenas um pequeno exemplo do muito que o PyGTK pode lhe oferecer. Espero que isto contribua para o aprendizado, enriquecendo com mais um exemplo de código PyGTK comentado. Espero ter mais tempo no futuro para pode disponibilizar mais alguns exemplos.

Deixe um comentário

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.