Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Screen Capture via wxPerl Script

by jmlynesjr (Deacon)
on Oct 19, 2012 at 00:52 UTC ( [id://999835]=perlquestion: print w/replies, xml ) Need Help??

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

Solved - See Update Below. Also added readmore tags to first example.

Alert: Cross-posted to wxPerl_users@perl.org

I recently found a 2003 wxPerl script for taking screen shots here on Perl Monks. After restructuring and cleaning up several bugs, I have it working. However, the results are inconsistent. The entire screen is not captured every time it's run. I know that events and "main-line code" run asynchronously. What am I missing? In addition, is there a way to select a specific window among the many to capture. I had to force the example window to the top the get it captured. Otherwise the lower level windows get captured. Code listed below...

Thanks, James

#! /usr/bin/perl # # Capture Screen to a file. Written in wxPerl. Tested on Citrus Perl 5 +.16 and wxWidgets 2.8.x. # # This is a hard coded example that needs to be generalized +into a package. # # There seems to be a race condition between getting the sam +ple written to the # screen and capturing the screen to the output file. Someti +mes it captures an # incomplete screen. The sample screen does not paint smooth +ly. It paints in two segments. Jerky. # It makes a difference where the Update and TakeScreenShot +calls # are placed(see comments below). Why???? # # Reference: GetScreenShot C++ code page 139 of "The wxBook" - # Cross-Platform GUI Programming with wxWidgets - # Smart, Hock, and Csomor # # Original author: "PodMaster" from Perl Monks-2003 (no idea of real i +dentity-not active for 6+ years) # # Modified by: James M. Lynes. Jr. # Modified Date: October 17, 2012 # # use strict; use warnings; use lib '/home/pete/CitrusPerl/perl/vendor/lib/GD/Graph'; #where colour.pm is found in Citrus Perl distro use Wx qw( :everything ); use colour qw( :everything ); use Wx::Event qw( EVT_PAINT ); # # Main Application # my $app = Wx::SimpleApp->new; my $frame = Wx::Frame->new(undef, -1, "Screen Capture", wxDefaultPosit +ion, wxDefaultSize , wxDEFAULT_FRAME_STYLE | wxMAXIMIZE | wxSTAY_ON_TOP); EVT_PAINT( $frame, \&onPaint); our $file = text_entry_dialog($frame); # get the output filename $frame->Show; #$frame->Update; # doesn't work to h +ave these calls in main loop????? why??? #TakeScreenshot($frame, $file); # ditto - they must be i +n onPaint sub??? $app->MainLoop; # # Copy the screen to the output file-similar to wxBook pg 139 example. # sub TakeScreenshot { my($self, $cfile ) = @_; my $screen = Wx::ScreenDC->new(); my( $x, $y) = $screen->GetSizeWH(); my $bitmap = Wx::Bitmap->new($x,$y,-1); my $memory = Wx::MemoryDC->new(); $memory->SelectObject( $bitmap ); $memory->Blit(0,0,$x,$y, $screen, 0, 0); + # copy the screen to the bitmap $bitmap->SaveFile( $cfile , wxBITMAP_TYPE_BMP ) ; # copy th +e bitmap to the output file } # # Ask for the output filename # sub text_entry_dialog { my( $self ) = @_; my $dialog = Wx::TextEntryDialog->new ( $self, "Enter the Screen Capture output filename\n(Cancel will u +se the default filename shown below)\n", "Screen Capture File Entry", "capture.bmp", wxOK | wxCANCEL ); my $textvalue = "capture.bmp"; if( $dialog->ShowModal == wxID_OK ) { $textvalue = $dialog->GetValue; } $dialog->Destroy; return $textvalue; } # # Generate the sample graphic screen to capture # sub onPaint{ my($self,$event)=@_; my $screen = Wx::PaintDC->new($self); $screen->SetBackgroundMode( wxTRANSPARENT ); $screen->SetFont(Wx::Font->new( 12, wxFONTFAMILY_ROMAN, wxNORMAL, wxBOLD)); for(0..17){ my $c = $_ * 15; $screen->SetTextForeground( Wx::Colour->newRGB(0,0,$c) +); $screen->DrawRotatedText("wxPerl",100 ,100 ,$c); } $screen->DrawRotatedText("wxPerl and PodMaster",000 ,4 +00 ,0); $screen->DrawRotatedText("are messing up your screen", +000 ,450 ,0); for(0..17){ my $c = $_ * 15; $screen->SetTextForeground( Wx::Colour->newRGB(0,$c,0) +); $screen->DrawRotatedText("wxPerl",200 ,200 ,$c); } $screen->DrawRotatedText("wxPerl and PodMaster",100 ,5 +00 ,0); $screen->DrawRotatedText("are messing up your screen", +100 ,550 ,0); for(0..17){ my $c = $_ * 15; $screen->SetTextForeground( Wx::Colour->newRGB($c,0,0) +); $screen->DrawRotatedText("wxPerl",300 ,300 ,$c); } $screen->DrawRotatedText("wxPerl and PodMaster",200 ,6 +00 ,0); $screen->DrawRotatedText("are messing up your screen", +200 ,650 ,0); $self->Update; # Force a screen repaint before capt +ure to make # sure all items are written to the screen TakeScreenshot($self, $file); # Capture the screen }
Update->Solved

Clipping and jerky screen painting was found to be caused by the frame being defined smaller(wxDefaultSize) than the sample graphic. Defining the frame to be (1024x768) cleans up the screen paint and captures the entire sample screen. Also the code flow now makes sense.

#! /usr/bin/perl # # Capture Screen to a file. Written in wxPerl. Tested on Citrus Perl 5 +.16 and wxWidgets 2.8.x. # # This script draws and captures a sample graphic. # Take_Screenshot needs to be generalized into a package # that can be inserted into any application. # # To capture the complete sample graphic, the frame size had to + be set to the screen size # of [1024x768]. Using wxDefaultSize caused the screen to paint + in two passes # and the capture to clip. # # Reference: GetScreenShot C++ code page 139 of "The wxBook" - # Cross-Platform GUI Programming with wxWidgets - # Smart, Hock, and Csomor # # Original author: "PodMaster" from Perl Monks-2003 (no idea of real i +dentity-not active for 6+ years) # # Modified by: James M. Lynes. Jr. # Last Modified Date: October 20, 2012 # # use strict; use warnings; use Wx qw(:everything); use Wx::Event qw( EVT_PAINT ); # # Main Application # my $app = Wx::SimpleApp->new; my $frame = Wx::Frame->new(undef, -1, "Screen Capture Example", [0,0], + [1024,768] , wxDEFAULT_FRAME_STYLE); EVT_PAINT( $frame, \&onPaint); my $file = file_entry_dialog($frame); $frame->Show; Take_Screenshot($frame, $file); $app->MainLoop; # # Generate a sample graphic screen to capture # sub onPaint{ my($self,$event)=@_; my $screen = Wx::PaintDC->new($self); $screen->SetBackgroundMode( wxTRANSPARENT ); $screen->SetFont(Wx::Font->new( 12, wxFONTFAMILY_ROMAN, wxNORMAL, +wxBOLD)); for(0..17){ my $c = $_ * 15; $screen->SetTextForeground( Wx::Colour->newRGB(0,0,$c) +); $screen->DrawRotatedText("wxPerl",100 ,100 ,$c); } $screen->DrawRotatedText("wxPerl and PodMaster",000 ,4 +00 ,0); $screen->DrawRotatedText("are messing up your screen", +000 ,450 ,0); for(0..17){ my $c = $_ * 15; $screen->SetTextForeground( Wx::Colour->newRGB(0,$c,0) +); $screen->DrawRotatedText("wxPerl",200 ,200 ,$c); } $screen->DrawRotatedText("wxPerl and PodMaster",100 ,5 +00 ,0); $screen->DrawRotatedText("are messing up your screen", +100 ,550 ,0); for(0..17){ my $c = $_ * 15; $screen->SetTextForeground( Wx::Colour->newRGB($c,0,0) +); $screen->DrawRotatedText("wxPerl",300 ,300 ,$c); } $screen->DrawRotatedText("wxPerl and PodMaster",200 ,6 +00 ,0); $screen->DrawRotatedText("are messing up your screen", +200 ,650 ,0); } # # Copy the screen to the output file-similar to wxBook pg 139 example. # sub Take_Screenshot { my($self, $file ) = @_; $self->Refresh; # without Refresh and Updat +e $self->Update; # the underlying window is c +aptured my $screen = Wx::ScreenDC->new(); my( $x, $y) = $screen->GetSizeWH(); my $bitmap = Wx::Bitmap->new($x,$y,-1); my $memory = Wx::MemoryDC->new(); $memory->SelectObject( $bitmap ); $memory->Blit(0,0,$x,$y, $screen, 0, 0); # copy the scr +een to the bitmap $bitmap->SaveFile( $file , wxBITMAP_TYPE_BMP ) ; # copy the + bitmap to the output file } # # Ask for the output filename # sub file_entry_dialog { my( $self ) = @_; my $textvalue = "capture.bmp"; my $dialog = Wx::TextEntryDialog->new ( $self, "Enter the Screen Capture output filename\n(Cancel will u +se the default filename shown below)\n", "Screen Capture Output File Entry", $textvalue, wxOK | wxCANCEL ); if( $dialog->ShowModal == wxID_OK ) { $textvalue = $dialog->GetValue; } $dialog->Destroy; return $textvalue; }

Replies are listed 'Best First'.
Re: Screen Capture via wxPerl Script
by Anonymous Monk on Oct 19, 2012 at 08:57 UTC

    Hi

    Look at this, no mainloop, no windows, before and after screenshot, and optional prompt for filename

    I know that events and "main-line code" run asynchronously.

    No they don't.

    In addition, is there a way to select a specific window among the many to capture.

    Not with stock wxWidgets, since its all about windows your app creates. There is probably an extension you could wrap, or you could use Imager::Screenshot / Win32::GuiTest / X11::WindowHierarchy

    However, the results are inconsistent. The entire screen is not captured every time it's run. I had to force the example window to the top the get it captured. Otherwise the lower level windows get captured.

    That seems to be the what Re: capture what's on the screen was designed to do. Ooops, I just cited the source :)

    It makes a difference where the Update and TakeScreenShot calls are placed(see comments below). Why????

    Because an Update is not Refresh, and doesn't necessarily trigger any wxPaintEvent ...

    After restructuring and cleaning up several bugs, I have it working.

    They probably weren't bugs in 2003 :) ( see wxPerl image handling (short & sweet). )

    Also, what you posted does not compile, besides extra newlines in your comments, there is odd  use colour qw( :everything );

    Also, CUFP is for exibition, SOPW is for questions :) see Where should I post X?

    Cheers

Re: Screen Capture via wxPerl Script
by jmlynesjr (Deacon) on Oct 19, 2012 at 20:46 UTC

    Every post is a learning experience. "AM" thank you very much for your time and comments. I am always amazed at the time and effort donated by the Monks in answering questions.

    Noted: I should have posted to SoPW rather that CUFP. I flipped a coin and picked the wrong place. I also should have linked to the original post and author. I got lazy and didn't look up how to do that.

    The original script did not display the sample graphics in the frame. Thus my changes to the sample graphic code to get it to draw. "Bug" was the wrong word to use as was pointed out. The libraries certainly changed since 2003.

    I saw the posts relating to the Win32 modules, but my goal is exploring wxPerl, so I didn't persue that option.

    I saw a mention last night in the wxWidgets documentation to using Refresh before Update to force a screen repaint. Trying that in my original code worked differently, but not as I hoped. More reading is required.

    My original post was cut and pasted from my wxperl_users email and must have gotten scrambled in the translation. Maybe that was the source of your compile problems. I have learned to post from the original file in the future, sorry.

    Too bad wxWidgets can't select a specific window, but the explanation made sense.

    I'm not informed enough to argue the wxPerl async issue, but I thought that was the purpose and function of event driven programming. It is in the real-time world I come from.

    When I run your suggested code, the sample graphic is still not being drawn and I get the following warning:

    (PM-ScreenCapture.pm:1799): Gtk-WARNING **: Tried to set the file choo +ser to multiple selection mode, but this is not allowed in SAVE or CR +EATE_FOLDER modes. Ignoring the change and leaving the file chooser +in single selection mode. saved /home/pete/Projects/perlprojects/CitrusPerl.proj/screenshot.png saved /home/pete/Projects/perlprojects/CitrusPerl.proj/messup.png

    I used the following structure from man Wx, but I really like your structure and plan to plagiarize it in the future! :)

    use Wx; my $app = Wx::SimpleApp->new; my $frame = Wx::Frame->new( undef, -1, 'Hello, world!' ); $frame->Show; $app->MainLoop;

    I considered using the file dialog, but went with the text entry dialog as a short cut. The file dialog is the better way to go.

    The original script used Wx::Colour which was not found in the "standard" Citrus Perl @INC, but was found in a deeper directory. Thus the use lib '......' to point to the module.

    Thanks again for the input. I have a lot to continue working with.

    James

      The original script used Wx::Colour which was not found in the "standard" Citrus Perl @INC, but was found in a deeper directory. Thus the use lib '......' to point to the module.

      What you found was GD::Colour which is not at all the same thing. Wx::Colour used to be a seperate module, it isn't anymore. use Wx; gets you Wx::Colour

        Oops! That could make a difference...Need to change that and see what changes. Thanks again! ---James

      I'm not informed enough to argue the wxPerl async issue, but I thought that was the purpose and function of event driven programming. It is in the real-time world I come from.

      Well, I really don't know what you mean by events and "main-line code" run asynchronously. I took it to mean you're talking about the perl code, and since it doesn't use fork or threads, doesn't open any sockets or files, look all synchronous to me

        We may be talking apples and oranges, but I am enjoying and learning from this conversation.

        My definition of "main-line code" is the stuff between my $app = Wx::SimpleApp->new; and $app->MainLoop;.

        Within this segment, the frame is created and the onPaint event handler is defined. onPaint is never called directly, but is called "asynchronously" from within the bowels of Wx. I moved the TakeScreenshot($self, $file); call to various places within the main-line and onPaint segments with various results. Usually only capturing the background window not the sample window.

        The sample screen is drawn by a continuous block of code(within the onPaint event handler)immediately followed by TakeScreenshot. On my PC, the screen doesn't paint completely in one motion, but paints the three circles and the first block of text followed by a blink and the next two blocks of text. Given this, it is strange that some of the time a complete screen is captured to the file. Mostly only the three circles and first block of text gets captured. All the drawing code runs before the capture code is called, so the $64,000 question is why is only a partial screen being captured(most of the time)? As Spock would say, "illogical".

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://999835]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (6)
As of 2024-04-20 02:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found