22. Glade e Gtk.Builder

A classe Gtk.Builder oferece a você a oportunidade de projetar interfaces de usuário sem escrever uma única linha de código. Isso é alcançado definindo a interface em um arquivo XML e, em seguida, carregando aquela definição XML de UI em tempo de execução usando a classe Builder que cria os objetos automaticamente. Para evitar a escrita do XML manualmente, use o aplicativo Glade, o qual permite criar a interface do usuário de uma maneira WYSIWYG (o que você vê é o que obtém)

Esse método possui várias vantagens:

  • Menos código precisa ser escrito.

  • As mudanças da interface do usuário podem ser vistas mais rapidamente, para que as interfaces de usuário possam melhorar.

  • Designers sem habilidades de programação podem criar e editar interfaces de usuário.

  • A descrição da interface do usuário é independente da linguagem de programação utilizada.

Ainda existe código necessário para lidar com mudanças de interface acionadas pelo usuário, mas Gtk.Builder permite que você se concentre em implementar essa funcionalidade.

22.1. Criando e carregando o arquivo .glade

Primeiro de tudo você tem que baixar e instalar o Glade. Existem vários tutoriais sobre o Glade, então isso não é explicado aqui em detalhes. Vamos começar criando uma janela com um botão e salvando-a em um arquivo chamado example.glade. O arquivo XML resultante deve se parecer com isso.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
      </object>
    </child>
  </object>
</interface>

Para carregar este arquivo em Python, precisamos de um objeto Gtk.Builder.

builder = Gtk.Builder()
builder.add_from_file("example.glade")

A segunda linha carrega todos os objetos definidos em example.glade no objeto Builder.

Também é possível carregar apenas alguns dos objetos. A linha a seguir adicionaria apenas os objetos (e seus objetos filhos) fornecidos na tupla.

# we don't really have two buttons here, this is just an example
builder.add_objects_from_file("example.glade", ("button1", "button2"))

Esses dois métodos também existem para o carregamento de uma string, em vez de um arquivo. Seus nomes correspondentes são Gtk.Builder.add_from_string() e Gtk.Builder.add_objects_from_string() e eles simplesmente pegam uma string XML em vez de um nome de arquivo.

22.2. Acessando widgets

Agora que a janela e o botão estão carregados, também queremos mostrá-los. Portanto, o método Gtk.Window.show_all() deve ser chamado na janela. Mas como acessamos o objeto associado?

window = builder.get_object("window1")
window.show_all()

Cada widget pode ser recuperado do construtor pelo método Gtk.Builder.get_object() e pelo id do widget. É realmente isso simples.

Também é possível obter uma lista de todos os objetos com

builder.get_objects()

22.3. Conectando sinais

O Glade também permite definir sinais que você pode conectar a manipuladores em seu código sem extrair todos os objetos do construtor e conectar-se aos sinais manualmente. A primeira coisa a fazer é declarar os nomes dos sinais no Glade. Para este exemplo, vamos agir quando a janela é fechada e quando o botão foi pressionado, então damos o nome “onDestroy” para o retorno de chamada manipulando o sinal “destroy” da janela e “onButtonPressed” para o retorno de chamada manipulando o sinal “pressed” do botão. Agora o arquivo XML deve ficar assim.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can-focus">False</property>
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
        <property name="use-action-appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </object>
</interface>

Agora temos que definir as funções do manipulador em nosso código. O onDestroy deve simplesmente resultar em uma chamada para Gtk.main_quit(). Quando o botão é pressionado, gostaríamos de imprimir a string “Hello World!”, Então definimos o manipulador da seguinte maneira

def hello(button):
    print("Hello World!")

Em seguida, temos que conectar os sinais e as funções do manipulador. A maneira mais fácil de fazer isso é definir um dict com um mapeamento dos nomes para os manipuladores e então passá-lo para o método Gtk.Builder.connect_signals().

handlers = {
    "onDestroy": Gtk.main_quit,
    "onButtonPressed": hello
}
builder.connect_signals(handlers)

Uma abordagem alternativa é criar uma classe que tenha métodos que sejam chamados como os sinais. Em nosso exemplo, o último snippet de código pode ser reescrito como:

 1from gi.repository import Gtk
 2
 3
 4class Handler:
 5    def onDestroy(self, *args):
 6        Gtk.main_quit()
 7
 8    def onButtonPressed(self, button):
 9        print("Hello World!")
10builder.connect_signals(Handler())

22.4. Exemplo

O código final do exemplo

 1import gi
 2
 3gi.require_version("Gtk", "3.0")
 4from gi.repository import Gtk
 5
 6
 7class Handler:
 8    def onDestroy(self, *args):
 9        Gtk.main_quit()
10
11    def onButtonPressed(self, button):
12        print("Hello World!")
13
14
15builder = Gtk.Builder()
16builder.add_from_file("builder_example.glade")
17builder.connect_signals(Handler())
18
19window = builder.get_object("window1")
20window.show_all()
21
22Gtk.main()

22.5. Gtk.Template

Gtk.WidgetClass allows UI definition files to be used to extend a widget, PyGObject provides Gtk.Template as a way of accessing this from Python.

O arquivo de definição de UI usado no exemplo precisa de uma pequena alteração para incluir um elemento <template>:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <template class="window1" parent="GtkWindow">
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
        <property name="use-action-appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </template>
</interface>

Então ele pode ser usado para implementar o exemplo com uma subclasse Gtk.Window:

 1import gi
 2
 3gi.require_version("Gtk", "3.0")
 4from gi.repository import Gtk
 5
 6
 7@Gtk.Template(filename="template_example.ui")
 8class Window1(Gtk.Window):
 9    __gtype_name__ = "window1"
10
11    @Gtk.Template.Callback()
12    def onDestroy(self, *args):
13        Gtk.main_quit()
14
15    @Gtk.Template.Callback()
16    def onButtonPressed(self, button):
17        print("Hello World!")
18
19
20window = Window1()
21window.show()
22
23Gtk.main()

More information can be found at the PyGObject website.