23. Objekt
GObject är den grundläggande typen som tillhandahåller de gemensamma attributen och metoderna för alla objekttyper i GTK+, Pango och andra bibliotek baserade på GObject. Klassen GObject.GObject
tillhandahåller metoder för skapande och förstörande av objekt, åtkomstmetoder för egenskaper samt stöd för signaler.
Detta avsnitt kommer introducera några viktiga aspekter om GObject-implementationen i Python.
23.1. Ärv från GObject.GObject
Ett inhemskt GObject är åtkomligt via GObject.GObject
. Det instansieras sällan direkt, vi använder i allmänhet ärvda klasser. En Gtk.Widget
är en ärvd klass av ett GObject.GObject
. Det kan vara intressant att göra en ärvd klass för att skapa en ny komponent, exempelvis en inställningsdialog.
För att ärva från GObject.GObject
måste du anropa GObject.GObject.__init__()
i din konstruktor (om klassen exempelvis ärver från Gtk.Button
så måste den anropa Gtk.Button.__init__()
), som i exemplet nedan:
from gi.repository import GObject
class MyObject(GObject.GObject):
def __init__(self):
GObject.GObject.__init__(self)
23.2. Signaler
Signaler ansluter godtyckliga programspecifika händelser till ett valfritt antal lyssnare. Till exempel så tas i GTK+ varje användarhändelse (tangenttryckning eller musrörelse) emot från X-servern och genererar en GTK+-händelse i form av en signalutsändning på en given objektinstans.
Varje signal registreras i typsystemet tillsammans med typen på vilken den kan sändas ut: användare av typen sägs ansluta till signalen på en given typinstans när de registrerar en funktion som ska anropas vid signalutsändning. Användare kan också sända ut signalen själva eller stoppa utsändningen av signalen inifrån en av funktionerna som är anslutna till signalen.
23.2.1. Ta emot signaler
23.2.2. Skapa nya signaler
Nya signaler kan skapas genom att lägga till dem till GObject.GObject.__gsignals__
, en ordbok:
Då en ny signal skapas kan en metodhanterare också definieras, den kommer anropas varje gång signalen sänds ut. Den kallas do_signalnamn.
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
indikerar att denna signal kommer anropa objektets metodhanterare (här do_my_signal()
) i det första utsändningssteget. Alternativ är GObject.SIGNAL_RUN_LAST
(metodhanteraren kommer anropas i det tredje utsändningssteget) och GObject.SIGNAL_RUN_CLEANUP
(anropa metodhanteraren i det sista utsändningssteget).
Den andra delen, None
, indikerar returtypen för signalen, vanligen None
.
(int,)
indikerar signalargumenten, här kommer signalen bara ta ett argument, vars typ är int. Typer av argument som krävs av signalen deklareras som en sekvens, här är det en tupel med ett argument.
Signaler kan sändas ut med GObject.GObject.emit()
:
my_obj.emit("my_signal", 42) # emit the signal "my_signal", with the
# argument 42
23.3. Egenskaper
En av de trevliga funktionerna med GObject är dess allmäna get/set-mekanism för objektegenskaper. Varje klass som ärver från GObject.GObject
kan definiera nya egenskaper. Varje egenskap har en typ som aldrig ändras (t.ex. str, float, int…). De används exempelvis för Gtk.Button
där det finns en egenskap ”label” som innehåller knappens text.
23.3.1. Använd befintliga egenskaper
Klassen GObject.GObject
tillhandahåller flera användbara funktioner för att hantera befintliga egenskaper, GObject.GObject.get_property()
och GObject.GObject.set_property()
.
Vissa egenskaper har också avsedda get- och set-funktioner. För egenskapen ”label” för en knapp finns det två funktioner för att erhålla och ställa in den, Gtk.Button.get_label()
och Gtk.Button.set_label()
.
23.3.2. Skapa nya egenskaper
En egenskap definieras med ett namn och en typ. Även om Python i sig är dynamiskt typat så kan du inte ändra typen för en egenskap efter att den definierats. En egenskap kan skapas med 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)
Egenskaper kan också vara skrivskyddade om du vill att några egenskaper ska vara läsbara men inte skrivbara. För att göra detta kan du lägga till några flaggor till egenskapsdefinitionen för att styra läs/skriv-åtkomst. Flaggor är GObject.ParamFlags.READABLE
(endast läsåtkomst för extern kod), GObject.ParamFlags.WRITABLE
(endast skrivåtkomst), GObject.ParamFlags.READWRITE
(öppen):
foo = GObject.Property(type=str, flags = GObject.ParamFlags.READABLE) # not writable
bar = GObject.Property(type=str, flags = GObject.ParamFlags.WRITABLE) # not readable
Du kan också definiera nya skrivskyddade egenskaper med en ny metod dekorerad med 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.'
Du kan få denna egenskap med:
my_object = MyObject()
print(my_object.readonly)
print(my_object.get_property("readonly"))
API:t för GObject.Property
liknar den inbyggda property()
. Du kan skapa egenskapsinställare på ett sätt liknande Python-egenskaper:
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
Det finns också ett sätt att definiera minsta och största värden för tal, med en mer utförlig form:
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)
Egenskaper måste definieras i GObject.GObject.__gproperties__
, en ordbok, och hanteras i do_get_property och do_set_property.
23.3.3. Bevaka egenskaper
När en egenskap ändras sänds en signal ut, vars namn är ”notify::egenskapsnamn”:
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
Observera att du måste använda det kanoniska egenskapsnamnet då du ansluter till notify-signalerna, som förklaras i GObject.Object.signals.notify()
. För en Python-egenskap foo_bar_baz skulle du exempelvis ansluta till signalen notify::foo-bar-baz med
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)
Erhåller ett egenskapsvärde.
- set_property(property_name, value)
Ställer in egenskapen property_name till value.
- emit(signal_name, ...)
Sänd ut signalen signal_name. Signalargument måste följa, t.ex. om din signal är av typen
(int,)
så måste den sändas ut med:self.emit(signal_name, 42)
- freeze_notify()
Denna metod fryser alla ”notify::”-signalerna (som sänds ut då någon egenskap ändras) till metoden
thaw_notify()
anropas.Det rekommenderas att använda with-satsen vid anrop av
freeze_notify()
, på det viset säkerställs attthaw_notify()
anropas implicit i slutet på blocket:with an_object.freeze_notify(): # Do your work here ...
- thaw_notify()
Tina alla ”notify::”-signalerna som frystes av
freeze_notify()
.Det rekommenderas att inte anropa
thaw_notify()
explicit utan anropafreeze_notify()
tillsammans med with-satsen.
- handler_block(handler_id)
Blockerar en hanterare av en instans så att den inte kommer att anropas under några signalutsändningar om inte
handler_unblock()
anropas för detta handler_id. Att ”blockera” en signalhanterare betyder därmed att tillfälligt inaktivera den, en signalhanterare behöver avblockeras exakt det antal gånger som den har blockerats innan den blir aktiv igen.Det rekommenderas att använda
handler_block()
tillsammans med with-satsen som kommer anropahandler_unblock()
implicit i slutet på blocket:with an_object.handler_block(handler_id): # Do your work here ...
- handler_unblock(handler_id)
Gör effekten av
handler_block()
ogjord. En blockerad hanterare hoppas över under signalutsändningar och kommer inte anropas förrän den har avblockerats exakt det antal gånger som den tidigare blockerats.Det rekommenderas att inte anropa
handler_unblock()
explicit utan anropahandler_block()
tillsammans med with-satsen.
- __gsignals__
En ordbok där en ärvd klass kan definiera nya signaler.
Varje element i ordboken är en ny signal. Nyckeln är signalnamnet. Värdet är en tupel med formen:
(GObject.SIGNAL_RUN_FIRST, None, (int,))
GObject.SIGNAL_RUN_FIRST
kan ersättas medGObject.SIGNAL_RUN_LAST
ellerGObject.SIGNAL_RUN_CLEANUP
.None
är returtypen för signalen.(int,)
är parametertupeln för signalen.
- __gproperties__
Ordboken
__gproperties__
är en klassegenskap där du definierar ditt objekts egenskaper. Detta är inte det rekommenderade sättet att skapa nya egenskaper, metoden ovan är mycket kortare. Fördelarna med denna metod är att en egenskap kan definieras med fler inställningar, som minsta eller största värde för tal.Nyckeln är namnet på egenskapen
Värdet är en tupel som beskriver egenskapen. Antalet element i denna tupel beror på dess första element, men tupeln kommer alltid innehålla åtminstone följande objekt:
Det första elementet är egenskapens typ (t.ex.
int
,float
…).Det andra elementet är egenskapens smeknamn, vilket är en sträng med en kort beskrivning av egenskapen. Detta används vanligen av program med starka introspektionsförmågor, som den grafiska användargränssnittsbyggaren Glade.
Det tredje är egenskapens beskrivning eller blurb, vilket är en annan sträng med en längre beskrivning av egenskapen. Används också av Glade och liknande program.
Det sista (vilket inte nödvändigtvis är det fjärde som vi kommer se senare) är egenskapens flaggor:
GObject.PARAM_READABLE
,GObject.PARAM_WRITABLE
,GObject.PARAM_READWRITE
.Den absoluta längden på tupeln beror på egenskapstypen (tupelns första element). Därmed har vi följande situationer:
Om typen är
bool
ellerstr
så är det fjärde elementet standardvärdet för egenskapen.Om typen är
int
ellerfloat
så är det fjärde elementet det minsta accepterade värdet, det femte elementet det största accepterade värdet, och det sjätte elementet är standardvärdet.Om typen inte är någon av dessa så finns det inget extra element.
- GObject.SIGNAL_RUN_FIRST
Anropa objektets metodhanterare i det första utsändningssteget.
- GObject.SIGNAL_RUN_LAST
Anropa objektets metodhanterare i det tredje utsändningssteget.
- GObject.SIGNAL_RUN_CLEANUP
Anropa objektets metodhanterare i det sista utsändningssteget.
- GObject.ParamFlags.READABLE
Egenskapen är läsbar.
- GObject.ParamFlags.WRITABLE
Egenskapen är skrivbar.
- GObject.ParamFlags.READWRITE
Egenskapen är läsbar och skrivbar.