Cairo::SolidPattern 不是 GooCanvas2::CairoPattern 类型
Cairo::SolidPattern is not of type GooCanvas2::CairoPattern
我正在尝试将旧的 Gtk2 perl 脚本转换为 Gtk3。这是 Gtk2 版本的样子:
#!/usr/bin/perl
use 5.30.0;
use strict;
use warnings;
use diagnostics;
use Gtk2 '-init';
use Goo::Canvas;
my $canvas = Goo::Canvas->new;
my $pattern = Cairo::SolidPattern->create_rgba(0, 0, 0, 0);
$pattern = Goo::Cairo::Pattern->new($pattern);
my $rect = Goo::Canvas::Rect->new(
$canvas->get_root_item,
0, 0, 100, 100,
'fill-pattern' => $pattern,
'line-dash' => Goo::Canvas::LineDash->new([5, 5]),
'line-width' => 1,
'stroke-color' => 'black',
);
这是我的 Gtk3 尝试:
#!/usr/bin/perl
use 5.30.0;
use strict;
use warnings;
use diagnostics;
use Gtk3 '-init';
use GooCanvas2;
my $canvas = GooCanvas2::Canvas->new;
my $pattern = Cairo::SolidPattern->create_rgba(0, 0, 0, 0);
my $rect = GooCanvas2::CanvasRect->new(
parent => $canvas->get_root_item,
x => 0, y => 0, width => 100, height => 100,
'fill-pattern' => $pattern,
'line-dash' => GooCanvas2::CanvasLineDash->newv([5, 5]),
'line-width' => 1,
'stroke-color' => 'black',
);
但这会导致错误:
Uncaught exception from user code:
Cairo::SolidPattern=SCALAR(0x558de4acb260) is not of type GooCanvas2::CairoPattern at /usr/lib64/perl5/vendor_perl/5.30.1/x86_64-linux/Glib.pm line 222.
Glib::Object::_LazyLoader::AUTOLOAD("GooCanvas2::CanvasRect", "parent", GooCanvas2::CanvasGroup=HASH(0x558de4abe1a0), "x", 0, "y", 0, "width", ...) called at test.pl line 14
我也试过插入 $pattern = GooCanvas2::CairoPattern->new($pattern);
但这没有帮助:
Uncaught exception from user code:
Could not fetch information for package GooCanvas2::CairoPattern; perhaps it has not been loaded via Glib::Object::Introspection? at test.pl line 13.
但是查看 GooCanvas2.pm
的来源,它确实通过 Glib::Object::Introspection 加载 GooCanvas2
。
有什么解决方法的建议吗?
我尝试在 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 的文档:
Classes, interfaces and boxed and fundamental types get their own
namespaces, in a way, as the concept of the GType is completely
replaced in the Perl bindings by the Perl package name.
因此 boxed
类型在 Perl 中获得了它们自己的命名空间。更多关于盒装类型 here.
错误信息:
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()
被重定向到 GObject.xs
中的 line 1327:
SV *
g_object_new (class, ...)
// ...
在 line 1359 it finds the expected type for the property argument fill-pattern
. It looks like this is type G_TYPE_BOXED. Then at line 1378
gperl_value_from_sv (¶ms[i].value, ST (FIRST_ARG+i*2+1));
它尝试从提供的 Perl 参数(Cairo::SolidPattern
类型)中检索装箱值,这导致 line 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()
,参见 GBoxed.xs
中的 line 558。第 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);
在 line 420 调用 default_boxed_unwrap()
:
第 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's source,它为模式定义了几个setters/getters:fill-color
、fill-color-rgba
、fill-color-gdk-rgba
、fill-pixbuf
、fill-pattern
,所有这些在内部 set/get 相同 属性 goo_canvas_style_fill_pattern_id
。其他 setter 没有 cairo_pattern_t
的错误内省包装器的问题。因此这有效:
my $rect = GooCanvas2::CanvasRect->new(
...
'fill-color-gdk-rgba' => Gtk3::Gdk::RGBA::parse('red'),
);
此外,构造$rect
后,我们可以通过$rect->get('fill-pattern')
得到GooCanvas2::CairoPattern
。
这不允许使用从 cairo 创建的其他图案(线性等),但至少纯色可以通过提供任意 RGBA 来工作,而 pixbuf setter 应该足以满足其他需求。
更新 4:
我写了 GooCanvas2::CairoTypes 来解决这个问题,至少是为了模式。
它使用gperl_register_boxed_synonym(CAIRO_GOBJECT_TYPE_PATTERN, GOO_TYPE_CAIRO_PATTERN);
让Cairo::Pattern
作为GooCanvas::CairoPattern
使用,另外提供了一个函数通过[=将GooCanvas::CairoPattern
显式转换为Cairo::Pattern
61=].
我正在尝试将旧的 Gtk2 perl 脚本转换为 Gtk3。这是 Gtk2 版本的样子:
#!/usr/bin/perl
use 5.30.0;
use strict;
use warnings;
use diagnostics;
use Gtk2 '-init';
use Goo::Canvas;
my $canvas = Goo::Canvas->new;
my $pattern = Cairo::SolidPattern->create_rgba(0, 0, 0, 0);
$pattern = Goo::Cairo::Pattern->new($pattern);
my $rect = Goo::Canvas::Rect->new(
$canvas->get_root_item,
0, 0, 100, 100,
'fill-pattern' => $pattern,
'line-dash' => Goo::Canvas::LineDash->new([5, 5]),
'line-width' => 1,
'stroke-color' => 'black',
);
这是我的 Gtk3 尝试:
#!/usr/bin/perl
use 5.30.0;
use strict;
use warnings;
use diagnostics;
use Gtk3 '-init';
use GooCanvas2;
my $canvas = GooCanvas2::Canvas->new;
my $pattern = Cairo::SolidPattern->create_rgba(0, 0, 0, 0);
my $rect = GooCanvas2::CanvasRect->new(
parent => $canvas->get_root_item,
x => 0, y => 0, width => 100, height => 100,
'fill-pattern' => $pattern,
'line-dash' => GooCanvas2::CanvasLineDash->newv([5, 5]),
'line-width' => 1,
'stroke-color' => 'black',
);
但这会导致错误:
Uncaught exception from user code:
Cairo::SolidPattern=SCALAR(0x558de4acb260) is not of type GooCanvas2::CairoPattern at /usr/lib64/perl5/vendor_perl/5.30.1/x86_64-linux/Glib.pm line 222.
Glib::Object::_LazyLoader::AUTOLOAD("GooCanvas2::CanvasRect", "parent", GooCanvas2::CanvasGroup=HASH(0x558de4abe1a0), "x", 0, "y", 0, "width", ...) called at test.pl line 14
我也试过插入 $pattern = GooCanvas2::CairoPattern->new($pattern);
但这没有帮助:
Uncaught exception from user code:
Could not fetch information for package GooCanvas2::CairoPattern; perhaps it has not been loaded via Glib::Object::Introspection? at test.pl line 13.
但是查看 GooCanvas2.pm
的来源,它确实通过 Glib::Object::Introspection 加载 GooCanvas2
。
有什么解决方法的建议吗?
我尝试在 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 的文档:
Classes, interfaces and boxed and fundamental types get their own namespaces, in a way, as the concept of the GType is completely replaced in the Perl bindings by the Perl package name.
因此 boxed
类型在 Perl 中获得了它们自己的命名空间。更多关于盒装类型 here.
错误信息:
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()
被重定向到 GObject.xs
中的 line 1327:
SV *
g_object_new (class, ...)
// ...
在 line 1359 it finds the expected type for the property argument fill-pattern
. It looks like this is type G_TYPE_BOXED. Then at line 1378
gperl_value_from_sv (¶ms[i].value, ST (FIRST_ARG+i*2+1));
它尝试从提供的 Perl 参数(Cairo::SolidPattern
类型)中检索装箱值,这导致 line 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()
,参见 GBoxed.xs
中的 line 558。第 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);
在 line 420 调用 default_boxed_unwrap()
:
第 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's source,它为模式定义了几个setters/getters:fill-color
、fill-color-rgba
、fill-color-gdk-rgba
、fill-pixbuf
、fill-pattern
,所有这些在内部 set/get 相同 属性 goo_canvas_style_fill_pattern_id
。其他 setter 没有 cairo_pattern_t
的错误内省包装器的问题。因此这有效:
my $rect = GooCanvas2::CanvasRect->new(
...
'fill-color-gdk-rgba' => Gtk3::Gdk::RGBA::parse('red'),
);
此外,构造$rect
后,我们可以通过$rect->get('fill-pattern')
得到GooCanvas2::CairoPattern
。
这不允许使用从 cairo 创建的其他图案(线性等),但至少纯色可以通过提供任意 RGBA 来工作,而 pixbuf setter 应该足以满足其他需求。
更新 4:
我写了 GooCanvas2::CairoTypes 来解决这个问题,至少是为了模式。
它使用gperl_register_boxed_synonym(CAIRO_GOBJECT_TYPE_PATTERN, GOO_TYPE_CAIRO_PATTERN);
让Cairo::Pattern
作为GooCanvas::CairoPattern
使用,另外提供了一个函数通过[=将GooCanvas::CairoPattern
显式转换为Cairo::Pattern
61=].