MooseX::Gtk2 - Connect Moose with Gtk2
version 0.01
package MyWindow;
use MooseX::Gtk2;
extends 'Gtk2::Window';
has button => (
is => 'ro',
isa => 'Gtk2::Button',
builder => '_build_button',
);
sub _build_button { Gtk2::Button->new('Hello World') }
sub BUILD {
my ($self) = @_;
$self->add($self->button);
}
register;
And later:
use MyWindow;
my $window = MyWindow->new(title => 'My App');
$window->show_all;
Gtk2->main;
This extension allows you to use Moose to declare Glib classes that are fit to be used as objects in Gtk2 applications. You cannot simply subclass a Gtk2::Widget
and put it into a Gtk2::VBox
. The class has to be registered with Glib as an object class first. This is what the call to "register" accomplishes in the "SYNOPSIS".
Note that you don't have to load Gtk2 with the -init
switch or call Gtk2->init
yourself. This extension will make sure the package MooseX::Gtk2::Init is loaded which ensures initialization has taken place.
While the behaviour of classes declared with this module is a bit different than the use of their Gtk2 or Glib parents, the original classes will not be changed. Most of the differences are for maintainability's sake. I tried to conform to Glib/Gtk2 practices where appropriate.
The class definitions look very similar to normal Moose classes. First you need to use MooseX::Gtk2
to initialize your metaclasses and import the necessary declarative callbacks:
package MyWidget;
use MooseX::Gtk2;
Note that you don't have to use Moose
, since MooseX::Gtk2
uses Moose::Exporter and automatically sets up Moose as well.
You will then usually define a class to extend with the "extends" keyword:
extends 'Gtk2::Button';
If you don't specify a parent class, your new class will be based on Glib::Object. This can be useful if you want to build non-widget classes which support Glib signals.
Since Glib only allows single inheritance, you will not be able to inherit from more than one class at a time. Even if Glib would support this, it would probably be limited to avoid edge cases.
You can declare "Properties" with "has" and apply roles via "with" as usual. If you want your roles to provide signals, you'll have to use MooseX::Gtk2::Role as outlined in "Role Definitions".
Unlike normal Perl classes, the Glib has to be told about your class before it wants anything to do with it. This is why classes declared with this module have to finalize themselves with a call to "register":
register;
This function will throw an error when something goes wrong and return a true value otherwise, so you can at least skip the 1;
in these classes. Calling this will effectively lock the class down, so make sure you do any declarations and role applications before this.
After this, your class is ready to use.
MooseX::Gtk2::Roles are mostly just like Moose::Roles:
package MyRole;
use MooseX::Gtk2::Role;
requires qw( _handle_an_event );
has a_property => (is => 'rw');
signal an_event => (handler => '_handle_an_event');
with qw( MyOtherRole );
1;
Notice the 1;
at the end? Since Glib only cares about the structure of the final classes, you don't have to register the roles you apply to your classes during declaration.
An error will be thrown if you try to compose a MooseX::Gtk2::Role into something that doesn't know how to handle signals.
When you call new
on one of your declared classes, you always have to pass in key/value pairs, no matter what your parent expects. All constructors are overridden, so we don't have to maintain lots of special cases. This means that while you can create a new Gtk2::Window
like this:
Gtk2::Window->new('toplevel');
When you want to do the same with your own window class, you'd have to do:
YourWindowClass->new(type => 'toplevel');
However, you can use BUILDARGS
(explained in "BUILDARGS" in Moose::Object) to modify the arguments the constructor accepts, just like regular Moose classes.
You can also use BUILD
and DEMOLISH
as usual.
What Moose calls an attribute is known to Glib as a property. If you create an attribute on your subclass like the following:
has some_attribute => (
is => 'rw',
isa => 'Str',
required => 1,
);
Your attribute will automatically be prepared to be registered with Glib when you call "register".
Usually the is
option controls if you receive an accessor or just a reader. In this case, however, it will have two different implications:
Generated Methods
You'll get a reader named
get_some_attribute
andset_some_attribute
instead of a reader or an accessor with the name of the attribute. This is just the default, of course. It was chosen to maintain symmetry with Glib conventions.General Access
A
Glib::Object
always also has aset
and aget
method that work like the following:$object->set(foo => 23, bar => 17); my ($foo, $bar) = $object->get(qw( foo bar ));
Unless your parent class overrides these methods (
Gtk2::TreeStore
is an example that uses these methods to set row column values), they will be overridden so your type constraints are respected (Note: This is only true in Perl-space). The same is true for theget_property
/set_property
aliases ofget
/set
.
It is also possible to extend Glib properties inside your class:
package MyWindow;
use MooseX::Gtk2;
extends 'Gtk2::Window';
has '+title' => (isa => 'MyTypeConstraint');
register;
Be careful, however, since full compatibility with all systems that access these values can not be guaranteed at all.
In all cases, things like trigger
, isa
and lazy defaults will probably not work correctly when triggered inside Glib. This might change over time, since it should be possible to emulate these behaviours via the GET_PROPERTY
/SET_PROPERTY
facilities.
Signals are specific to Glib and have no Moose equivalent. You can declare a new signal with the "signal" keyword:
signal new_signal => (@options);
See "signal" for all available options. Signals and overrides can be declared in classes as well as in roles. They can only be declared once per class, and you cannot override a signal in the same class as you declare it on. These restrictions include signals that are composed in via roles.
All of these are exported by default.
extends 'ParentClass';
Declares the parent class, which needs to be a subclass of Glib::Object
. Multiple inheritance is not supported.
In case your parent class is a Glib::Object
that is not yet a MooseX::Gtk2
extended class, your subclass will actually not directly depend on the parent class, but on a membrane class which maps the Glib identity of the class into Moose terms. This is the layer that makes things such as attributes for existing Gtk2 properties possible.
has attribute_name => (%options);
The possible options are the same as for normal Moose attributes. You can also extend existing attributes (even those provided by Glib objects) as normal with Moose:
has '+existing_attribute' => (%new_options);
Not all functionality of attributes might be avilable when trigger from inside Glib.
signal signal_name => (%options);
The above will simply declare a new signal. You can additionally override an existing signal handler via:
signal existing_signal => \&signal_handler_override;
The subroutine provided instead of a set of options will be used as new signal handler. Overrides can only be done for signals provided above the current class they are added to (including from roles). Signals and overrides are also only settable once.
A signal defined like this can be called like the usual Glib signals:
my $return = $object->signal_emit(signal_name => $object, @args);
The available signal options are:
arity
-
Declares the number of arguments. Defaults to 0. The widget on which the signal is emitted is always expected to be the first argument and is not counted in the arity setting. Here is an example of a different arity:
signal add => ( arity => 2, handler => '_handle_add', ); sub _handle_add { my ($self, $num1, $num2) = @_; return $num1 + $num2; } $object->signal_emit(add => $object, 2, 3); # returns 5
runs
-
Can be one of
first
,last
orcleanup
. Defaults tolast
. This run type regulates when the handler will be run in the signal chain, and what callback is able to return a value. handler
-
This should be either a method name or a code reference. The handler will be called when the signal was emitted. When it will run depends on the value passed to "runs".
restart
-
If set to true, a signal will be restarted instead of running recursively when it is fired while it is being handled.
collect
-
The accumulator function. You probably don't want this unless you know that you do.
register;
Requires no arguments. This must be called last in your class to register it with Glib. It will return a true value so you don't have to do that.
After this function is called, your class will also be immutable, and it will stay that way. Once Glib knows about your class, nothing can be changed.
It's the same as usual.
Only blessed hash references can be used. Trying to make it compatible with something else would be a mess.
Glib only supports single inheritance. So we do the same.
Don't hook into these, since they might not work as expected. Destruction is handled completely by Glib, since your object's lifetime might be longer than that of the reference you have of it.
If you provide a DEMOLISH
method, calls to it will be emulated by hooking into Glib's FINALIZE_INSTANCE
. The methods BUILD
and BUILDARGS
should be available as usual.
Things that are set by Glib code outside of Perl or even just outside MooseX::Gtk2
, might bypass specific functions of Moose and set the values directly. This needs to be emulated carefully, but there might always be ways around the MOP.
For attributes that are loaded from non-MooseX::Gtk2
classes, if we can find set_*
and get_*
methods that have the right name, we'll assume they are related to the attribute.
Currently not supported at all.
Since we have to register every class with Glib or it won't be usable as such, things like anonymous classes, or role-to-object application will not work.
Provide
requires_signals(@signal_names)
for roles.Allow to provide Glib types other than
Glib::Scalar
.Option to turn of implicit invocant signal parameter.
More tests.
Please report any bugs or feature requests to bug-moosex-gtk2@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Gtk2
Robert 'phaylon' Sedlacek <rs@474.at>
This software is copyright (c) 2011 by Robert 'phaylon' Sedlacek.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.