NAME
Glib::Ex::ConnectProperties -- link properties between objects
SYNOPSIS
use Glib::Ex::ConnectProperties;
my $conn = Glib::Ex::ConnectProperties->new
([ $check, 'active' ],
[ $widget, 'visible' ]);
$conn->disconnect; # explicit disconnect
DESCRIPTION
Glib::Ex::ConnectProperties
links together specified properties on two or more Glib::Object
s (including Gtk2 widgets) so a change made to any one of them is propagated to the others.
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 it.
+--------------------+ +-------------+
| CheckButton/active | <-------> | Foo/visible |
+--------------------+ +-------------+
The advantage of ConnectProperties is that it's bi-directional, so if other code changes "Foo/visible" then that change is sent back to "CheckButton/active" too, ensuring the button display shows what it's controlling, no matter how the target changes.
Property Types
String, number, enum, flags, and object property types are supported. A few boxed types like Gtk2::Gdk::Color
work too, though others may not (see "Equality" below).
Read-only properties can be given. They're propagated out to the other linked properties but changes in those others are not stored back. This can leave different values, which rather defeats the purpose of the linkage. Linking a read-only probably only makes sense if that read-only is the only one changing. See the read_only
option below to force read-only.
Write-only properties can be given. Nothing is read out of them, they're just set from changes in the other linked properties. Write-only properties are often pseudo "add" methods etc, so it's a little unlikely linking a write-only will be wanted. See the write_only
option below to force write-only.
It works to connect two properties on the same object. This can ensure they update together. It also works to have two different ConnectProperties with an object/property in common. A change coming from one group propagates through to the other. Such a setup arises quite naturally if you've got two controls for the same target; neither needs to know the other exists.
FUNCTIONS
Creation
$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 withdisconnect
below, otherwise it can be ignored. $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 that returned object.
The arguments to both functions are arrayrefs with an object, a property name, and possible further options described below. For example
Glib::Ex::ConnectProperties->new
([$aa_object, 'one-prop'],
[$bb_object, 'another-prop']);
An initial value is propagated from the first object+property (the first readable one) to set all the others in case 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.
The new
is a permanent linkage for the given objects. A dynamic
can be used if a linkage is only wanted for a certain time, or if the target objects to link might change and you should drop the old linkage and make a new one. For example something like the following in a widget or object allowing a target to be changed, including to undef
for no linkage.
sub set_target {
my ($self, $target_object) = @_;
$self->{'conn'} =
$target_object && Glib::Ex::ConnectProperties->new
([$self, 'my-prop'],
[$target, 'target-prop']);
}
Operations
$conn->disconnect()
-
Disconnect the given ConnectProperties linkage.
This can be linkages made by both the
new
anddynamic
above. A dynamic one is disconnected automatically when garbage collected.
OPTIONS
Various options can be given in each [$object,$propname]
element as key/value pairs. For example,
Glib::Ex::ConnectProperties->new
([$checkbutton, 'active'],
[$label, 'sensitive', bool_not => 1]);
General Options
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 stop other properties writing back to a master control. write_only => $bool
-
Treat the property as write-only, ignoring any
readable
flag in its ParamSpec.One use for this is 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 not be an easy reverse transformation to read back anyway.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 couple less lines.
read_signal => $signame
-
Connect to
$signame
to see changes to the property. The defaultnotify::$propname
means a property change is immediately seen and propagated. A different signal can be used to do it at other times instead.For example on
Gtk2::Entry
thetext
property notifies for every character typed by the user. With theactivate
signal you can instead take the value only when the user presses Return.Glib::Ex::ConnectProperties->new ([$entry, 'text', read_signal => 'activate'], [$label, 'text']);
The signal can have any parameters. 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. The default return isundef
.Most
read_signal
candidates arevoid
, so no particular return value is needed. But as an example if a widget event was a good time to look at a property then a return ofEVENT_PROPAGATE
would be wanted to let other handlers see the event too.Glib::Ex::ConnectProperties->new ([$widget, 'window', read_signal => 'map-event', read_signal_return => Gtk2::EVENT_PROPAGATE ], [$drawing_thing, 'target-window']);
Value Transformations
The following value transformations can be specified with parameters in each object/property element.
Before storing a value it's also put through value_validate
on the target ParamSpec (in Glib-Perl 1.220 where that method is available). This clamps numbers which might be out of range, may manipulate string contents, etc. The result might not be the exact desired value, but is at least something which can be set.
bool_not => 1
-
Negate with the Perl
!
operator. For example a check button which when checked makes a label insensitive,Glib::Ex::ConnectProperties->new ([$checkbutton, 'active'], [$label, 'sensitive', bool_not => 1]);
func_in => $coderef
func_out => $coderef
-
Call
$value = &$coderef($value)
to transform values going in or coming out. hash_in => $hashref
hash_out => $hashref
-
Apply
$value = $hashref->{$value}
to transform values going in or coming out.If a
$value
doesn't exist in the hash then the result will beundef
in the usual way. Various tie modules can change that in creative ways, for exampleHash::WithDefaults
to look in fallback hashes.The hashes are not copied, so future changes to their contents will be used by the ConnectProperties, though there's nothing to forcibly update values if the current settings might be affected.
A read-only or write-only property only needs the corresponding "out" or "in" transformation, including those with the read_only
or write_only
options (see "General Options" above).
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.
IMPLEMENTATION NOTES
ConnectProperties uses a notify
signal handler on each object to update the others. Updating causes them to emit 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 twofold
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 aset
. If already right then theset
call is not made at all.
The in-progress flag acts against immediate further notify
s. This could be done by temporarily disconnecting or blocking the handlers, but that seems more work than ignoring.
The 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.
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.
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.
Equality
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.
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:
equal
orcompare
method from the value type when available. This coversGtk2::Gdk::Color
,Gtk2::Gdk::Region
andGtk2::TreePath
.Glib::Strv
compared by string contents.Gtk2::Border
compared by field values.Gtk2::Gdk::Cursor
compared bytype
, though bitmap cursors are still only by pointer.Glib::Scalar
compared witheq
. This may be of limited help and it's probably better to subclassGlib::Param::Scalar
and make a type-specificvalues_cmp
, if/when that's possible.
Glib::Param::Object
pspecs could perhaps benefit from using an equal
or compare
method on the value type 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 may be handled by a ParamSpec subclass with a values_cmp
to express when different objects are equal enough (which, if/when possible, would work for both C code and Perl code comparing).
Notifies
If you're writing an object or widget (per Glib::Object:Subclass) don't forget to explicitly notify
when changing a property outside of SET_PROPERTY
. For example,
sub set_foo {
my ($self, $newval) = @_;
if ($self->{'foo'} != $newval) {
$self->{'foo'} = $newval;
$self->notify('foo');
}
}
This sort of thing is wanted in any object or widget, but failing to do so will in particular mean ConnectProperties doesn't work. SET_PROPERTY
can call out to a setter function like this to re-use code. In that case the extra notify
call is harmless and Glib collapses it to just one notify at the end of SET_PROPERTY
.
SEE ALSO
HOME PAGE
http://user42.tuxfamily.org/glib-ex-connectproperties/index.html
LICENSE
Copyright 2007, 2008, 2009, 2010 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/.