PyGObject: Properties
Properties
Le Properties descrivono la configurazione e lo stato dei widgets.
Come per i segnali, ogni widget ha il proprio set di properties.
Ad esempio un Button ha la property “label” che contiene il testo della label dentro al button.
Quando creiamo un’istanza di un widget, possiamo specificare il nome e il valore di una property,
passandola come keyword argument.
Ad esempio per creare una label allineata sulla destra con il testo “Hello World” ed un angolo
di 25 gradi, useremo la sintassi:
label = Gtk.Label(label="Hello World", angle=25, halign=Gtk.Align.END)
che è l’equivalente di scrivere:
label = Gtk.Label() label.set_label("Hello World") label.set_angle(25) label.set_halign(Gtk.Align.END)
Invece di usare i getters e i setters, possiamo ottenere e settare le properties attraverso la
property props, con la sintassi:
widget.props.prop_name = value
Per sapere quali properties sono disponibili per un widget, possiamo scrivere:
>>> for prop in dir(button.props): ... print(prop) ... action_name action_target always_show_image app_paintable ... >>> button.props.label 'Ok' >>> button.props.label = "Click" >>> button.props.label 'Click'
Utilizzo di properties esistenti
La classe GObject.GObject fornisce le seguenti funzioni per gestire le properties esistenti:
GObject.GObject.get_property() e GObject.GObject.set_property().
Come accennato in precedenza, alcune properties hanno funzioni ad esse dedicate (getter e setter).
Ad esempio per la property “label” di un Button, ci sono 2 funzioni, la getter Gtk.Button.get_label()
e la setter Gtk.Button.set_label(label).
Creare nuove properties
Una property viene definita tramite “name” e “type”.
Nonostante Python stesso sia tipizzato dinamicamente, non potremo modificare una property,
una volta che questa è stata definita. Per creare una property utilizzaiamo GObject.Property:
from gi.repository import GObject class CustomObject(GObject.GObject): pr_string = GObject.Property(type=str, default='bancaldo') pr_float = GObject.Property(type=float) def __init__(self): super().__init__()
Possiamo decidere che una property sia “readable”, “writable”, o “readwrite”.
Per farlo è necessario settare alcuni flags, in fase di definizione della property stessa.
Queste Flags sono:
GObject.ParamFlags.READABLE: solo accesso in lettura;
GObject.ParamFlags.WRITABLE: solo accesso in scrittura;
GObject.ParamFlags.READWRITE: property pubblica, con accesso in
lettura e scrittura.
pr_1 = GObject.Property(type=str, flags = GObject.ParamFlags.READABLE) # non scrivibile pr_2 = GObject.Property(type=str, flags = GObject.ParamFlags.WRITABLE) # non leggibile
Per definire una nuova property read-only possiamo utilizzare un metodo
con decoratore GObject.Property:
from gi.repository import GObject class CustomObject(GObject.GObject): def __init__(self): super().__init__() @GObject.Property def readonly(self): return 'This is read-only property.'
Per accedere a questa property:
>>> myobj = CustomObject() >>> myobj.readonly 'This is read-only property.' >>> myobj.get_property("readonly") 'This is read-only property.'
La API di GObject.Property è molto simile alla built-in property() di python.
Possiamo creare i setter in maniera simile:
class CustomObject(GObject.Object): value = 0 @GObject.Property def readonly(self): 'Read only property.' return 1 @GObject.Property(type=int) def my_int(self): 'Read-write integer property.' return self.value @my_int.setter def my_int(self, value): self.value = value
>>> myobj = CustomObject() >>> myobj.readonly 1 >>> myobj.my_int 0 >>> myobj.my_int = 2 >>> myobj.my_int 2
Ad esempio è possibile anche definire un valore minimo e massimo per i numeri:
from gi.repository import GObject class CustomObject(GObject.GObject): __gproperties__ = { "int-prop": (int, # type "integer prop", # nick "A property that contains an integer", # desc 1, # min 5, # max 2, # default GObject.ParamFlags.READWRITE # flags ), } def __init__(self): super().__init__() self.int_prop = 2 def do_get_property(self, prop): return self.int_prop def do_set_property(self, prop, value): self.int_prop = value
Le properties, come si nota, vanno definite nel dizionario __gproperties__ di
GObject.GObject, e gestite con i metodi:
do_get_property e do_set_property.
>>> obj = CustomObject() >>> obj.get_property("int-prop") 2 >>> obj.set_property("int-prop", 10) <string>:1: Warning: value "10" of type 'gint' is invalid or out of range for property 'int-prop' of type 'gint' >>> obj.set_property("int-prop", -2) <string>:1: Warning: value "-2" of type 'gint' is invalid or out of range for property 'int-prop' of type 'gint' >>> obj.set_property("int-prop", 4) >>> obj.get_property("int-prop") 4
se la property richiesta non è esistente, ovvero non fa parte del dizionario __gproperties__,
verrà sollevata una eccezione TypeError:
obj = CustomObject() myprop = "int-propp" try: obj.get_property(myprop) except TypeError: print("ERROR: %s property not found!" % myprop) ERROR: int-propp property not found!
Segnale modifica property
Quando una property viene modificata, viene emesso un segnale il cui nome è
“notify::property-name”.
E’ quindi possibile connettere il segnale di modifica della property, del nostro oggetto, ad una
callback, con il metodo connect(signal, callback):
>>> def on_notify_int_prop(obj, string): ... print("INFO: 'int-prop' property changed") ... >>> obj.connect("notify::int-prop", on_notify_int_prop) 1 >>> obj.set_property("int-prop", 2) INFO: 'int-prop' property changed
Nota:
Per convenzione il nome della callback da usare con il metodo connect(), per un segnale
“notify::property-name”, sarà “on_notify_property_name”.
API
Ricapitolando vediamo i metodi principali della classe GObject.GObject:
get_property(property_name)
Recupera il valore di una property di nome “property_name”.
set_property(property_name, value)
Setta il valore di una property di nome “property_name” al valore “value”, passato come argomento.
emit(signal_name, …)
Emette un segnale di nome “signal_name”. Gli argomenti opzionali da passare al segnale devono
seguire “signal_name”. Se ad esempio il nostro segnale è di tipo (int,), viene emesso come segue:
self.emit(signal_name, 42)
freeze_notify()
Questo metodo congela tutti i segnali “notify::” (che vengono cioè emessi ogni volta che una
property viene modificata), finchè non viene invocato il metodo thaw_notify().
E’ consigliato utilizzare il metodo freeze_notify(), all’interno di un
with statement, in modo da assicurarsi che il metodo thaw_notify(),
sia invocato implicitamente alla fine del blocco with utilizzato.
with an_object.freeze_notify(): # Do stuff ...
thaw_notify()
“scongela” tutti i segnali “notify::” che erano stati congelati dal metodo freeze_notify().
Come detto in precedenza non è consigliato utilizzare thaw_notify() esplicitamente,
ma utilizzare freeze_notify() con un with statement.
handler_block(handler_id)
Blocca un handler di un’istanza che non potrà essere chiamato durante l’emissione di un segnale
che lo coinvolga, fino a chè non sarà chiamato il complementare handler_unblock().
Questo blocco significa disattivare l’handler temporaneamente.
Un signal-handler deve essere sbloccato lo stesso numero di volte che è stato bloccato, prima di
tornare attivo. Come per freeze/thaw, è raccomandato utilizzare handler_block()
all’interno di un with statement, che, raggiunta la fine, chiamerà implicitamente
handler_unblock():
with an_object.handler_block(handler_id): # Do stuff ...
handler_unblock(handler_id)
Annulla l’effetto di handler_block(). Un handler boccato viene saltato durante
l’emissione di un segnale e non sarà invocato fino a quando non verrà sbloccato lo stesso numero
di volte per cui è stato bloccato.
Come già visto non è consigliabile chiamare handler_unblock() esplicitamente,
ma usare handler_block() all’interno di un with statement.
__gsignals__
Un dizionario dove le classi ereditate possono definire nuovi segnali.
Ogni elemento nel dizionario è un nuovo segnale. La chiave è il nome del segnale, il valore
invece, una tupla con la forma (GObject.SIGNAL_RUN_FIRST, None, (int,))
GObject.SIGNAL_RUN_FIRST: può essere rimpiazzato con
GObject.SIGNAL_RUN_LAST o GObject.SIGNAL_RUN_CLEANUP.
None: ciò che ritorna quel segnale;
(int,): lista dei parametri del segnale, deve finire con virgola.
__gproperties__
Un dizionario dove è possibile definire le properties del nostro oggetto.
Questo non è il metodo raccomandato per definire nuove proprietà, ma è utile per aggiungere
caratteristiche supplementari come valore minimo e massimo ammessi.
La chiave è il nome della property, mentre il valore è una tupla che descrive tale property.
Il numero degli elementi di questa tupa è variabile e dipende dal primo elemento, ma la tuple
conterrà sempre, almeno i seguenti elementi in questo ordine:
property’s type esmepio int, float,
property’s nick name: una breve descrizione della property
(utilizzato da programmi come Glade);
property’s description (blurb): un’altra stringa con descrizione
più esaustiva (usata da Glade e programmi simili);
property’s flags: indica il tipo di accesso alla property,
GObject.PARAM_READABLE, GObject.PARAM_WRITABLE o GObject.PARAM_READWRITE;
In base al primo elemento della tupla (property’s type), possono seguire altri elementi.
gli scenari sono:
– il primo elemento è di tipo bool o str, l’elemento successivo sarà:
default: valore di default della property;
– il type è int o float:
minimum: il minimo valore accettato,
maximum: il valore massimo accettato;
default: valore di default della property;
– il type non è nessuno di essi: nessun extra elemento
GObject.SIGNAL_RUN_FIRST
Invoca il method handler dell’oggetto nel primo stadio dell’emissione;
GObject.SIGNAL_RUN_LAST
Invoca il method handler dell’oggetto nel terzo stadio dell’emissione;
GObject.SIGNAL_RUN_CLEANUP
Invoca il method handler dell’oggetto nell’ultimo stadio dell’emissione;
GObject.ParamFlags.READABLE
La property è in sola lettura e non soggetta a modifica;
GObject.ParamFlags.WRITABLE
La property è in scrittura e quindi soggetta a modifica;
GObject.ParamFlags.READWRITE
La property è in lettura e scrittura (pubblica);
Unicode
Da Python3.0, tutte le stringhe sono memorizzate come Unicode in un’istanza di str type.
Le Encoded strings, d’altro canto, sono rappresentate come binary data, in forma di istanza di byte type.
In parole povere str fa riferimento a text, mentre bytes
fa riferimento a data.
Per convertire da str a bytes, usare str.encode(),
da bytes a str, usare bytes.decode().
Inoltre non è più possibile mixare strings con encoded strings:
>>> s = "àòè" >>> s.encode() b'\xc3\xa0\xc3\xb2\xc3\xa8' >>> s.encode("utf-8") b'\xc3\xa0\xc3\xb2\xc3\xa8' >>> b = s.encode("utf-8") >>> b b'\xc3\xa0\xc3\xb2\xc3\xa8' >>> b.decode("utf-8") 'àòè'
In GTK+ le cose sono molto più comode perchè PyGObject fa automaticamente l’encode/decode da/in
UTF-8, quando passeremo una stringa ad un metodo, o un metodo ritornerà una stringa.
In ogni caso verrà sempre rappresentato un testo/stringa come instanza di str:
>>> import gi >>> gi.require_version("Gtk", "3.0") >>> from gi.repository import Gtk >>> label = Gtk.Label() >>> text = "\xc3\xa7\xc3\xa7\xc3\xa7" >>> label.set_text(text) >>> txt = label.get_text() >>> type(txt), txt (<class 'str'>, 'ççç') >>> txt == text True
link di riferimento:
torna all’indice degli appunti
python gtk3 tutorial
Gtk3
Commenti recenti