romgrk / node-gtk

GTK+ bindings for NodeJS (via GObject introspection)
MIT License
494 stars 41 forks source link

Class struct methods are not exposed #283

Open ju1ius opened 3 years ago

ju1ius commented 3 years ago

Hi,

It seems the gst_elementclass* methods are missing (i.e. gst_element_class_add_pad_template):

const gi = require('node-gtk')
const Gst = gi.require('Gst', '1.0')
Gst.init()

console.log(Gst.Element.addPadTemplate) // undefined
console.log(Gst.Element.prototype.addPadTemplate) // undefined
console.log(Gst.ElementClass) // undefined

Btw, congrats for this ambitious project ! 👍🏻

romgrk commented 3 years ago

From what I can see in the GIR file, GstElementClass is a gtype struct that isn't normally meant to be exposed in bindings (IIUC). We're currently filtering those out here:

https://github.com/romgrk/node-gtk/blob/d68a407f2311975e982ddbc938f3266495a5b7e6/lib/bootstrap.js#L402

g_struct_info_is_gtype_struct ()

gboolean
g_struct_info_is_gtype_struct (GIStructInfo *info);
Return true if this structure represents the "class structure" for some GObject or GInterface. This function is mainly useful to hide this kind of structure from generated public APIs.

Can you give an example use-case that you have for them? I'd prefer not to expose them, but if it's absolutely necessary so be it.

ju1ius commented 3 years ago

Can you give an example use-case that you have for them?

For example gst_element_class_add_pad_template is required to implement custom Gstreamer bins with dynamic proxy pads... More generally I believe these kinds of methods are required to implement custom Gstreamer plugins in the host language. Theses methods are available in the python bindings. In particular, gst_element_class_add_pad_template is made available both as an instance method:

from gi.repository import Gst

class MyBin(Gst.Bin):
    def __init__(self):
        super().__init__(self)
        self._element = Gst.ElementFactory.make('someelement')
        self.add(self._element)
        tpl = Gst.PadTemplate.new(
            'audio_%u',
            Gst.PadDirection.SRC,
            Gst.PadPresence.REQUEST,
            Gst.Caps.from_string('audio/x-raw'),
        )
        self.add_pad_template(tpl)

    def do_request_new_pad(self, tpl: Gst.PadTemplate, name: str, caps: Gst.Caps):
        target = self._element.get_request_pad('src_%u')
        ghost_pad: Gst.GhostPad = Gst.GhostPad.new_from_template(name, target, tpl)
        self.add_pad(ghost_pad)
        return ghost_pad

and through the __gsttemplates__ class field mecanism:

from gi.repository import Gst

class MyBin(Gst.Bin):
    __gsttemplates__ = (
        Gst.PadTemplate.new(
            'audio_%u',
            Gst.PadDirection.SRC,
            Gst.PadPresence.REQUEST,
            Gst.Caps.from_string('audio/x-raw'),
        ),
    )

    def __init__(self):
        super().__init__(self)
        self._element = Gst.ElementFactory.make('someelement')
        self.add(self._element)

    def do_request_new_pad(self, tpl: Gst.PadTemplate, name: str, caps: Gst.Caps):
        target = self._element.get_request_pad('src_%u')
        ghost_pad: Gst.GhostPad = Gst.GhostPad.new_from_template(name, target, tpl)
        self.add_pad(ghost_pad)
        return ghost_pad
romgrk commented 3 years ago

Oh ok, pygobject approach seems to be more involved than I would like it to be, they probably have some custom code just for GST :/

Do you happen to have a minimal example in C showing what you're trying to do? I can work from that and add the missing parts. Maybe just removing the if-guard will work but I'm not sure.

ju1ius commented 3 years ago

Oh ok, pygobject approach seems to be more involved than I would like it to be, they probably have some custom code just for GST :/

No, the Gst.Element.add_pad_template() method is exposed automatically by pygobject, and it seems to be the standard behavior since it is also exposed by Vala...

The __gsttemplates__ mechanism is an override added by the gst-python package (reference).

ju1ius commented 3 years ago

So, I just wrote script to inspect the class struct methods, and it appears they are all exposed in vala. Here's the output for the Gtk 3.0 module:

CellAreaClass

method: find_cell_property symbol: gtk_cell_area_class_find_cell_property exposed by node-gtk: no

method: install_cell_property symbol: gtk_cell_area_class_install_cell_property exposed by node-gtk: no

method: list_cell_properties symbol: gtk_cell_area_class_list_cell_properties exposed by node-gtk: no


CellRendererClass

method: set_accessible_type symbol: gtk_cell_renderer_class_set_accessible_type exposed by node-gtk: no


ContainerClass

method: find_child_property symbol: gtk_container_class_find_child_property exposed by node-gtk: no

method: handle_border_width symbol: gtk_container_class_handle_border_width exposed by node-gtk: no

method: install_child_properties symbol: gtk_container_class_install_child_properties exposed by node-gtk: no

method: install_child_property symbol: gtk_container_class_install_child_property exposed by node-gtk: no

method: list_child_properties symbol: gtk_container_class_list_child_properties exposed by node-gtk: no


WidgetClass

method: bind_template_callback_full symbol: gtk_widget_class_bind_template_callback_full exposed by node-gtk: no

method: bind_template_child_full symbol: gtk_widget_class_bind_template_child_full exposed by node-gtk: no

method: find_style_property symbol: gtk_widget_class_find_style_property exposed by node-gtk: no

method: get_css_name symbol: gtk_widget_class_get_css_name exposed by node-gtk: no

method: install_style_property symbol: gtk_widget_class_install_style_property exposed by node-gtk: no

method: list_style_properties symbol: gtk_widget_class_list_style_properties exposed by node-gtk: no

method: set_accessible_role symbol: gtk_widget_class_set_accessible_role exposed by node-gtk: no

method: set_accessible_type symbol: gtk_widget_class_set_accessible_type exposed by node-gtk: no

method: set_connect_func symbol: gtk_widget_class_set_connect_func exposed by node-gtk: no

method: set_css_name symbol: gtk_widget_class_set_css_name exposed by node-gtk: no

method: set_template symbol: gtk_widget_class_set_template exposed by node-gtk: no

method: set_template_from_resource symbol: gtk_widget_class_set_template_from_resource exposed by node-gtk: no

ju1ius commented 3 years ago

And here's the output for the Gst-1.0 module:

DeviceProviderClass

method: add_metadata symbol: gst_device_provider_class_add_metadata exposed by node-gtk: no

method: add_static_metadata symbol: gst_device_provider_class_add_static_metadata exposed by node-gtk: no

method: get_metadata symbol: gst_device_provider_class_get_metadata exposed by node-gtk: no

method: set_metadata symbol: gst_device_provider_class_set_metadata exposed by node-gtk: no

method: set_static_metadata symbol: gst_device_provider_class_set_static_metadata exposed by node-gtk: no


ElementClass

method: add_metadata symbol: gst_element_class_add_metadata exposed by node-gtk: no

method: add_pad_template symbol: gst_element_class_add_pad_template exposed by node-gtk: no

method: add_static_metadata symbol: gst_element_class_add_static_metadata exposed by node-gtk: no

method: add_static_pad_template symbol: gst_element_class_add_static_pad_template exposed by node-gtk: no

method: add_static_pad_template_with_gtype symbol: gst_element_class_add_static_pad_template_with_gtype exposed by node-gtk: no

method: get_metadata symbol: gst_element_class_get_metadata exposed by node-gtk: no

method: get_pad_template symbol: gst_element_class_get_pad_template exposed by node-gtk: no

method: get_pad_template_list symbol: gst_element_class_get_pad_template_list exposed by node-gtk: no

method: set_metadata symbol: gst_element_class_set_metadata exposed by node-gtk: no

method: set_static_metadata symbol: gst_element_class_set_static_metadata exposed by node-gtk: no

ju1ius commented 3 years ago

Output for the GObject 2.0 module:

ObjectClass

method: find_property symbol: g_object_class_find_property exposed by node-gtk: no

method: install_properties symbol: g_object_class_install_properties exposed by node-gtk: no

method: install_property symbol: g_object_class_install_property exposed by node-gtk: no

method: list_properties symbol: g_object_class_list_properties exposed by node-gtk: no

method: override_property symbol: g_object_class_override_property exposed by node-gtk: no

ju1ius commented 3 years ago

Using PyGObject:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

btn = Gtk.Button()
print(btn.list_properties())
[<GParamString 'action-name'>,
 <GParamVariant 'action-target'>,
 <GParamObject 'related-action'>,
 <GParamBoolean 'use-action-appearance'>,
 <GParamString 'name'>,
 <GParamObject 'parent'>,
 <GParamInt 'width-request'>,
 <GParamInt 'height-request'>,
 <GParamBoolean 'visible'>,
 <GParamBoolean 'sensitive'>,
 <GParamBoolean 'app-paintable'>,
 <GParamBoolean 'can-focus'>,
 <GParamBoolean 'has-focus'>,
 <GParamBoolean 'is-focus'>,
 <GParamBoolean 'focus-on-click'>,
 <GParamBoolean 'can-default'>,
 <GParamBoolean 'has-default'>,
 <GParamBoolean 'receives-default'>,
 <GParamBoolean 'composite-child'>,
 <GParamObject 'style'>,
 <GParamFlags 'events'>,
 <GParamBoolean 'no-show-all'>,
 <GParamBoolean 'has-tooltip'>,
 <GParamString 'tooltip-markup'>,
 <GParamString 'tooltip-text'>,
 <GParamObject 'window'>,
 <GParamDouble 'opacity'>,
 <GParamBoolean 'double-buffered'>,
 <GParamEnum 'halign'>,
 <GParamEnum 'valign'>,
 <GParamInt 'margin-left'>,
 <GParamInt 'margin-right'>,
 <GParamInt 'margin-start'>,
 <GParamInt 'margin-end'>,
 <GParamInt 'margin-top'>,
 <GParamInt 'margin-bottom'>,
 <GParamInt 'margin'>,
 <GParamBoolean 'hexpand'>,
 <GParamBoolean 'vexpand'>,
 <GParamBoolean 'hexpand-set'>,
 <GParamBoolean 'vexpand-set'>,
 <GParamBoolean 'expand'>,
 <GParamInt 'scale-factor'>,
 <GParamUInt 'border-width'>,
 <GParamEnum 'resize-mode'>,
 <GParamObject 'child'>,
 <GParamString 'label'>,
 <GParamObject 'image'>,
 <GParamEnum 'relief'>,
 <GParamBoolean 'use-underline'>,
 <GParamBoolean 'use-stock'>,
 <GParamFloat 'xalign'>,
 <GParamFloat 'yalign'>,
 <GParamEnum 'image-position'>,
 <GParamBoolean 'always-show-image'>]

Using GJS:

imports.gi.versions.GObject = '2.0'
imports.gi.versions.Gtk = '3.0'
const GObject = imports.gi.GObject
const Gtk = imports.gi.Gtk

const btn = new Gtk.Button()
const propNames = GObject.Object.list_properties.call(btn).map(prop => prop.name)
print(propNames)
action-name,action-target,related-action,use-action-appearance,name,parent,width-request,height-request,visible,sensitive,app-paintable,can-focus,has-focus,is-focus,focus-on-click,can-default,has-default,receives-default,composite-child,style,events,no-show-all,has-tooltip,tooltip-markup,tooltip-text,window,opacity,double-buffered,halign,valign,margin-left,margin-right,margin-start,margin-end,margin-top,margin-bottom,margin,hexpand,vexpand,hexpand-set,vexpand-set,expand,scale-factor,border-width,resize-mode,child,label,image,relief,use-underline,use-stock,xalign,yalign,image-position,always-show-image

Using node-gtk:

const gi = require('node-gtk')
const Gtk = gi.require('Gtk', '3.0')

Gtk.init()
const btn = new Gtk.Button()
console.log(GObject.Object.listProperties.call(btn)) // Uncaught TypeError: GObject.Object.listProperties is not a function
console.log(btn.listProperties()) // Uncaught TypeError: btn.listProperties is not a function
ju1ius commented 3 years ago

a gtype struct that isn't normally meant to be exposed in bindings

In the light of the previous comments, I think you might want to reassess this statement (and thus maybe reopen https://github.com/romgrk/node-gtk/issues/100 ?). It seems to me that, even if the class structs themselves are not publicly exposed, at least their methods should be made available on the corresponding class constructor or prototype. For the class struct fields, I don't know but it might be worth to investigate.

chfritz commented 1 month ago

I'd be very interested in access to g_object_class_list_properties as well. My use-case is like gst-inspect (see this line): I'm working with gstreamer and need to check which properties a specific gst element has. In different versions of gstreamer, these elements expose different properties. So in order to avoid an exception when trying to build a gstreamer pipeline with that element that uses a property that doesn't exist, I want to first check. I can probably work around it for now by using getProperty(NAME) !== undefined but that's kludgy and makes discovery very awkward.

romgrk commented 1 month ago

I found GObject.Object.interfaceListProperties but g_object_class_list_properties doesn't seem to be bound, not sure why. We might be missing a type of functions to add, it's listed under "class methods" in the docs, unlike g_object_interface_list_properties which is listed under "functions". I tried looking into it a bit but I don't have enough time at the moment, I might need to add a note that the repository doesn't receive regular maintenance.

I don't think it would be very hard to fix, the entry point to setup GIR entries is here if someone wants to have a go at it.