0

I'm writing an application in python+gtk3 with pygobject 3.24

I'm developing custom widgets and need information about the call stack and return values of do_get_preferred_width, do_size_allocate, etc. in order to debug layout issues I'm having.

I'm trying to monkey patch these methods for regular Gtk Widgets like Gtk.Box, Gtk.Paned, etc. The new methods will just call the original methods but print their input/output to a log file so I can follow the layout logic.

I can accomplish this by creating subclasses for each of these, but since my application uses .ui files which rely on the original classes, I can't install these patched classes and still use the templates (pygobject doesn't support derived widgets in templates). Recreating the template logic within the application requires a lot of work.

Ideally, I could just monkey patch the methods on the fly like so:

builder = Gtk.Builder.new_from_file('my-template.ui')
box = builder.get_object('my-box')
box.do_get_preferred_width = patched_do_get_preferred_width

But this seems to have no effect. If I try to set it at the class level, it also doesn't work:

Gtk.Box.do_get_preferred_width = patched_do_get_preferred_width

The runtime seems to be completely unaware of these changes. I suspect it's due to the fact that the underlying calls are all in C. At this point I'm tempted to insert logging messages into the c source code and build it myself, but this feels like overkill.

Does anyone know how I can accomplish this monkey patching? Or easily obtain the information I need? (The GTK debugger isn't informative enough)

sicklybeans
  • 299
  • 3
  • 11

1 Answers1

0

Do you need this in production on for debugging purposes only? If the latter is the case, you can subclass from specific Gtk widgets and override the methods you are interested in. Try this MCVE:

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

class Box(Gtk.Box):
    def __init__(self, *args):
        super().__init__(self, *args)

    def get_allocated_width(self):
        val = super().get_allocated_width()
        print("val: ", val)
        return val

def clicked(button, box):
    print (box.get_allocated_width())

Gtk.Box = Box # make following calls to Gtk.Box return overriden version
win = Gtk.Window()
box = Gtk.Box()
win.add(box)
button = Gtk.Button("foo")
button.connect("clicked", clicked, box)
box.add(button)
win.show_all()
Gtk.main()

And if you need bulk patching, you can use this technique.

Alexander Dmitriev
  • 2,483
  • 1
  • 15
  • 20
  • Thanks for the response, but this is not quite what I'm looking for. I know how to subclass and override those methods. My problem is that my application heavily uses .ui templates and therefore I am not free to replace Gtk.Box with my own class since its constructed by the template builder. Also that bulk patching technique does not work here – sicklybeans Dec 07 '19 at 01:31
  • @sicklybeans .ui files are loaded with GtkBuilder which knows nothing about your python code. If it were C, you could patch methods like [this](https://gist.github.com/fxff/6bdec8d20b303cbce05983d33f72dbde) and it will apply to all widgets of this type. I doubt it's possible to patch C from python. – Alexander Dmitriev Dec 09 '19 at 09:59