1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
.. currentmodule:: gi.repository
Basics
======
.. hint::
In this example, we will use GTK widgets to demonstrate GObject
capabilities.
GObject Initialization
----------------------
GObjects are initialized like any other Python class.
.. code:: python
label = Gtk.Label()
.. _basics-properties:
Properties
----------
GObject has a powerful properties system.
Properties describe the configuration and state of a gobject. Each gobject has
its own particular set of properties. For example, a GTK button has the property
``label`` which contains the text of the label widget inside the button.
You can specify the name and value of any number of properties as keyword
arguments when creating an instance of a gobject. To create a label aligned to
the right with the text "Hello World", use:
.. code:: python
label = Gtk.Label(label='Hello World', halign=Gtk.Align.END)
which is equivalent to
.. code:: python
label = Gtk.Label()
label.set_label('Hello World')
label.set_halign(Gtk.Align.END)
There are various ways of interacting with a gobject properties from Python, we
already have seen the two first ways, these are setting them on initialization
or using the getters and setters functions that the gobject might provide.
Other option is to use :class:`GObject.Object` builtin methods :meth:`GObject.Object.get_property`
and :meth:`GObject.Object.set_property`. Using these methods is more common when
you have created a :class:`GObject.Object` subclass and you don't have getters
and setters functions.
.. code:: python
label = Gtk.Label()
label.set_property('label', 'Hello World')
label.set_property('halign', Gtk.Align.END)
print(label.get_property('label'))
Instead of using getters and setters you can also get and set the gobject
properties through the ``props`` property such as ``label.props.label = 'Hello World'``.
This is equivalent to the more verbose methods that we saw before, and it's a
more pythonic way of interacting with properties.
To see which properties are available for a gobject you can ``dir`` the
``props`` property:
.. code:: python
widget = Gtk.Box()
print(dir(widget.props))
This will print to the console the list of properties that a :class:`Gtk.Box`
has.
Property Bindings
^^^^^^^^^^^^^^^^^
GObject provides a practical way to bind properties of two different gobjects.
This is done using the :meth:`GObject.Object.bind_property` method.
The behavior of this binding can be controlled by passing a
:class:`GObject.BindingFlags` of choice.
:attr:`GObject.BindingFlags.DEFAULT` will update the target property every time
the source property changes.
:attr:`GObject.BindingFlags.BIDIRECTIONAL` creates a bidirectional binding; if
either the property of the source or the property of the target changes, the
other is updated.
:attr:`GObject.BindingFlags.SYNC_CREATE` is similar to ``DEFAULT`` but it will
also synchronize the values of the source and target properties when creating
the binding.
:attr:`GObject.BindingFlags.INVERT_BOOLEAN` works only for boolean properties
and setting one property to ``True`` will result in the other being set to
``False`` and vice versa (this flag cannot be used when passing custom
transformation functions to :meth:`GObject.Object.bind_property`).
.. code:: python
entry = Gtk.Entry()
label = Gtk.Label()
entry.bind_property('text', label, 'label', GObject.BindingFlags.DEFAULT)
In this example **entry** is our source object and ``text`` the source property
to bind.
**label** is the target object and the namesake property ``label`` is the
target property.
Every time someone changes the ``text`` property of the entry the label
``label`` will be updated as well with the same value.
Property Bindings with Transformations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sometimes you may want to bind two properties that are incompatible, or you
simply need to apply some transformation between these values.
For these scenarios :meth:`GObject.Object.bind_property` also accepts custom
transformation functions that serve to this purpose.
The transformation functions take as first argument the :class:`GObject.Binding`
instance for this binding and as second argument the property value depending
on the direction of the transformation.
For ``transform_to`` this will be the value of the source property and for
``transform_from`` the value of the target property.
Each function should return the value to be set in the other object's property,
with the correct type.
In this example we'll do an ``int`` to ``bool`` type conversion between two
objects:
.. code:: python
def transform_to(_binding, value):
return bool(value) # Return int converted to a bool
def transform_from(_binding, value):
return int(value) # Return bool converted to a int
source.bind_property(
'int_prop',
target,
'bool_prop',
GObject.BindingFlags.BIDIRECTIONAL,
transform_to,
transform_from
)
.. _basics-signals:
Signals
-------
GObject signals are a system for registering callbacks for specific events.
A generic example is:
.. code:: python
handler_id = gobject.connect('event', callback, data)
Firstly, *gobject* is an instance of a gobject we created earlier. Next, the
event we are interested in. Each gobject has its own particular events which
can occur.
This means that when the gobject emits the event, the signal is issued.
Thirdly, the *callback* argument is the name of the callback function.
It contains the code which runs when signals of the specified type are issued.
Finally, the *data* argument includes any data which should be passed when the
signal is issued. However, this argument is completely optional and can be left
out if not required.
The function returns a number that identifies this particular signal-callback
pair.
It is required to disconnect from a signal such that the callback function will
not be called during any future or currently ongoing emissions of the signal it
has been connected to:
.. code:: python
gobject.disconnect(handler_id)
When creating the callback function for a signal, the arguments it accepts will
depend on the specific signal, but for a signal with no arguments it will look
like this:
.. code:: python
def on_event(gobject, data):
...
my_object.connect('event', on_event, data)
Where ``gobject`` is the object that triggered the signal and ``data`` is the
additional data that we previously passed to the :meth:`GObject.Object.connect`
method.
If the signal had arguments, they will come before the optional data argument.
The ``notify`` signal
^^^^^^^^^^^^^^^^^^^^^
When any of a GObject's properties change, it will emit the ``notify`` signal.
This is a "detailed" signal, meaning that you can listen to a subset of the
signal, in this case a specific property.
For example, you can connect to the signal in the form of
``notify::property-name``:
.. code:: python
def callback(label, _pspec):
print(f'The label prop changed to {label.props.label}')
label = Gtk.Label()
label.connect('notify::label', callback)
|