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

Hey all!

Well, I have the following 2 scripts:

"testWX.pl"
#!/usr/bin/env perl use Data::Dumper; use threads; use Thread::Queue::Any; use strict; use warnings; my $thread_queue = Thread::Queue::Any->new; async { \&testThread($thread_queue) }; async { \&_thread_for_images($thread_queue) }; while (1) { sleep 1; } sub testThread { my $queue = shift; while (1) { $queue->enqueue("something"); sleep 2; } } sub _thread_for_images { my $queue = shift; my $pid; while (1) { my ($full_path) = $thread_queue->dequeue; print $full_path . "\n"; sleep 2; #if ($oid) { # print "KILL!\n"; # $oid->kill(); #} #sleep 2; $pid = fork(); if ($pid) { print "Parent\n"; waitpid($pid,0); } elsif (defined ($pid)) { print "Child\n"; &mainloop2(); exit; } #$app->MainLoop; } } sub mainloop2 { if (-e "imageHandling.pl") { my $app = do "imageHandling.pl"; if (!defined($app)) { die "imageHandling not loaded.\n"; } } else { print "imageHandling.pl not found.\n"; exit; } my $app = MyApp->new(); # create $app->newImageFrame("L:/02.jpg", 200, 500); $app->newImageFrame("I:/34.jpg", 400, 500); #print Dumper($app); $app->MainLoop; $app->Destroy; }

"imageHandling.pl"
package MyApp; use strict; use vars qw(@ISA); use Wx qw(:everything); @ISA=qw(Wx::App); #use Wx::Event qw( EVT_MOTION EVT_LEFT_DOWN ); use Data::Dumper; sub new { my $class = shift; my $this = $class->SUPER::new(@_); return $this; } sub OnInit { return 1; } sub newImageFrame { #my $this = @_; my $this = shift; my $file = shift; my $x = shift; my $y = shift; #wxFrame(wxWindow* parent, wxWindowID id, const wxString& titl +e, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDef +aultSize, long style = wxDEFAULT_FRAME_STYLE, const wxString& name = +"frame") my $frame = MyFrame->new( "Image View", [-1,-1], [-1,-1], $fil +e); unless ($frame) {print "unable to create frame -- exiting."; r +eturn undef} push(@{$this->{Frame}}, $frame); #EVT_LEFT_DOWN ($this, \&evtLeftDown); #$frame->SetSize(0,0, 210, 210); $frame->SetSize(0,0, 200, 200); $frame->Move($x, $y); $frame->Show( 1 ); return 1; } package MyFrame; use vars qw(@ISA); use strict; # # All we load here are constants used # to keep the image stretched to the dimensions of the window. # use Wx qw( wxWidth wxHeight wxLeft wxTop wxDefaultPosition wxDefau +ltSize wxID_CANCEL wxCentreX wxCentreY wxFRAME_NO_TASKBAR); use Wx::Event qw(:everything); # # Wx::Image loads the Image control and all of the Image handler +s. # use IO::File; use Wx::Event qw( EVT_LEFT_DOWN EVT_LEFT_DCLICK EVT_MOTION EVT_RIG +HT_DOWN ); @ISA=qw(Wx::Frame); #use Image::Magick; #use MIME::Base64; use Data::Dumper; sub new { my $class = shift; my $title = shift; my $pos = shift; my $size = shift; my $filename = shift; #wxFrame(wxWindow* parent, wxWindowID id, const wxString& titl +e, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDef +aultSize, long style = wxDEFAULT_FRAME_STYLE, const wxString& name = +"frame") my $this = $class->SUPER::new( undef, -1, $title, $pos, $size, + wxFRAME_NO_TASKBAR ); my $file = IO::File->new( $filename, "r" ); unless ($file) {print "Can't load file."; return undef}; binmode $file; my $handler = Wx::JPEGHandler->new(); my $imgdata = Wx::Image->new(); $handler->LoadFile( $imgdata, $file ); # ?????????? # my $imgquick = Image::Magick->new(); #open(IMAGE, 'quote.png'); #binmode(IMAGE); #my $imageData = $img->Read(file=>\*IMAGE); # my $imageData = $imgquick->Read('K:\222.jpg'); #close(IMAGE); # $imgquick->BlobToImage($imageData); # $imgquick->set(magick=>'bmp'); # $imgquick->Scale(geometry=>'200x200'); # my $imgdata2 = $imgquick->ImageToBlob(); #$imgdata->SetData($imgdata); my $a = $imgdata->Rescale(200,200); #print Dumper($imgquick); my $bmp = Wx::Bitmap->new($imgdata); if( $bmp->Ok() ) { # create a static bitmap called ImageViewer that displays + the # selected image. my $img = Wx::StaticBitmap->new($this, -1, $bmp); my $size = $img->GetSize(); $img->SetBitmap($bmp); #if ($parent->{ScaleImage}){$img->SetSize($size)}; #$parent->Clear(); $img->Refresh; EVT_LEFT_DOWN ($img, sub { &evtLeftDown($this, @_) } ); EVT_RIGHT_DOWN ($img, sub { &evtRightDown($this, @_) } ); EVT_LEFT_DCLICK ($img, sub { &evtRightDown($this, @_) } ); EVT_MOTION ($this, \&evtMotion); $this->{'ScaleImage'} = 0; $this->SetAutoLayout( 1 ); # allow wxperl to manage contr +ol sizing & placement } #EVT_MOTION ($this, \&evtMotion); return $this; # return the frame object to the calling applic +ation. } sub evtLeftDown { my ($this, $img, $event) = @_; if ($event->LeftIsDown()) { my ($x, $y) = $event->GetPositionXY(); $this->{'mouse_x'} = $x; $this->{'mouse_y'} = $y; } } sub evtMotion { my ($this, $event) = @_; if ($event->LeftIsDown()) { my ($real_x, $real_y) = $this->GetPositionXY(); my ($x, $y) = $event->GetPositionXY(); my $old_x = $this->{'mouse_x'}; my $old_y = $this->{'mouse_y'}; my $x_diff = $x - $old_x; my $y_diff = $y - $old_y; $this->Move($real_x + $x_diff, $real_y + $y_diff); } } sub evtRightDown { my ($this, $img, $event) = @_; $this->Destroy(); }
Ok, run the "testWX.pl" script with the "imageHandling.pl" in the same dir. (and yea, you have to change the path to some jpg image at $app->newImageFrame( <here> ...); Two images should pop up. Now, either doubleclick on those and/or rightclick on them. They are now destroyed, and the mainloop2 is ran again... However, now it crasches! Why?

Actually, this wasnt the main problem I tried to solve doing this testexample, but rather to make the _thread_for_images thread able to create and recreate the "show images" part. As it is now, I use forking, which seems to be rather ok working.. although not 100% as you can see. testThread is supposed to simulate some work done by user, and put a path in the queue. The second thread _thread_for_images is then waking up by my ($full_path) = $thread_queue->dequeue, and is then supposed to show images. That is, user shouldnt have to interact with the images at all (like close them), but the _thread_for_images should be able of destroying and reshow (new) images when the queue is set. Anyone here that can help me out here making this happen? The main problem is that the I have to call ->MainLoop to show the images. Then that thread is locked until the gui is gone... Else it would be nice to somehow add/remove images from the gui without having to shut the mainloop down...

Also, if I put the loading in imageHandling code outside the sub, I cant even load the image from the imageHandling object! Why?

Note: Although the "imageHandling.pl" aint 100% done yet (is supposed to have more functions and such), it shouldnt have to be modified to make this all work. Note also that you can move the images by dragging them! :)

And if someone feels like it, how to make the imageHandling work with imagequick? (i have some code for it there, but I couldnt make it show the image, although it worked in Tk)

Thanks,
Ace

Replies are listed 'Best First'.
Re: ReShow images without user interact with the GUI.
by zentara (Cardinal) on Feb 04, 2006 at 14:03 UTC
    Sorry I can't help you with Wx, but since Wx is based on Gtk2, I will tell you that the thread handling in Gtk2 has changed substantially over the last year. So you might have better luck making sure you have the latest versions of Wx and the underlying Gtk2 libs.

    The recent Gtk2 releases have

    use Gtk2 qw/-init -threads-init/; #this line causes the warning, but dosn't die die "Glib::Object thread safetly failed" unless Glib::Object->set_threadsafe (TRUE);
    You also have the ability to use $thread->enter and $thread->leave to share Gtk2 objects across threads. Now, this may not have anything to do with your problem, but it goes to show that thread support is evolving fast, and that you should be sure what "level of Gtk2 libs" your Wx is built with, and write code accordingly.

    You just can't use threads like they are some "improved form of forking with shared data", and expect everything to work, especially in a higher level abstraction layer like Wx.


    I'm not really a human, but I play one on earth. flash japh
      Wx (wxPerl) is based on wxWidgets. wxGTK is a port of wxWidgets using the GTK+ library.
        That's true, but if you just compile wxWidgets with the default options(on linux, unix), and you have Gtk2 installed ( which most systems do), it will be built as the Gtk2 port. So it is highly likely that most wxPerl's that get compiled, are using the Gtk2 libs, unless they were specially compiled.

        I don't know what the windows precompiled binaries are based on, but it may be gtk2 also.


        I'm not really a human, but I play one on earth. flash japh
Re: ReShow images without user interact with the GUI.
by Anonymous Monk on Feb 04, 2006 at 10:18 UTC
    perlthrtut
    Thinking of mixing fork() and threads? Please lie down and wait until the feeling passes-- but in case you really want to know, the semantics is that fork() duplicates all the threads. (In UNIX, at least, other platforms will do something different.) Similarly, mixing signals and threads should not be attempted. Implementations are platform-dependent, and even the POSIX semantics may not be what you expect (and Perl doesn't even give you the full POSIX API).
      Hmm, ok, back to just threads then...

      Removing everything that got to do with forking from _thread_for_images and adding:
      threads->create( \&mainloop2() )->join;
      didnt help much since I get:
      thread failed to start: Usage: threads::new(classname, function_to_call, ...) at ...
      at the SECOND time it tries to create the thread! Which means, it works once! (so why not complaining then?)
Re: ReShow images without user interact with the GUI.
by Anonymous Monk on Feb 04, 2006 at 10:10 UTC
    Also, if I put the loading in imageHandling code outside the sub, I cant even load the image from the imageHandling object! Why?
    What code? What sub?
      I mean that I take this out from the mainloop2 sub:
      if (-e "imageHandling.pl") { my $app = do "imageHandling.pl"; if (!defined($app)) { die "imageHandling not loaded.\n"; } } else { print "imageHandling.pl not found.\n"; exit; }
      and put it "globally".
        and put it where?
Re: ReShow images without user interact with the GUI.
by Ace128 (Hermit) on Feb 06, 2006 at 14:45 UTC
    Well, I ended up using a Wx::Timer instead. Dont like it that much, but isnt that bad. Just a few extra instructions (to check if there is something in the queue) every some 100 milliseconds...