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
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,)
indica os argumentos do sinal, aqui, o sinal só receberá um argumento, cujo tipo é int. Os tipos de argumentos exigidos pelo sinal são declarados como uma sequência, aqui é uma tupla de um elemento.
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.Recomenda-se usar a instrução with ao chamar
freeze_notify()
, dessa forma é assegurado quethaw_notify()
é chamado implicitamente no final do bloco:with an_object.freeze_notify(): # Do your work here ...
- thaw_notify()
Descongela todos os sinais “notify::” que foram congelados por
freeze_notify()
.Recomenda-se não chamar
thaw_notify()
explicitamente mas usefreeze_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á chamarhandler_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 usehandler_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
pode ser substituído porGObject.SIGNAL_RUN_LAST
ouGObject.SIGNAL_RUN_CLEANUP
.None
é o tipo de retorno do sinal.(int,)
é a tupla dos parâmetros do sinal.
- __gproperties__
O dicionário
__gproperties__
é uma propriedade de classe onde você define as propriedades do seu objeto. Esta não é a maneira recomendada de definir novas propriedades, o método escrito acima é muito menos detalhado. Os benefícios desse método é que uma propriedade pode ser definida com mais configurações, como o mínimo ou o máximo para números.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
oustr
, o quarto elemento é o valor padrão da propriedade.Se o tipo for
int
oufloat
, 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.