Re: Making Windows Busy
- From: muppet <scott asofyet org>
- To: Anthony Edward Cooper <aecooper coosoft plus com>
- Cc: gtk-perl-list gnome org
- Subject: Re: Making Windows Busy
- Date: Thu, 31 Jan 2008 20:27:05 -0500
(Am i suffering deja vu, or has this message already been through the
list? I can't find it in the archives...)
On Jan 25, 2008, at 6:08 AM, Anthony Edward Cooper wrote:
I'm writing a GTK2 Perl application and I am trying to write a routine
that will make a window busy, i.e. display the busy cursor and stop
all
input events (keyboard and mouse clicks, but not move movement) from
going to the window).
This is using Perl version 5.8.5 on WhiteBox 4 respin 1 (clone of
RHAS4U3).
I include the following:
[snip]
set_locale Gtk2;
This is unnecessary and deprecated with gtk+ 2.x. It's all handled
under the hood.
I use the following routine to update the display when inside a
callback
that is busy doing some processing:
sub gtk2_update()
{
return if (Gtk2->main_level() == 0);
while (Gtk2->events_pending())
{
Gtk2->main_iteration();
}
}
That gets the job done, but it's preferable to restructure your code
into a work queue processing small chunks in idle callbacks. Or farm
the work off to a child process and communicate via pipes.
When you're using the "chew up events every so often" model, figuring
out how to tell you've been asked to cancel is much more difficult and
application-specific. So, all the examples below will use the
traditional mainloop model, and it's an exercise for you to warp it to
your app. ;-)
sub make_busy($$)
{
my($instance, $busy) = @_;
# Create and store the cursors if we haven't done so already.
if (! exists($instance->{busy_cursor}))
{
$instance->{normal_cursor} = Gtk2::Gdk::Cursor->new("left-
ptr");
$instance->{busy_cursor} = Gtk2::Gdk::Cursor->new("watch");
You really only need the watch cursor. To restore a window's default
cursor, you can pass undef.
}
# Do it. Make the application bar grab the input when the window is
busy,
# that way we gobble up keyboard and mouse events that could muck
up the
# application state.
if ($busy)
{
$instance->{window}->window()->set_cursor($instance-
>{busy_cursor});
Gtk2->grab_add($instance->{appbar});
This is a somewhat nasty way to prevent keyboard and mouse input.
Grabs are notoriously hard to get right, and there is no visual
indication to the user that the window is inactive. See below for
other options.
}
else
{
$instance->{window}->window()->
set_cursor($instance->{normal_cursor});
See above. This could be simply
$instance->{window}->window()->set_cursor (undef);
Gtk2->grab_remove($instance->{appbar});
}
}
[snip]
I have tried to interrupt the flow of events by writing my own main
event loop but that didn't work very well. I'm sure there is an easy
answer as it is a common thing to want to do but I can't find anything
on this. Any ideas?
The normal way to do this is to set the toplevel window insensitive,
e.g.:
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#!/usr/bin/perl -w
use strict;
use Gtk2 -init;
use Glib qw(TRUE FALSE);
my $window = Gtk2::Window->new;
my $button = Gtk2::Button->new ("Click me");
$button->signal_connect (clicked => sub {
# set the busy cursor.
$window->window->set_cursor (Gtk2::Gdk::Cursor->new ('watch'));
# disable user input, and make the window *look* disabled.
$window->set_sensitive (FALSE);
# inhibit the delete event so the window can't be closed.
my $id = $window->signal_connect (delete_event => sub { return 1 });
# Wait here for two seconds.
Glib::Timeout->add (2000, sub { Gtk2->main_quit; return 0 });
Gtk2->main;
# re-enable input.
$window->set_sensitive (TRUE);
# restore the normal cursor.
$window->window->set_cursor (undef);
# uninhibit the delete event.
$window->signal_handler_disconnect ($id);
});
$window->set_border_width (25);
$window->add ($button);
$window->show_all;
$window->signal_connect (destroy => sub { Gtk2->main_quit });
Gtk2->main;
__END__
Now, if your gui is more complicated, or you need a "cancel" button,
then simply desensitizing the whole window won't work (because then
the user couldn't click the cancel button).
There are options, of course:
1. Keep track of which widgets need to be desensitized and do only
those.
2. Use a modal dialog with a progress bar. A modal child window will
block all user interaction with the parent, and can have a progress
bar with a cancel button. Note that i use a Timeout and
Gtk2::Dialog::run(), which will block in the main loop; if you can't
structure your code that way, you'll have to unroll
Gtk2::Dialog::run(), which is another topic altogether.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#!/usr/bin/perl -w
use strict;
use Gtk2 -init;
use Glib qw(TRUE FALSE);
my $window = Gtk2::Window->new;
my $button = Gtk2::Button->new ("Click me");
$button->signal_connect (clicked => \&do_stuff);
$window->set_border_width (25);
$window->add ($button);
$window->show_all;
$window->signal_connect (destroy => sub { Gtk2->main_quit });
Gtk2->main;
sub do_stuff {
my $widget = shift;
my $toplevel = $widget->get_toplevel;
my $dialog = Gtk2::Dialog->new ('Doing Stuff', $toplevel, [],
'gtk-stop', 'cancel');
my $progress = Gtk2::ProgressBar->new;
$dialog->vbox->pack_start ($progress, FALSE, FALSE, 0);
$progress->show;
$progress->set_fraction (0);
my $total = 4; # seconds
my $step = 0.01; # seconds
my $period = $total * $step;
my $id;
$id = Glib::Timeout->add ($period * 1000, sub {
my $fraction = $progress->get_fraction;
$fraction += $step;
if ($fraction <= 1.0) {
$progress->set_fraction ($fraction);
return TRUE;
} else {
$dialog->response ('ok');
$id = 0;
return FALSE;
}
});
my $response = $dialog->run;
if ($id) {
# was stopped by user, be sure to remove timeout
Glib::Source->remove ($id);
} else {
# finished on its own
}
$dialog->destroy;
}
__END__
--
Santa ate all the cookies!
-- Zella's first, indignant words on Christmas morning, '07.
[Date Prev][
Date Next] [Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]