23. Objetos

O GObject é o tipo fundamental que fornece os atributos e métodos comuns para todos os tipos de objeto no GTK+, no Pango e em outras bibliotecas baseadas no GObject. A classe GObject.GObject fornece métodos para construção e destruição de objetos, métodos de acesso a propriedades e suporte a sinais.

Esta seção apresentará alguns aspectos importantes sobre a implementação do GObject no Python.

23.1. Herdar de GObject.GObject

Um GObject nativo é acessível via GObject.GObject. É raramente instanciado diretamente, geralmente usamos classes herdadas. A Gtk.Widget é uma classe herdada de um GObject.GObject. Pode ser interessante criar uma classe herdada para criar um novo widget, como uma caixa de diálogo de configurações.

Para herdar de GObject.GObject, você deve chamar GObject.GObject.__init__() em seu construtor (se a classe herdar de Gtk.Button, deve chamar Gtk.Button.__init__() por exemplo), como no exemplo abaixo:

from gi.repository import GObject

class MyObject(GObject.GObject):

    def __init__(self):
        GObject.GObject.__init__(self)

23.2. Sinais

Os sinais conectam eventos específicos de aplicativos arbitrários com qualquer número de ouvintes. Por exemplo, no GTK+, cada evento de usuário (pressionamento de tecla ou mouse) é recebido do servidor X e gera um evento GTK+ sob a forma de uma emissão de sinal em uma determinada instância de objeto.

Cada sinal é registrado no sistema de tipos junto com o tipo no qual ele pode ser emitido: os usuários do tipo são conectados ao sinal em uma determinada instância de tipo quando registram uma função a ser invocada na emissão do sinal. Os usuários também podem emitir o sinal sozinhos ou interromper a emissão do sinal de dentro de uma das funções conectadas ao sinal.

23.2.1. Receba sinais

Veja Loop principal e sinais

23.2.2. Crie novos sinais

Novos sinais podem ser criados adicionando-os a GObject.GObject.__gsignals__, um dicionário:

Quando um novo sinal é criado, um manipulador de método também pode ser definido, ele será chamado toda vez que o sinal for emitido. É chamado do_nome_sinal.

class MyObject(GObject.GObject):
    __gsignals__ = {
        'my_signal': (GObject.SIGNAL_RUN_FIRST, None,
                      (int,))
    }

    def do_my_signal(self, arg):
        print("method handler for `my_signal' called with argument", arg)

GObject.SIGNAL_RUN_FIRST indica que este sinal invocará o manipulador do método de objeto (do_my_signal() aqui) no primeiro estágio de emissão. As alternativas são GObject.SIGNAL_RUN_LAST (o manipulador de método será invocado no terceiro estágio de emissão) e GObject.SIGNAL_RUN_CLEANUP (invoca o manipulador de método no último estágio de emissão).

A segunda parte, None, indica o tipo de retorno do sinal, geralmente None.

(int,) indicates the signal arguments, here, the signal will only take one argument, whose type is int. Types of arguments required by signal are declared as a sequence, here it is a one-element tuple.

Os sinais podem ser emitidos usando GObject.GObject.emit():

my_obj.emit("my_signal", 42) # emit the signal "my_signal", with the
                             # argument 42

23.3. Propriedades

Um dos ótimos recursos do GObject é seu mecanismo get/set genérico para propriedades de objetos. Cada classe herdada de GObject.GObject pode definir novas propriedades. Cada propriedade tem um tipo que nunca muda (por exemplo, str, float, int …). Por exemplo, eles são usados para Gtk.Button onde existe uma propriedade “label” que contém o texto do botão.

23.3.1. Use propriedades existentes

A classe GObject.GObject fornece várias funções úteis para gerenciar propriedades existentes, GObject.GObject.get_property() e GObject.GObject.set_property().

Algumas propriedades também possuem funções dedicadas a elas, chamadas de getter e setter. Para a propriedade “label” de um botão, existem duas funções para obter e configurá-las, Gtk.Button.get_label() e Gtk.Button.set_label().

23.3.2. Crie novas propriedades

Uma propriedade é definida com um nome e um tipo. Mesmo se o próprio Python for digitado dinamicamente, você não poderá alterar o tipo de uma propriedade depois que ela for definida. Uma propriedade pode ser criada usando GObject.Property.

from gi.repository import GObject

class MyObject(GObject.GObject):

    foo = GObject.Property(type=str, default='bar')
    property_float = GObject.Property(type=float)
    def __init__(self):
        GObject.GObject.__init__(self)

As propriedades também podem ser somente leitura, se você quiser que algumas propriedades sejam legíveis, mas não graváveis. Para fazer isso, você pode adicionar alguns sinalizadores à definição da propriedade, para controlar o acesso de leitura/gravação. Sinalizadores são GObject.ParamFlags.READABLE (somente acesso de leitura para código externo), GObject.ParamFlags.WRITABLE (somente acesso de gravação), GObject.ParamFlags.READWRITE (publico):

foo = GObject.Property(type=str, flags = GObject.ParamFlags.READABLE) # not writable
bar = GObject.Property(type=str, flags = GObject.ParamFlags.WRITABLE) # not readable

Você também pode definir novas propriedades somente leitura com um novo método decorado com GObject.Property:

from gi.repository import GObject

class MyObject(GObject.GObject):

    def __init__(self):
        GObject.GObject.__init__(self)

    @GObject.Property
    def readonly(self):
        return 'This is read-only.'

Você pode obter essa propriedade usando:

my_object = MyObject()
print(my_object.readonly)
print(my_object.get_property("readonly"))

A API de GObject.Property é semelhante ao construído em property(). Você pode criar o setter de propriedades de maneira semelhante à propriedade Python:

class AnotherObject(GObject.Object):
    value = 0

    @GObject.Property
    def prop(self):
        'Read only property.'
        return 1

    @GObject.Property(type=int)
    def propInt(self):
        'Read-write integer property.'
        return self.value

    @propInt.setter
    def propInt(self, value):
        self.value = value

Há também uma maneira de definir valores mínimos e máximos para números, usando um formulário mais detalhado:

from gi.repository import GObject

class MyObject(GObject.GObject):

    __gproperties__ = {
        "int-prop": (int, # type
                     "integer prop", # nick
                     "A property that contains an integer", # blurb
                     1, # min
                     5, # max
                     2, # default
                     GObject.ParamFlags.READWRITE # flags
                    ),
    }

    def __init__(self):
        GObject.GObject.__init__(self)
        self.int_prop = 2

    def do_get_property(self, prop):
        if prop.name == 'int-prop':
            return self.int_prop
        else:
            raise AttributeError('unknown property %s' % prop.name)

    def do_set_property(self, prop, value):
        if prop.name == 'int-prop':
            self.int_prop = value
        else:
            raise AttributeError('unknown property %s' % prop.name)

As propriedades devem ser definidas em GObject.GObject.__gproperties__, um dicionário e manipulado em do_get_property e do_set_property.

23.3.3. Veja as propriedades

Quando uma propriedade é modificada, um sinal é emitido, cujo nome é “notify::property-name”:

my_object = MyObject()

def on_notify_foo(obj, gparamstring):
    print("foo changed")

my_object.connect("notify::foo", on_notify_foo)

my_object.set_property("foo", "bar") # on_notify_foo will be called

Note que você tem que usar o nome da propriedade canônica ao se conectar aos sinais de notificação, como explicado em GObject.Object.signals.notify(). Por exemplo, para uma propriedade Python foo_bar_baz você conectaria ao sinal notify::foo-bar-baz usando

my_object = MyObject()

def on_notify_foo_bar_baz(obj, gparamstring):
    print("foo_bar_baz changed")

my_object.connect("notify::foo-bar-baz", on_notify_foo_bar_baz)

23.4. API

class GObject.GObject
get_property(property_name)

Recupera um valor de propriedade.

set_property(property_name, value)

Configura a propriedade property_name para valor.

emit(signal_name, ...)

Emite sinal signal_name. Argumentos de sinal devem seguir, p. ex., se o seu sinal é do tipo (int,), deve ser emitido com:

self.emit(signal_name, 42)
freeze_notify()

Este método congela todos os sinais “notify::” (que são emitidos quando qualquer propriedade é alterada) até que o método thaw_notify() seja chamado.

It is recommended to use the with statement when calling freeze_notify(), that way it is ensured that thaw_notify() is called implicitly at the end of the block:

with an_object.freeze_notify():
    # Do your work here
    ...
thaw_notify()

Thaw all the “notify::” signals which were frozen by freeze_notify().

Recomenda-se não chamar thaw_notify() explicitamente mas use freeze_notify() juntamente com a instrução with.

handler_block(handler_id)

Bloqueia um manipulador de uma instância para que ele não seja chamado durante qualquer emissão de sinal, a menos que handler_unblock() seja chamado para aquele handler_id. Assim, “bloquear” um manipulador de sinal significa desativá-lo temporariamente, um manipulador de sinal precisa ser desbloqueado exatamente na mesma quantidade de vezes que foi bloqueado antes de se tornar ativo novamente.

Recomenda-se usar handler_block() em conjunto com a instrução with que irá chamar handler_unblock() implicitamente no final do bloco:

with an_object.handler_block(handler_id):
    # Do your work here
    ...
handler_unblock(handler_id)

Desfaz o efeito de handler_block(). Um manipulador bloqueado é ignorado durante as emissões do sinal e não será chamado até que tenha sido desbloqueado exatamente a quantidade de vezes que foi bloqueado antes.

É recomendado não chamar explicitamente handler_unblock() mas use handler_block() junto com a instrução with.

__gsignals__

Um dicionário onde a classe herdada pode definir novos sinais.

Cada elemento no dicionário é um novo sinal. A chave é o nome do sinal. O valor é uma tupla, com o formato:

(GObject.SIGNAL_RUN_FIRST, None, (int,))

GObject.SIGNAL_RUN_FIRST can be replaced with GObject.SIGNAL_RUN_LAST or GObject.SIGNAL_RUN_CLEANUP. None is the return type of the signal. (int,) is the tuple of the parameters of the signal.

__gproperties__

The __gproperties__ dictionary is a class property where you define the properties of your object. This is not the recommended way to define new properties, the method written above is much less verbose. The benefits of this method is that a property can be defined with more settings, like the minimum or the maximum for numbers.

A chave é o nome da propriedade

O valor é uma tupla que descreve a propriedade. O número de elementos dessa tupla depende de seu primeiro elemento, mas a tupla sempre conterá pelo menos os seguintes itens:

O primeiro elemento é o tipo da propriedade (por exemplo, int, float…).

O segundo elemento é o apelido da propriedade, que é uma string com uma breve descrição da propriedade. Isso geralmente é usado por programas com fortes recursos de introspecção, como o construtor de interface gráfica de usuário Glade.

A terceira é a descrição da propriedade ou sinopse, que é outra string com uma descrição mais longa da propriedade. Também usado pelo Glade e programas similares.

O último (que não é necessariamente o último, como veremos mais adiante) é o sinalizador da propriedade GObject.PARAM_READABLE, GObject.PARAM_WRITABLE, GObject.PARAM_READWRITE.

O comprimento absoluto da tupla depende do tipo de propriedade (o primeiro elemento da tupla). Assim, temos as seguintes situações:

Se o tipo for bool ou str, o quarto elemento é o valor padrão da propriedade.

Se o tipo for int ou float, o quarto elemento é o valor mínimo aceito, o quinto elemento é o valor máximo aceito e o sexto elemento é o valor padrão.

Se o tipo não for um desses, não há elemento extra.

GObject.SIGNAL_RUN_FIRST

Invoca o manipulador de método de objeto no primeiro estágio de emissão.

GObject.SIGNAL_RUN_LAST

Invoca o manipulador de método de objeto no terceiro estágio de emissão.

GObject.SIGNAL_RUN_CLEANUP

Invoca o manipulador do método de objeto no último estágio de emissão.

GObject.ParamFlags.READABLE

A propriedade é legível.

GObject.ParamFlags.WRITABLE

A propriedade é gravável.

GObject.ParamFlags.READWRITE

A propriedade é legível e gravável.