22. Glade e Gtk.Builder

The Gtk.Builder class offers you the opportunity to design user interfaces without writing a single line of code. This is achieved by defining the interface in an XML file and then loading that XML UI definition at runtime using the Builder class which creates the objects automatically. To avoid writing the XML manually use the Glade application which lets you create the user interface in a WYSIWYG (what you see is what you get) manner

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from gi.repository import Gtk


class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")
builder.connect_signals(Handler())

22.4. Exemplo

O código final do exemplo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")


builder = Gtk.Builder()
builder.add_from_file("builder_example.glade")
builder.connect_signals(Handler())

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

Gtk.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.

The UI definition file used in the example needs a small change to include a <template> element:

<?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>

Then it can be used to implement the example with a Gtk.Window subclass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


@Gtk.Template(filename="template_example.ui")
class Window1(Gtk.Window):
    __gtype_name__ = "window1"

    @Gtk.Template.Callback()
    def onDestroy(self, *args):
        Gtk.main_quit()

    @Gtk.Template.Callback()
    def onButtonPressed(self, button):
        print("Hello World!")


window = Window1()
window.show()

Gtk.main()

More information can be found at the PyGObject website.