我尝试在 Ubuntu 20.04 上安装libgoocanvas-2.0-dev并将 GooCanvas的内省 XML 文件安装为/usr/share/gir-1.0/GooCanvas-2.0.gir
. 在这个文件中,我看到了绑定的定义GooCanvas2::CanvasRect->new()
:
<class name="CanvasRect"
c:symbol-prefix="canvas_rect"
c:type="GooCanvasRect"
parent="CanvasItemSimple"
glib:type-name="GooCanvasRect"
glib:get-type="goo_canvas_rect_get_type"
glib:type-struct="CanvasRectClass">
<implements name="CanvasItem"/>
<function name="new"
c:identifier="goo_canvas_rect_new"
introspectable="0">
<return-value transfer-ownership="full">
<type name="CanvasItem" c:type="GooCanvasItem*"/>
</return-value>
<parameters>
<parameter name="parent" transfer-ownership="none" skip="1">
<type name="CanvasItem" c:type="GooCanvasItem*"/>
</parameter>
<parameter name="x" transfer-ownership="none">
<type name="gdouble" c:type="gdouble"/>
</parameter>
<parameter name="y" transfer-ownership="none">
<type name="gdouble" c:type="gdouble"/>
</parameter>
<parameter name="width" transfer-ownership="none">
<type name="gdouble" c:type="gdouble"/>
</parameter>
<parameter name="height" transfer-ownership="none">
<type name="gdouble" c:type="gdouble"/>
</parameter>
<parameter name="..." transfer-ownership="none">
<varargs/>
</parameter>
</parameters>
</function>
我注意到fill-pattern
参数的定义可能包含在最后一个name="..."
参数包中。所以不清楚参数应该是什么类型。但在文件的后面可能有一个线索,在第 244 行,我们有:
<glib:boxed glib:name="CairoPattern"
c:symbol-prefix="cairo_pattern"
glib:type-name="GooCairoPattern"
glib:get-type="goo_cairo_pattern_get_type">
</glib:boxed>
我不确定究竟是什么glib:boxed
意思,但根据Glib::Object::Introspection的文档:
类、接口以及盒装和基本类型在某种程度上都有自己的命名空间,因为 GType 的概念在 Perl 绑定中完全被 Perl 包名取代。
所以boxed
类型在 Perl 中有自己的命名空间。更多关于盒装类型在这里。
错误信息:
Cairo::SolidPattern=SCALAR(0x55a027382d18) is not of type GooCanvas2::CairoPattern ...
表示它需要一个GooCanvas2::CairoPattern
类型,但是当我尝试使用这种类型时,例如
my $pattern = GooCanvas2::CairoPattern->new();
我收到错误消息:
Could not fetch information for package GooCanvas2::CairoPattern; perhaps it has not been loaded via Glib::Object::Introspection?
更新:
以下 C 程序运行良好,并显示模式应该是类型cairo_pattern_t
:
#include <gtk/gtk.h>
#include <goocanvas.h>
static gboolean
on_delete_event (GtkWidget *window,
GdkEvent *event,
gpointer unused_data)
{
exit (0);
}
int main(int argc, char *argv[]) {
gtk_init (&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 640, 600);
g_signal_connect (window, "delete_event", G_CALLBACK (on_delete_event), NULL);
GtkWidget *scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), scrolled_win);
GtkWidget *canvas = goo_canvas_new();
goo_canvas_set_bounds (GOO_CANVAS (canvas), 0, 0, 1000, 1000);
GooCanvasItem *root = goo_canvas_get_root_item (GOO_CANVAS (canvas));
cairo_pattern_t *pattern = cairo_pattern_create_rgba( 0.0, 0.0, 1.0, 0.5 );
GooCanvasItem *rect = goo_canvas_rect_new(
root, 0, 0, 100, 100,
"line-width", 1.0,
"stroke-color", "black",
"fill-pattern", pattern,
NULL
);
gtk_widget_set_size_request (canvas, 600, 450);
gtk_container_add (GTK_CONTAINER (scrolled_win), canvas);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
更新 2:
更多调试信息:调用GooCanvas2::CanvasRect->new()
被重定向到第 1327行GObject.xs
:
SV *
g_object_new (class, ...)
// ...
在第 1359 行,它找到了属性参数的预期类型fill-pattern
。看起来这是类型G_TYPE_BOXED。然后在第 1378 行
gperl_value_from_sv (¶ms[i].value, ST (FIRST_ARG+i*2+1));
它尝试从提供的 Perl 参数(类型Cairo::SolidPattern
)中检索装箱值,这导致第 136 行
case G_TYPE_BOXED:
/* SVs need special treatment! */
if (G_VALUE_HOLDS (value, GPERL_TYPE_SV)) {
g_value_set_boxed (value,
gperl_sv_is_defined (sv)
? sv : NULL);
} else {
g_value_set_static_boxed (
value,
gperl_get_boxed_check (
sv, G_VALUE_TYPE(value)));
}
第一次检查失败,所以它进入了第g_value_set_static_boxed()
一次调用的第二个选择gperl_get_boxed_check()
,见第 558行GBoxed.xs
。上线568
boxed_info = g_hash_table_lookup (info_by_gtype, (gpointer) gtype);
boxed_info
BoxedInfo *
与内容一样返回
{
gtype = 93825018188384,
package = 0x555556e4e7a0 "GooCanvas2::CairoPattern",
wrapper_class = 0x0
}
在第 575 行unwrap
设置为_default_wrapper_class.unwrap
第 583 行:
return (*unwrap) (gtype, boxed_info->package, sv);
default_boxed_unwrap()
在第 420 行调用:
在第 429 行发出嘶哑的声音:
if (!sv_derived_from (sv, package))
croak ("%s is not of type %s",
gperl_format_variable_for_output (sv),
package);
因为Cairo::SolidPattern
不是从GooCanvas2::CairoPattern
.
更新 3:
查看GooCanvas 的源代码,它为模式定义了几个 setter/getter:fill-color
, fill-color-rgba
, fill-color-gdk-rgba
, fill-pixbuf
, fill-pattern
,所有这些都在内部设置/获取相同的属性goo_canvas_style_fill_pattern_id
。并且其他设置者没有错误的内省包装器到cairo_pattern_t
. 因此这有效:
my $rect = GooCanvas2::CanvasRect->new(
...
'fill-color-gdk-rgba' => Gtk3::Gdk::RGBA::parse('red'),
);
此外,在$rect
构造之后,我们可以GooCanvas2::CairoPattern
通过$rect->get('fill-pattern')
.
这不允许使用从 cairo 创建的其他模式(线性等),但至少纯色通过提供任意 RGBA 起作用,并且 pixbuf setter 应该足以满足其他需求。
更新 4:
我写了GooCanvas2::CairoTypes来解决这个问题,至少对于 Patterns。
它用于gperl_register_boxed_synonym(CAIRO_GOBJECT_TYPE_PATTERN, GOO_TYPE_CAIRO_PATTERN);
letCairo::Pattern
用作GooCanvas::CairoPattern
,并另外提供了一个函数来显式转换GooCanvas::CairoPattern
为Cairo::Pattern
via gperl_new_boxed(gperl_get_boxed_check(input, GOO_TYPE_CAIRO_PATTERN), CAIRO_GOBJECT_TYPE_PATTERN, 0)
。