Radio Buttons com Glade e PyGObject

Devido a uma pergunta interessante feito pelo Cassimiro Andrade aqui resolvi fazer este post mostrando como criar o programa com as funcionalidades sugeridas pelo Cassimiro no comentário.
Basicamente irei mostrar como criar um programa que irá modificar uma determinada palavra, e as opções de modificação desta palavra será definida por um radio button selecionado em um grupo de radio buttons.

Primeiramente tenha em mente que os passos descritos aqui foram realizados com a versão 3.12.1 do Glade e a versão 3.2 do Python.

Como estou utilizando o ubuntu 12.04 precisei instalar todos os componentes necessários, felizmente os mesmos estão disponíveis no próprio repositório do sistema, portanto basta executar os comandos abaixo para instala-los



 # apt-get install python3 glade python3-gi

Caso esteja utilizando o fedora instale da seguinte forma



 # yum install python3-gobject python3 glade 

Uma vez que tenha tudo instado podemos começar criando a interface, abaixo segue como será a estrutura do programa criado no glade

Janela Principal - GtkWindow
       `------- Divisão vertical com 4 partes - GtkBox
         |
         `------+ Quadro de Opções com a etiqueta Opções - GtkFrame
         |    `------------+  Caixa para os Radio Buttons - GtkButtonBox
         |          `------------------  3 Radio Buttons - GtkRadioButton
         |
         `--------+ Quadro de inserção da palavra - GtkFrame
         |     `-------------- Entrada de texto - GtkEntry
         |
         `-------+ Caixa para adicionar o botão de processamento - GtkButtonBox
         |      `----------------- Botão de processamento - GtkButton
         |
         `---------- + Barra de rolagem - GtkScrolledWindow
                `---------------- Área de visualização da palavra modificada - GtkTextView
        
Em imagem fica melhor não ?


Perceba na imagem acima as opções que estarão disponíveis no programa, nada muito complicado apenas simples manipulações de um texto como inverte-lo, criar anagramas ou deixar a palavra com letras maiúsculas ( a sugestão no comentário foi realizar um Capitalize na palavra mas eu preferi deixar todas as letras em maiúsculo do que apenas a primeira letra, é possível realizar Capitalize com o método "string".capitalize() no python).

Ok vamos começar a criar a interface no glade, inicie criando um top level window que será nossa janela principal e adicione um GtkBox separado em 4 partes conforme as imagens abaixo


Gtk Window
GtkBox dividido em 4 partes
Adicione um GtkFrame na primeira e segunda parte do GtkBox




Gtk Frames
Veja que cada frame tem um rótulo (frame1, frame2) pois o GtkFrame vem com um GtkLabel junto, mude o label do frame 1 para Opções e o label do frame2 para Palavra na aba geral de cada label.



Agora vamos adicionar um GtkButtonBox  com 3 campos no frame Opções que irá suportar os radio buttons, adicione no centro do frame onde está vazio conforme imagem abaixo



GtkButtonBox

Adicione um GtkRadioButton em cada box do GtkButtonBox




GtkRadioButton(s)
Mude os Rótulos dos radio buttons na aba geral para os respectivos nomes de suas ações Inverter, Anagrama, Letra Maiúscula


Note na imagem acima que os radio buttons estão todos ativos (selecionados) isso ocorre por que nenhum botão sabe da existência dos outros.

Para que apenas um radio button esteja ativo devemos agrupa-los para que todos os radio buttons se enxerguem, quando agrupamos radio buttons, definimos um dos botões como o grupo em si, basicamente este botão (o pai do grupo) será o que estará ativo quando o programa for executado pela primeira vez e obviamente os outros botões estarão desativados.

Devemos adicionar apenas os botões desativados ao grupo do botão pai que será o grupo em si, nesse caso iremos definir o radio button com o rótulo de Inverter como grupo logo não iremos mexer nele e sim nos outros botões restantes Anagrama e Letra Maiúscula.

Na aba geral desses botões clique na opção Grupo e na janela que abrir selecione o botão pai (radiobutton1 - Inverter) e clique em ok, faça este procedimento com o radio button Anagrama e Letra Maiúscula.





Observação: No momento que eu estava criando a interface eu não fui renomeando os widgets em cada passo e por isso os widgets estão com os nomes que são criado por padrão mas é uma boa pratica renomear os widgets com seus devidos nomes para você se organizar e não se perder, outro motivo para renomear os widgets é para poder obtê-los mais facilmente no seu código python.
Na imagem acima o widget radiobutton1 é o que está com o rótulo Inverter e deveria estar com o nome de inverter também.

Após agrupar os botões apenas o botão pai deverá estar ativo e na aba geral na entrada do grupo deverá constar o nome do grupo (botão pai == radiobutton1) como na imagem abaixo



Agora os radio buttons estão agrupados e conseguem se enxergarem, portanto ao selecionar um botão automaticamente os outros botões do grupo se desativarão.

Saindo um pouco do passo a passo, o Cassimiro fez esse comentário  perguntou como ficaria a questão dos signals com um grupo de radio buttons, e essa é uma parte interessante do tutorial de se levantar essa questão, por que basicamente o programa terá um botão processar que quando clicado irá verificar qual dos radio button está ativo e efetuar seu determinado processo, indo direto ao ponto a identificação do botão ativo será realizada em código python onde nós iremos obter o widget do pai do grupo (radiobutton1 == Inverter) que consequentemente irá nos trazer as informações dos outros botões então o único signal necessário para o procedimento do programa será realizado por um outro botão único que iremos adicionar mais adiante. De qualquer forma as coisas irão ficar mais claro no código python do programa.

Continuando a criação da interface, agora adicione um GtkEntry no GtkFrame Palavra





GtkEntry
No terceiro campo do GtkBox adicione outro GtkButtonBox com 1 único campo que irá suportar nosso botão que irá realizar todo o processo do programa



E adicione um GtkButton no GtkBox que foi criado




GtkButton

Na aba Geral do novo GtkButton altere o campo Rótulo para Processar


No último campo do GtkBox adicione um GtkScrolledWindow (Barra de rolagem)


GtkScrolledWindow

Na aba Empacotamento do GtkScrolledWindow ative a opção Expandir para que ele ocupe todo o espaço disponível em seu campo no GtkBox




Dentro do GtkScrolledWindow insira o GtkTextView que será utilizado para exibir a palavra modificada




GtkTextView

Agora para finalizar a criação da interface, vamos configurar os siginals necessários que serão apenas dois, o signal delete-event do widget da nossa janela principal que serve para finalizar o programa e o signal clicked do botão Processar que irá chamar uma função do código python e executar os procedimentos necessários.

Na aba Sinais do GtkWindow configure no setor GtkWidget o signal delete-event




Na aba Sinais do GtkButton Processar configure o signal clicked no setor GtkButton


Com isso a interface está basicamente pronta, basta salvar o trabalho em um arquivo .glade que toda a interface estará disponível em formato xml.

Antes de salvar o arquivo eu realizei algumas modificações como simples acabamentos, alinhei os labels no centro do programa, adicionei uma mensagem amigável no GtkEntry e defini um tamanho limitado de 10 caracteres para que palavras muito grandes não serem inseridas, o motivo é por que a função que utilizo para criar um anagrama da palavra irá gerar todas as combinações possíveis com os caracteres da palavra inserida o que pode gerar erros de memória caso a palavra seja muito grande.
Renomeei os widgets para que eu possa obtê-los mais facilmente através do python, não mostrei como fazer essas modificações por serem simples e o posto já está bem grande, mas são modificações simples que você pode realizar nas abas de gerenciamento dos widgets. No final do Post eu vou deixar um link com o arquivo .glade gerado neste tutorial.

Abaixo segue o código comentado do programa e mais adiante uma explicação mais detalhada da função que checa os radio buttons




#!/usr/bin/python3

from gi.repository import Gtk
from itertools import permutations

class ManageWord(object):
    def __init__(self):
        # Obtem uma Instância do Gtk.Builder e adiciona o arquivo .glade
        builder = Gtk.Builder()
        builder.add_from_file("radio_buttons.glade")
        # Obtendo os widgets
        self.window = builder.get_object("window")
        self.window.show()
        # GtkEntry onde será inserida a palavra
        self.word_entry = builder.get_object("word_entry")
        # GtkTextView que onde será exibido a palavra modificada
        self.text_view = builder.get_object("textview1")
        # Botão Processar
        self.button = builder.get_object("process_button")
        # Radio button com o rótulo Inverter que é o botão Pai
        self.inverter_radio = builder.get_object("inverter_radio")
        # Conectando os Sinais
        builder.connect_signals({
                                "gtk_main_quit": Gtk.main_quit,
                                "process_button_clicked": self.process_option,
                                "on_word_entry_activate": self.process_option
                                })

    # Métodos com as funções de modificação da palavra
    
    def invert_word(self, word):
        """ Inverte a palavra """        
        word = word[::-1]
        return word
        
    def get_anagram(self, word):
        """ Cria anagrams da palavra """
        # Cuidado com essa função pois ela pode causar erros
        # de falta de memória pois gera todas as palavras possíveis
        anagrams = ["".join(anagram) for anagram in permutations(word)]
        
        # Retorna somente as primeiras 100 palavras
        return anagrams[:100]
                                                                          
    def get_active_radio(self):
        """ Verifica qual Radio Button está ativo"""
        # Obtem uma lista com os radio buttons que pertencem ao grupo
        radio_buttons = self.inverter_radio.get_group()
        # Percore a lista de botões e verifica qual botão está ativo
        for radio in radio_buttons:
            # check if option is activated
            if radio.get_active():
                # Retorna o Rótulo do Radio Button que está ativo
                return radio.get_label()
            
    def process_option(self, widget):
        """ Executa a modificação da palavra baseado na opção ativa """
        # Obtem a palavra que foi inserida pelo usuário
        word = self.word_entry.get_text()
        # Obtem o campo de inserção onde a palavra modificada será exibida
        text_buffer = self.text_view.get_buffer()
        option = self.get_active_radio()
        # Verifica a opção escolhida e executa o procedimento necessário
        if option == "Inverter":
            output = self.invert_word(word)        
        elif option == "Anagrama":
            # Obtem uma lista com os anagramas
            list_output = self.get_anagram(word)
            # Cria uma string com todos os elementos da lista
            output = ""
            for line in list_output:
                output += line + "\n"
                
        elif option == "Letra Maiúscula":
            output = word.upper()
                            
        # Inseri o texto modificado no GtkTextView
        text_buffer.set_text(output)
        
if __name__ == "__main__":
    # Instância da classe do programa
    app = ManageWord()
    # Loop para o programar continuar rodando até o usuário fecha-lo
    Gtk.main()
        
            

Com o código acima e o arquivo .glade no mesmo diretório podemos executar o programa

 
# De permissão de execução
chmod +x radio_buttons.py
# Execute
./radio_buttons.py


Programa em execução

Invertendo a palavra

Criando Anagramas

Deixando as letras maiúsculas

O arquivo .py e .glade criados nesse post podem ser baixados aqui


Agora vou dar uma breve explicação um pouco mais detalhada sobre a seguinte função:

def get_active_radio(self):
        """ Verifica qual Radio Button está ativo"""
        # Obtem uma lista com os radio buttons que pertencem ao grupo
        radio_buttons = self.inverter_radio.get_group()
        # Percore a lista de botões e verifica qual botão está ativo
        for radio in radio_buttons:
            # check if option is activated
            if radio.get_active():
                # Retorna o Rótulo do Radio Button que está ativo
                return radio.get_label()

Essa função é utilizada para obter o radio button que está ativo no momento em que o botão Processar do programa é pressionado, ela utiliza o objeto inverter_radio definido no constructor da classe, como esse objeto é o grupo (botão pai) dos radio buttons ele traz consigo uma referência dos outros botões por isso podemos obter uma lista de todos os botões do grupo com o método .get_group() da instância deste widget.

Veja abaixo um exemplo prático executado no shell python



>>> from gi.repository import Gtk
>>> builder = Gtk.Builder()
>>> builder.add_from_file("radio_buttons.glade")
1

# aqui eu obtenho o widget inverter_radio que é grupo
>>> inverter_radio = builder.get_object("inverter_radio")

# utilizo o método .get_group() que irá me retornar uma lista com todos 
# os botões do grupo
>>> radio_group = inverter_radio.get_group()
>>> len(radio_group)
3

# Cada elemento do grupo é um radio button porém representado como um objeto
# do python e seu endereço único na memória
>>> radio_group
[<RadioButton object at 0x1188550 (GtkRadioButton at 0x154e2e0)>,

 <RadioButton object at 0x1188730 (GtkRadioButton at 0x154e170)>,

 <RadioButton object at 0x11880f0 (GtkRadioButton at 0x154e000)>]

# Cada radio button tem um método .get_name() que em teoria deveria retornar
# o nome definido no glade mas retorna a classe 
>>> radio_group[0].get_name()
'GtkRadioButton'

# Felizmente o método .get_label() retorna corretamente o Rótulo do radio button
>>> radio_group[0].get_label()
'Letra Maiúscula'

# Com o método .get_active() retornando True ou False podemos verificar 
# se o botão está ativo (selecionado)
>>> radio_group[0].get_active()
False

# Com esse métodos podemos verificar qual botão está ativo e obter seu rótulo
>>> for button in radio_group:
...   print(button.get_label(), button.get_active())
... 
Letra Maiúscula False
Anagrama False
Inverter True
>>> 

O GtkRadioButton possuí um signal chamado  toggled na seção GtkToggledButton, que é disparado toda vez que determinado botão for ativado, nesse programa seria possível configurar esse signal para que quando um radio_button for selecionado imediatamente chamaria uma função que iria armazenar o nome ou rótulo do radio button em uma variável global e dessa forma já saberíamos qual opção foi escolhida, porém trabalhar com uma variável global que fica mudando aleatoriamente desta forma não é considerado uma boa prática.

Basicamente é isso espero que esse post possa ajudar alguém e principalmente sanar a dúvida do Cassimiro Andrade.

Deixe um comentário