#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 восходящий поток — если / когда он будет объединен, я обновлю ответ