Glib::Ex::ConnectProperties -- link properties between objects
use Glib::Ex::ConnectProperties; my $conn = Glib::Ex::ConnectProperties->new ([ $check, 'active' ], [ $widget, 'visible' ]); $conn->disconnect; # explicit disconnect
Glib::Ex::ConnectProperties links together specified properties on two or more Glib::Objects (such as Gtk2 widgets etc) so a change made to any one of them is propagated to the others.
Glib::Ex::ConnectProperties
Glib::Object
This is an easy way to tie a user control widget to a setting elsewhere. For example a CheckButton active could be linked to the visible of another widget, letting the user click to hide or show.
active
visible
+--------------------+ +-------------+ | CheckButton/active | <-------> | Foo/visible | +--------------------+ +-------------+
The advantage of ConnectProperties is that it's bi-directional. If other code in the program changes "Foo/visible" then that change is propagated to "CheckButton/active" too, ensuring the button display shows what it's controlling no matter how the target changes. See examples/sensitive.pl in the Glib-Ex-ConnectProperties sources for a complete program.
The following property types are supported
string number integer or float enum Glib::Enum subtypes flags Glib::Flags subtypes object Glib::Object string array Glib::Strv some boxed Glib::Boxed
Boxed types which work include Gtk2::Gdk::Color and Gtk2::Gdk::Rectangle, but others may not. See "Equality" below.
Gtk2::Gdk::Color
Gtk2::Gdk::Rectangle
Read-only properties can be used. They're propagated out to the other linked properties but any changes in those others are not stored back. Usually this is only useful when the read-only is the only one changing. You could easily enough make an explicit signal handler to propagate the value, but a ConnectProperties is convenient and is careful not to make circular references. See the read_only option below to force read-only.
read_only
Write-only properties can be used. Nothing is read out of them, they're just set from changes in the other linked properties. Write-only properties are unusual, but see the write_only option below to force write-only.
write_only
It works to link properties on the same object. This can ensure they update together. It also works to have different ConnectProperties linkages with an object/property in common. A change coming from one group propagates through to the other. This arises quite naturally if there's two separate controls for the same target.
A property name can include an explicit class such as GtkLabel::justify in the usual style of set_property(), find_property(), etc.
GtkLabel::justify
set_property()
find_property()
[ $widget, 'GtkLabel::justify' ] # class name and property name
If a subclass accidentally shadows a superclass property name then this gives access to the superclass property. But it's otherwise unnecessary and is not recommended. For a Perl subclass like My::Foo::Bar the fully-qualified name is My__Foo__Bar::propname, as usual for Perl module to Glib class name conversion.
My::Foo::Bar
My__Foo__Bar::propname
Various additional object or widget settings can be accessed by ConnectProperties. They're either other flavour properties, or are non-property attributes which have some sort of signal notifying when they change. For example
child#propname
See the following modules for Gtk related things,
The Gtk2 elements don't create a dependency on Gtk2 unless you use them. The implementation is modular so extras are not loaded unless used. The # separator character doesn't clash with plain property names as it's not allowed in a ParamSpec name.
Gtk2
#
See Glib::Ex::ConnectProperties::Element to create a new element subclass.
$conn = Glib::Ex::ConnectProperties->new ([$obj1,$pname1], [$obj,$pname2], ...)
Connect two or more given object+property combinations. The connection lasts for as long as the objects do.
The return value is a Perl object of type Glib::Ex::ConnectProperties. It can be kept to later break the connection with disconnect() below, otherwise it can be ignored.
disconnect()
$conn = Glib::Ex::ConnectProperties->dynamic ([$obj1,$pname1], [$obj,$pname2], ...)
Connect two or more given object+property combinations. The return is a Perl object of type Glib::Ex::ConnectProperties. The connection lasts only as long as you keep this returned object.
The arguments to both constructors are arrayrefs with an object, a property name, and perhaps further options as described below. For example
Glib::Ex::ConnectProperties->new ([$object1, 'some-propname'], [$object2, 'another-propname']);
An initial value is propagated from the first object+property (the first readable one) to set all the others, if they're not already the same. So put the object with the desired initial value first.
A ConnectProperties only keeps weak references to the objects, so the linkage doesn't prevent some or all of them being garbage collected.
A dynamic() linkage can be used if it's only wanted for a certain time, or if desired linkages might change and you want to drop an old one and make a new. For example something like the following inside a widget or object would allow a target to be changed, including changed to undef for nothing linked.
dynamic()
undef
sub set_target { my ($self, $target_object) = @_; $self->{'conn'} = $target_object && Glib::Ex::ConnectProperties->dynamic ([$self, 'my-prop'], [$target, 'target-prop']); }
$conn->disconnect()
Disconnect the given ConnectProperties linkage.
$conn can made by either new() and dynamic() above. A dynamic one is also disconnected automatically when garbage collected.
$conn
new()
Various key/value options can be given in each [$object,$propname] element. For example,
[$object,$propname]
Glib::Ex::ConnectProperties->new ([$checkbutton, 'active'], [$label, 'sensitive', bool_not => 1]);
read_only => $bool
Treat the property as read-only, ignoring any writable flag in its ParamSpec. This is probably of limited use, but might for instance on an output-only or to stop other properties writing back to a master control. See examples/unidirectional.pl in the Glib-Ex-ConnectProperties sources for a complete program.
writable
write_only => $bool
Treat the property as write-only, ignoring any readable flag in its ParamSpec.
readable
This can be used for display things such as a Gtk2::Label which you want to set, but don't want to read back. If the value is mangled for display (see "Value Transformations" below) then there might be no easy reverse transformation to read back anyway.
Gtk2::Label
Glib::Ex::ConnectProperties->new ([$job, 'status'], [$label, 'text', write_only => 1]);
Of course an explicit signal handler can do a one-way set like this, but ConnectProperties is a fewer lines of code.
read_signal => $signame
Connect to $signame to see changes to the property. The default is the notify signal as notify::$propname which means a property change is immediately seen and propagated. A different signal can be used to do it at other times instead.
$signame
notify::$propname
For example on a Gtk2::Entry the text property notifies for every character typed by the user. Using the activate signal instead you can take the value only when the user presses Return.
Gtk2::Entry
text
activate
Glib::Ex::ConnectProperties->new ([$entry, 'text', read_signal => 'activate'], [$label, 'text']);
The signal can have any parameters (which are all ignored currently). Usually the only sensible signals are those like activate which are some sort of user action.
read_signal_return => $signame
The return value for the read_signal handler above. The default return is undef.
read_signal
Generally the signals which are useful for triggering a read have no return value (ie. void), so no particular return is needed. But for example if a widget event handler was a good time to look at a property then a return of Gtk2::EVENT_PROPAGATE would generally be wanted to let other handlers see the event too.
void
Gtk2::EVENT_PROPAGATE
Glib::Ex::ConnectProperties->new ([$widget, 'window', read_signal => 'map-event', read_signal_return => Gtk2::EVENT_PROPAGATE ], [$drawing_thing, 'target-window']);
Storing a value goes through the following steps,
Value transformations specified in the element, if any.
value_validate() of the target ParamSpec (in Glib-Perl 1.220 where that method is available).
value_validate()
Equality check, if the target is readable, to avoid a set_property() if it's already what's desired (see "Equality" below).
value_validate() does things like clamp numbers outside the ParamSpec min/max, perhaps manipulate string contents, etc. This at least gives something which can be stored.
Perl's usual value coercing such as stringizing, numizing, truncating integers, etc, applies to the value_validate() call and the set_property() call, in the usual way. This means string properties can be linked to number properties or similar with no special transformations.
In the following options the "in" transformations are for storing and the "out" for reading. func is the most general. hash is handy for a fixed set of possible values.
func
hash
bool_not => 1
Negate with the Perl ! operator. For example a check button which when checked makes a label insensitive,
!
func_in => $coderef
func_out => $coderef
Call $value = &$coderef($value) to transform values going in or coming out. (See examples/func-transform.pl in the Glib-Ex-ConnectProperties sources for a complete program doing this.)
$value = &$coderef($value)
hash_in => $hashref
hash_out => $hashref
Apply $value = $hashref->{$value} to transform values going in or coming out.
$value = $hashref->{$value}
If a $value doesn't exist in the hash then the result will be undef in the usual way. Various tied hash modules can change that in creative ways, for example Hash::WithDefaults to look in fallback hashes.
$value
Hash::WithDefaults
The hashes are not copied, so future changes to their contents will be used, though there's nothing to forcibly update property values if current settings might be affected.
See examples/hash-transform.pl in the Glib-Ex-ConnectProperties sources for a complete program using hash transforms.
For a read-write property the "in" should generally be the inverse of "out". Nothing is done to enforce that, but strange things are likely to happen if the two are inconsistent.
A read-only property only needs an "out" transformation and a write-only property only needs an "in" transformation, including when the read_only or write_only options above force that ("General Options").
ConnectProperties uses a notify signal handler on each object to update the others. Updating those others causes them to emit their own further notify signals (even if the value is unchanged), so some care must be taken not to have an infinite loop. The present strategy is
notify
An "in progress" flag in the ConnectProperties object, so during an update it recognises that any further notify emissions as its own doing and can be ignored.
On each target the value from a get is compared before doing a set. If already right then the set call is not made at all.
get
set
The in-progress flag acts against immediate further notifys. This could also be done by temporarily disconnecting or blocking the handlers, but that seems more work than ignoring.
Compare-before-set copes with freeze_notify() because in that case the notify calls don't come while the "in progress" flag is on, only later, perhaps a long time later.
freeze_notify()
If the func_in / func_out transformations are inconsistent, so that a value going in is always different from what comes out, then usually the "in progress" case prevents an infinite loop, as long as the program eventually reaches a state with no freeze_notify in force.
func_in
func_out
freeze_notify
It might be wondered if something simpler is possible. For the general case no, not really. The specific set_foo() methods on most widgets and objects often notice an unchanged setting and do nothing, but when using the generic set_property() the protection above is needed.
set_foo()
An existing value and prospective new value are compared using values_cmp() in Glib-Perl 1.220 or a fallback otherwise. For example in Glib::Param::Double anything within "epsilon" (1e-90 by default) is close enough. values_cmp() lets ParamSpec subclasses control what they consider equal.
values_cmp()
Glib::Param::Double
The core Glib::Param::Boxed only compares by pointer value, which is fairly useless since boxed objects are frequently copied so you probably don't have an identical pointer. ConnectProperties tries to improve this by:
Glib::Param::Boxed
equal() or compare() method from the value type when available. This covers Gtk2::Gdk::Color, Gtk2::Gdk::Region and Gtk2::TreePath.
equal()
compare()
Gtk2::Gdk::Region
Gtk2::TreePath
Glib::Strv compared by string contents.
Glib::Strv
Gtk2::Border compared by field values.
Gtk2::Border
Gtk2::Gdk::Cursor compared by type, though bitmap cursors are still only by pointer.
Gtk2::Gdk::Cursor
type
Glib::Scalar compared with eq. This may be of limited help and it's probably better to subclass Glib::Param::Scalar and make a type-specific values_cmp, if/when that's possible.
Glib::Scalar
eq
Glib::Param::Scalar
values_cmp
Glib::Param::Object pspecs could perhaps benefit from using an equal() or compare() method on the object the same as for boxed objects. But usually when setting a Glib::Object it's a particular object which is desired, not just contents. If that's not so then as with Glib::Scalar it could be handled by a ParamSpec subclass with a values_cmp() to express when different objects are equal enough. If/when possible that would work for both C code and Perl code comparisons.
Glib::Param::Object
When writing an object or widget (per Glib::Object::Subclass) don't forget to explicitly notify if changing a property outside a SET_PROPERTY(). For example,
SET_PROPERTY()
sub set_foo { my ($self, $newval) = @_; if ($self->{'foo'} != $newval) { $self->{'foo'} = $newval; $self->notify('foo'); } }
This sort of notify is necessary in any object or widget implementation. Failing to do so will mean ConnectProperties doesn't work, and probably other things too. A SET_PROPERTY() can call out to a setter function like the above to re-use code. In that case Glib collapses the notify() there to just one notify at the end of SET_PROPERTY().
notify()
Glib::Object, Glib::ParamSpec, Glib::Boxed
Gtk2, Glib::Ex::TieProperties
Tie::Wx::Widget
http://user42.tuxfamily.org/glib-ex-connectproperties/index.html
Copyright 2007, 2008, 2009, 2010, 2011, 2012 Kevin Ryde
Glib-Ex-ConnectProperties is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
Glib-Ex-ConnectProperties is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Glib-Ex-ConnectProperties. If not, see http://www.gnu.org/licenses/.
To install Glib::Ex::ConnectProperties, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Glib::Ex::ConnectProperties
CPAN shell
perl -MCPAN -e shell install Glib::Ex::ConnectProperties
For more information on module installation, please visit the detailed CPAN module installation guide.