http://qs321.pair.com?node_id=723785

renegadex has asked for the wisdom of the Perl Monks concerning the following question:

I made a thumbnail viewer using Gtk2 and Perl but sadly its very slow because it reads all the images, shrinks them into a thumbnail size and displays them after all have been loaded. I researched about threads in perl and learned that I could use a separate thread to process the images into thumbnails. The processing of the image works ok, but the displaying of the image isn't. I noticed that if the sub routine that created the thread finishes, the thread can no longer use the widgets in the main window. The widget im using is the Iconview widget. Some of the thumbnails were showed but most were not showed. If I add sleep(2) on the sub routine that created the thread, it shows all the thumbnails.
  • Comment on perl gtk2 thumbnail viewer with threads

Replies are listed 'Best First'.
Re: perl gtk2 thumbnail viewer with threads
by zentara (Archbishop) on Nov 15, 2008 at 14:23 UTC
    It's pretty hard to say what your exact problem is without seeing a minimal working code example, but using the ESP module, I think what you are looking for is a single usuable thread, that processes the images(looping thru them), then use an Idle->add in the thread after each thumbnail is created, to add it to the widget in the main Gtk2 thread.

    Here is the basic idea of using Idle->add, it is the preferred way for threads to manipulate widgets in the main thread. The 2 second wait, you mention, hints at the eventloop being lazy in updating everything. The Idle->add will instruct the eventloop to add the new thumbnail to the display widget, at the first possible opening in event processing.

    Glib::Idle->add is the great advance that Gtk2 made over Tk, to allow threads to access the gui widgets safely. And DO NOT spawn a new thread to process each image, that will be very slow. Reuse a single (or maybe a couple) of threads.

    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; use Glib qw/TRUE FALSE/; use Gtk2 qw/-init -threads-init/; Glib::Object->set_threadsafe (TRUE); #setup shared hash my %shash; share(%shash); #will work for first level keys $shash{'go'} = 0; $shash{'work'} = ''; $shash{'die'} = 0; my $num:shared = 0; my $window = Gtk2::Window->new('toplevel'); $window ->signal_connect( 'destroy' => \&delete_event ); $window->set_border_width(10); $window->set_size_request(300,300); my $vbox = Gtk2::VBox->new( FALSE, 6 ); $window->add($vbox); $vbox->set_border_width(2); my $hbox= Gtk2::HBox->new( FALSE, 6 ); my $hbox1 = Gtk2::HBox->new( FALSE, 6 ); $vbox->pack_end($hbox,FALSE,FALSE,0); $vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0); $vbox->pack_end($hbox1,FALSE,FALSE,0); $hbox->set_border_width(2); $vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0); my $ebutton = Gtk2::Button->new_from_stock('gtk-quit'); $hbox->pack_end( $ebutton, FALSE, FALSE, 0 ); $ebutton->signal_connect( clicked => \&delete_event ); my $pbar = Gtk2::ProgressBar->new(); $pbar->set_pulse_step(.1); $hbox->pack_start($pbar,1,1,0); my $label_w_markup = Gtk2::Label->new(); $label_w_markup->set_markup("<span foreground=\"yellow1\" size=\"40000\">$num</span>"); $vbox->pack_end($label_w_markup,FALSE,FALSE,4); ###################################################### my $tbutton = Gtk2::Button->new_with_label('Run Thread'); $hbox1->pack_start($tbutton , 1, 1, 0 ); my $lconnect = $tbutton->signal_connect( clicked => sub{ launch() }); my $sconnect; $window->show_all(); $pbar->hide; #needs to be called after show_all #create 1 sleeping thread passing it the label and pbar to control my $thread = threads->new(\&work, $label_w_markup, $pbar); Gtk2->main; ###################################### sub delete_event { $shash{'go'} = 0; $shash{'die'} = 1; $thread->join; Gtk2->main_quit; return FALSE; } ####################################### sub launch{ $pbar->show; $tbutton->set_label('Stop Thread'); $tbutton->signal_handler_block($lconnect); $sconnect = $tbutton->signal_connect( clicked => sub{ stop() }); $shash{'go'} = 1; } ################################################## sub stop{ print "stopped\n"; $shash{'go'} = 0; $pbar->hide; $tbutton->set_label('Run Thread'); $tbutton->signal_handler_block ($sconnect); $tbutton->signal_handler_unblock ($lconnect); } ######################################################### sub work{ my ($label,$pbar) = @_; $|++; while(1){ if($shash{'die'} == 1){ return }; if ( $shash{'go'} == 1 ){ $num = 0; while(1){ $num++; if($num > 30){last} Glib::Idle->add( sub{ if($shash{'die'} == 1){ return }; $label->set_markup("<span foreground=\"yellow1\" size=\"40000\">$num</span>"); $pbar->pulse; return FALSE; }); select(undef,undef,undef, .1); if($shash{'go'} == 0){last} if($shash{'die'} == 1){ return }; } #if reach this point, the thread has finished print "my results = $num\n"; Glib::Idle->add( sub{ if($shash{'die'} == 1){ return }; $label->set_markup("<span foreground=\"green\" size=\"60000\">$num</span>"); $pbar->hide; return FALSE; }); $shash{'go'} = 0; #turn off self before returning }else { select(undef,undef,undef,.1) } #sleep time } }

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: perl gtk2 thumbnail viewer with threads
by Joost (Canon) on Nov 16, 2008 at 01:21 UTC
    From what I've gathered, in most GTK bindings for most languages, you can't/shouldn't call GTK routines from different threads - in other words: all GUI interactions should be handled by the same thread (but you can probably off-load a lot of non-GUI processing to other threads).

    Whether or not threads and the way you're using them is even the right way to go about whatever it is that you're doing is another subject and I'm not going into that. I'm assuming you know what you're doing.

Re: perl gtk2 thumbnail viewer with threads
by Anonymous Monk on Nov 15, 2008 at 16:36 UTC
    You don't even need threads, you can load the images in a idle callback.

    Here is an example I've made some time ago. It's a much more complex example, because it doesn't use a IconView, and it's not very clean, as the code comes from a much bigger program and I just made some quick modifications to make it work by itself, but it works very nicely.

    To use it, pass it a list of files in standard input, for example with find :

    find ~/Pictures/ -iname \*.jpg | perl mosaic_standalone.pl

    It is made to display HUGE list of pictures, that's one of the reason it doesn't use Gtk2::IconView. But the principle of loading the pictures in a idle callback can be used with Gtk2::IconView.

    You can find it here

Re: perl gtk2 thumbnail viewer with threads
by renegadex (Beadle) on Nov 27, 2008 at 11:00 UTC
    Mr. Anonymous' code works great. That is just what I wanted to do :) Though I haven't had the time to study how idle works. :D but I works great! :)
    Mabuhay Civil Engineers! :D