2

我正在尝试将旧的 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,它确实GooCanvas2通过 Glib::Object::Introspection 加载。

任何建议如何解决这个问题?

4

1 回答 1

2

我尝试在 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()被重定向到第 1327GObject.xs

SV *
g_object_new (class, ...)
   // ...

第 1359 行,它找到了属性参数的预期类型fill-pattern。看起来这是类型G_TYPE_BOXED。然后在第 1378 行

gperl_value_from_sv (&params[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(),见第 558GBoxed.xs。上线568

boxed_info = g_hash_table_lookup (info_by_gtype, (gpointer) gtype);

boxed_infoBoxedInfo *与内容一样返回

{
  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::CairoPatternCairo::Patternvia gperl_new_boxed(gperl_get_boxed_check(input, GOO_TYPE_CAIRO_PATTERN), CAIRO_GOBJECT_TYPE_PATTERN, 0)

于 2020-10-31T22:37:18.360 回答