*.pyd
*.dll.a
.coverage
+poetry.lock
\ No newline at end of file
+3.42.0 - 2021-09-19
+-------------------
+
+* meson: Bump minimum meson_version to 0.47.0
+* Expose GObject.Object.run_dispose() :issue:`470`
+* docs: document Gtk.Template. :issue:`396`
+* dev: Add poetry support
+* meson: use main branch for glib subproject
+* Fix some small memory leaks :mr:`178`
+
+
3.40.1 - 2021-03-30
-------------------
Installing from PyPI with pip:
#) Open a terminal and enter your virtual environment
- #) Execute ``sudo dnf install gcc gobject-introspection-devel cairo-devel pkg-config python3-devel gtk3``
+ #) Execute ``sudo dnf install gcc gobject-introspection-devel cairo-gobject-devel pkg-config python3-devel gtk3``
to install the build dependencies and GTK
#) Execute ``pip3 install pycairo`` to build and install Pycairo
#) Execute ``pip3 install PyGObject`` to build and install PyGObject
--- /dev/null
+============
+Gtk.Template
+============
+
+A GtkWidget subclass can use a
+`GtkBuilder UI Definition <https://developer.gnome.org/gtk3/stable/GtkBuilder.html#BUILDER-UI>`__
+XML document as a template to create child widgets and set its own
+properties, without creating a GtkBuilder instance. This is implemented
+for Python by PyGObject with Gtk.Template.
+
+The subclass uses a ``@Gtk.Template`` decorator and declares a class
+variable ``__gtype_name__`` with the value of the XML ``template``
+element ``class`` attribute.
+
+Child widgets are declared, typically with the same names as the XML
+``object`` element ``id`` attributes, at the class level as instances
+of ``Gtk.Template.Child``.
+
+Signal handler methods, typically with the same names as the XML ``signal``
+element ``handler`` attributes, use the ``@Gtk.Template.Callback`` decorator.
+
+``Gtk.Template()`` takes a mandatory keyword argument passing the XML document
+or its location, either ``string``, ``filename`` or ``resource_path``.
+
+``Gtk.Template.Child()`` and ``Gtk.Template.Callback()`` optionally take
+a ``name`` argument matching the value of the respective XML attribute,
+in which case the Python attribute can have a different name.
+
+Examples
+--------
+
+.. code-block:: python
+
+ xml = """\
+ <interface>
+ <template class="example1" parent="GtkBox">
+ <child>
+ <object class="GtkButton" id="hello_button">
+ <property name="label">Hello World</property>
+ <signal name="clicked" handler="hello_button_clicked" swapped="no" />
+ </object>
+ </child>
+ </template>
+ </interface>
+ """
+
+ @Gtk.Template(string=xml)
+ class Foo(Gtk.Box):
+ __gtype_name__ = "example1"
+
+ hello_button = Gtk.Template.Child()
+
+ @Gtk.Template.Callback()
+ def hello_button_clicked(self, *args):
+ pass
+
+Python attribute names that are different to the XML values:
+
+.. code-block:: python
+
+ @Gtk.Template(string=xml)
+ class Foo(Gtk.Box):
+ __gtype_name__ = "example1"
+
+ my_button = Gtk.Template.Child("hello_button")
+
+ @Gtk.Template.Callback("hello_button_clicked")
+ def bar(self, *args):
+ pass
+
+
+To add widgets to the built-in child of a parent, describe the built-in widget
+in the XML with its ``child`` element having an ``internal-child`` attribute set
+to the name of the built-in widget:
+
+.. code-block:: XML
+
+ <interface>
+ <template class="example2" parent="GtkDialog">
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <child>
+ <object class="GtkButton" id="hello_button">
+ <property name="label">Hello World</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ </interface>
+
+
+Subclasses that declare ``__gtype_name__`` can be used as objects in the XML:
+
+.. code-block:: python
+
+ xml = """\
+ <interface>
+ <template class="example3" parent="GtkBox">
+ <child>
+ <object class="ExampleButton" id="hello_button">
+ <property name="label">Hello World</property>
+ <signal name="clicked" handler="hello_button_clicked" swapped="no" />
+ </object>
+ </child>
+ </template>
+ </interface>
+ """
+
+
+ class HelloButton(Gtk.Button):
+ __gtype_name__ = "ExampleButton"
+
+
+ @Gtk.Template(string=xml)
+ class Foo(Gtk.Box):
+ __gtype_name__ = "example3"
+
+ hello_button = Gtk.Template.Child()
+
+ @Gtk.Template.Callback()
+ def hello_button_clicked(self, *args):
+ pass
api/index
cairo_integration
+ gtk_template
threading
debug_profile
deploy
* `Pithos <https://pithos.github.io/>`__ - a Pandora Radio client
* `Pitivi <http://www.pitivi.org/>`__ - a free and open source video editor
* `Quod Libet <https://quodlibet.readthedocs.io/>`__ - a music library manager / player
-* `Terminator <https://gnome-terminator.org/>` -- The Robot Future of Terminals
+* `Terminator <https://gnome-terminator.org/>`__ - The Robot Future of Terminals
* `Transmageddon <http://www.linuxrising.org/>`__ - a video transcoder
interface_install_property = _unsupported_method
interface_list_properties = _unsupported_method
notify_by_pspec = _unsupported_method
- run_dispose = _unsupported_method
watch_closure = _unsupported_method
# Make all reference management methods private but still accessible.
tmp = generate_repr(self->gtype, (guint)PyLong_AsUnsignedLongMask ((PyObject*)self));
module = PyObject_GetAttrString ((PyObject *)self, "__module__");
- if (module == NULL)
+ if (module == NULL) {
+ g_free (tmp);
return NULL;
+ }
if (!PyUnicode_Check (module)) {
+ g_free (tmp);
Py_DECREF (module);
return NULL;
}
object_type = (PyTypeObject *) PyObject_Type (object);
if (object_type == NULL) {
+ g_free (type_name_expected);
return -1;
}
project('pygobject', 'c',
- version : '3.40.1',
- meson_version : '>= 0.46.0',
+ version : '3.42.0',
+ meson_version : '>= 0.47.0',
default_options : [ 'warning_level=1',
'buildtype=debugoptimized'])
+[tool.poetry]
+name = "PyGObject"
+version = "3.42.0"
+description = "Python bindings for GObject Introspection"
+authors = ["Christoph Reiter"]
+
+[tool.poetry.dependencies]
+python = "^3.6"
+pycairo = "^1.16"
+
+[tool.poetry.dev-dependencies]
+pytest = "^6.0.0"
+flake8 = "^3.9.1"
+Sphinx = "^3.5.4"
+sphinx-rtd-theme = "^0.5.2"
+coverage = "^5.5"
+
[build-system]
requires = ["setuptools", "wheel", "pycairo"]
from distutils.spawn import find_executable
-PYGOBJECT_VERSION = "3.40.1"
+PYGOBJECT_VERSION = "3.42.0"
GLIB_VERSION_REQUIRED = "2.56.0"
GI_VERSION_REQUIRED = "1.56.0"
PYCAIRO_VERSION_REQUIRED = "1.16.0"
directory=glib
url=https://gitlab.gnome.org/GNOME/glib.git
push-url=git@gitlab.gnome.org:GNOME/glib.git
-revision=master
+revision=main
+depth=1
url=https://gitlab.gnome.org/GNOME/gobject-introspection.git
push-url=git@gitlab.gnome.org:GNOME/gobject-introspection.git
revision=master
+depth=1
directory=libffi
url=https://github.com/centricular/libffi.git
revision=meson
+depth=1
directory=pycairo
url=https://github.com/pygobject/pycairo.git
revision=pycairo-1.18
+depth=1
class TestGObjectAPI(unittest.TestCase):
+ def test_run_dispose(self):
+ class TestObject(GObject.GObject):
+ int_prop = GObject.Property(default=0, type=int)
+
+ obj = TestObject()
+ called = []
+
+ def on_notify(*args):
+ called.append(args)
+
+ obj.connect('notify::int-prop', on_notify)
+ obj.notify("int-prop")
+ obj.notify("int-prop")
+ # after this everything should be disconnected
+ obj.run_dispose()
+ obj.notify("int-prop")
+ obj.notify("int-prop")
+ assert len(called) == 2
+
def test_call_method_uninitialized_instance(self):
obj = GObject.Object.__new__(GObject.Object)
with self.assertRaisesRegex(RuntimeError, '.*is not initialized'):
static GValue *
test_gvalue_callback (GObject *object, const GValue *v)
{
- GValue *ret = g_malloc0 (sizeof (GValue));
+ GValue *ret;
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
g_return_val_if_fail (G_IS_VALUE (v), NULL);
+ ret = g_malloc0 (sizeof (GValue));
g_value_init (ret, G_VALUE_TYPE (v));
g_value_copy (v, ret);
return ret;
static GValue *
test_gvalue_ret_callback (GObject *object, GType type)
{
- GValue *ret = g_malloc0 (sizeof (GValue));
+ GValue *ret;
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
+ ret = g_malloc0 (sizeof (GValue));
g_value_init (ret, type);
switch (type) {