Cairo ::SolidPattern не относится к типу GooCanvas2::CairoPattern

#perl #gtk3 #cairo #gobject-introspection

#perl #gtk3 #cairo #gobject-самоанализ

Вопрос:

Я пытаюсь преобразовать старый скрипт 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 .

Есть предложения, как это исправить?

Ответ №1:

Я попытался установить libgoocanvas-2.0-dev на Ubuntu 20.04, и он устанавливает XML-файл introspection для GooCanvas как /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 (amp;argc, amp;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 (amp;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() , см. Строку 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, он определяет несколько сеттеров / геттеров для шаблона: 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 из него via $rect->get('fill-pattern') .

Это не позволяет использовать другие шаблоны, созданные из cairo (линейные и т. Д.), Но, По крайней мере, solid color работает, Предоставляя произвольный RGBA, а настройки pixbuf должно быть достаточно для других нужд.

Обновление 4:

Я написал GooCanvas2::CairoTypes, чтобы обойти это, по крайней мере, для шаблонов.

Он использует gperl_register_boxed_synonym(CAIRO_GOBJECT_TYPE_PATTERN, GOO_TYPE_CAIRO_PATTERN); , чтобы разрешить Cairo::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) .

Комментарии:

1. Повторите » шаблон должен быть типа cairo_pattern_t «, да, но typedef cairo_pattern_t GooCairoPattern . Вот откуда взято имя GooCanvas2::CairoPattern из сообщения об ошибке. Из того, что я могу сказать, псевдонимы предназначены для инструментов автодокументации и / или того, что создает этот файл .gir.

2. Я думаю, что boxed — это внешний тип, который будет предоставлен другим модулем.

3. cairo_pattern_t обеспечивается включением из libcairo2-dev . Этот пакет не устанавливает .gir файл.

4. Глядя на goocanvasitemsimple.c, я понял, что здесь делать, хотя решение здесь специфично для библиотеки GooCanvas2. Я отредактирую ответ

5. Я отправил gitlab.gnome.org/GNOME/goocanvas/-/merge_requests/9 восходящий поток — если / когда он будет объединен, я обновлю ответ