Pygobject / Gtk 在小部件上绘制

Pygobject / Gtk draw on top of widgets

在 pygobject(或 Gtk)中是否可以在 Gtk.Container 小部件(例如 VBox)的 children 小部件之上绘制?

我知道我可以连接到任何自定义绘图小部件的 'draw' 信号。但是回调是在正常的widget绘制之前调用的。

所以任何“自定义”绘图都留在容器的 children 后面,除非回调函数连接到绘图信号 return True,在这种情况下信号不会传播并且容器不绘制其 children;但这不是我想要的。

我需要在 容器 绘制其 children 之后绘制

我知道除了响应绘图信号外,我还可以重写子class 中的do_draw 方法,但容器又不会绘制其children。我必须调用 parent class 绘图方法,但我不知道如何调用。

我尝试调用 super().do_draw 和 super().draw() 但出现堆栈溢出错误,这意味着我的 do_draw 函数正在调用自身。

你知道一些解决办法吗?

python GObject 中的方法名称如 do_something 被视为基本 C classes/interfaces 之一中相应的 something 虚函数的实现。 Metaclass wizadry/hackery is used to implement this virtual function registration whenever you subclass GObject.Object. I believe you want to know how to chain up 到 PyGObject 中父虚函数的实现。

我的猜测是,当您调用 super().draw() 时,您只是 calling a wrapper draw() 函数,然后会将调用转发给子 class 的虚拟函数实现。

当您访问 super().do_draw 时,您正在直接检索 VFuncInfo. This is a callable wrapper object that will invoke the appropriate virtual function for you. It uses the owner argument from __get__() (descriptor protocol) to determine which child class implementation to use when you __call__(). As such, super().do_draw() will also again refer to your subclass's implementation. You can manually specify the class whose vfunc implementation you actually want by calling VFuncInfo.invoke(),例如

super().do_draw.invoke(Gtk.VBox, self, *args, **kwargs)

没有什么可以阻止您使用您未继承的 class,尽管可能会发生不好的事情。

或者您可以通过父 class 访问 do_draw,以便根据需要分配描述符协议中的所有者参数,您只需 __call__():

Gtk.VBox.do_draw(self, *args, **kwargs)

演示:

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

class MyVBox(Gtk.VBox):
    def do_draw(self, cr: cairo.Context) -> None:
        """Overlay a red rectangle in the top-left corner."""
        Gtk.VBox.do_draw(self, cr)
        # If you don't want to hard-code the parent class:
        #  __class__.mro()[1].do_draw(self, cr)
        cr.rectangle(10, 10, 64, 64)
        cr.set_source_rgba(1., 0., 0.)
        cr.fill()

window = Gtk.Window()
vbox = MyVBox()
window.add(vbox)

button = Gtk.Button(label="Button")
label = Gtk.Label(label="^^^ Click me ^^^")
vbox.add(button)
vbox.add(label)

window.show_all()

button.connect('clicked', lambda *_: print("Button clicked"))
window.connect('delete-event', Gtk.main_quit)
Gtk.main()

我不知道这是否是在小部件上绘制的最佳方式。