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

Hi Guys,

I've just wrapped wxRichTextPrintout and I'm using it to handle a print preview more precisely. I'm using Kubuntu.

The printpreview works fine, but when I press the "close" button in preview, the PreviewFrame does not close and I get:

"*** glibc detected *** /usr/bin/perl: double free or corruption (!pr +ev): 0x000000000261cb80 ***"

The book says that "close" and "destroy" should both be handled automatically.  Code follows.

You'll need to have the most recent version of wxPerl to show this.

I would be grateful for your thoughts.

Regards

Steve

#!/usr/bin/perl -w -- use Wx 0.15 qw[:allclasses]; use strict; package MyFrame; use Wx qw[:everything]; use base qw(Wx::Frame); our $gl_installation_path = ''; our $gl_i_frame_ptr; sub new { my( $self, $parent, $id, $title, $pos, $size, $style, $name ) = @_ +; my %RTC_Buttons = ( preview=>1, ); $style = wxDEFAULT_FRAME_STYLE unless defined $style; $self = $self->SUPER::new( undef, wxID_ANY, "Rich Text Control", w +xDefaultPosition, wxDefaultSize, $style, "" ); $self->SetTitle("Rich Text Control"); $self->{sizer} = Wx::BoxSizer->new( wxVERTICAL ); my $i_RTC_Panel=RichText_Obj->new($self, "first panel", \%RTC_Butt +ons,[800, 300]); $self->{sizer}->Add( $i_RTC_Panel, 1, wxGROW|wxALL, 5 ); $self->SetSizerAndFit( $self->{sizer} ); $self->Layout(); return $self; } package RichText_Obj; use Wx qw[:everything]; sub new{ my ($class, $parent, $name, $buttons_ptr, $size) = @_; $size = wxDefaultSize unless defined $size; my $i_RTC_panel = Wx::Panel->new( $parent, wxID_ANY, wxDefaultPosi +tion, wxDefaultSize, wxTAB_TRAVERSAL, "" ); $i_RTC_panel->{Ctl_Post_Exam_Report_Szr_1} = Wx::BoxSizer->new(wxV +ERTICAL); # Top level in RTC Object; $i_RTC_panel->{Ctl_Post_Exam_Report_Szr_2} = Wx::BoxSizer->new(wxH +ORIZONTAL); # Button bar for RTC Object; $i_RTC_panel->{name} = $name; # General Parameters my @loc_parameters_arr; $#loc_parameters_arr=0; # Remove el +ements from array. @loc_parameters_arr = ("", "", 1, "", -1, -1, "", "", \$i_RTC_panel->{Ctl_Post_Exam_Report_Szr_2}, \$i_RTC_panel, ""); $loc_parameters_arr [12]= ''; $loc_parameters_arr [13]= ''; # Preview Button if ($$buttons_ptr{preview}){ $loc_parameters_arr [0]= \$i_RTC_panel->{Ctl_Report_Preview_Te +xt_Btn}; # Button name, passed by reference $loc_parameters_arr [7]= (("Preview report.")); + # Tooltip $loc_parameters_arr [10]= ("Images/wordproccess/16px-Document- +print-preview.png"); # Bitmap path $loc_parameters_arr [11]= (\&on_click_richtext_preview); # __new_BitmapButton(@loc_parameters_arr); } $i_RTC_panel->{Ctl_Report_Text_Txt} = Wx::RichTextCtrl->new( $i_RT +C_panel, -1, '', [-1, -1], $size ); $i_RTC_panel->{Ctl_Report_Text_Txt} -> SetBackgroundColour(wxWHITE +); $i_RTC_panel->{Ctl_Report_Text_Txt} -> SetForegroundColour(wxBLACK +); $i_RTC_panel->{Ctl_Post_Exam_Report_Szr_1}->Add($i_RTC_panel->{Ctl +_Post_Exam_Report_Szr_2}, 0, 0, 0); $i_RTC_panel->{Ctl_Post_Exam_Report_Szr_1}->Add($i_RTC_panel->{Ctl +_Report_Text_Txt}, 0, 0, 0); my $XML_handler = Wx::RichTextXMLHandler->new; my $HTML_handler = Wx::RichTextHTMLHandler->new; my $RTC_Buffer = $i_RTC_panel->{Ctl_Report_Text_Txt}->GetBuffer(); $RTC_Buffer ->AddHandler($XML_handler); $RTC_Buffer ->AddHandler($HTML_handler); $i_RTC_panel->{printing}= Wx::RichTextPrinting->new('Wx::Demo +Printing', $i_RTC_panel); $i_RTC_panel->{printout}= Wx::RichTextPrintout->new('Wx::Demo +Printing'); $i_RTC_panel->{printout}->GetRichTextBuffer(); $i_RTC_panel->{printout}->GetDC; $i_RTC_panel->SetSizerAndFit( $i_RTC_panel->{Ctl_Post_Exam_Report_ +Szr_1} ); $i_RTC_panel->Layout(); return $i_RTC_panel; } sub __new_BitmapButton{ # sets field properties, format: # __new_Button(\$loc_self_ptr, "", $loc_enabled, "", $loc_minsize_x, $ +loc_minsize_y, "", $loc_tooltip, \$loc_sizer_ptr, \$loc_panel_ptr, (( +"Label")); my $loc_bcur = Wx::BusyCursor->new; # Creat +e hourglass within current scope. Hourglass is deleted on exit. my $loc_self_ptr=shift; # pointer t +o control (Pass by reference) my $loc_ID=shift; # ID u +sed to over-ride wxID_ANY my $loc_enabled=shift; # Enab +led or disabled my $loc_max_length=shift; # Not +used, set to null my $loc_minsize_x=shift; # Hold +s minimum size of control (x-axis) my $loc_minsize_y=shift; # Hold +s minimum size of control (y-axis) my $loc_validation_string=shift; # Not +used, set to null my $loc_tooltip=shift; # Hold +s tooltip already translated. my $loc_sizer_ptr=shift; # pointer to s +izer (Pass by reference) my $loc_panel_ptr=shift; # pointer to p +anel (Pass by reference) my $loc_control_value=shift; # Labe +l for button or bitmap my $loc_button_event_ptr=shift; # E +vent (pointer.) my $loc_gbposition_ptr=shift; # Gri +dbag position details (pointer.) my $loc_gbspan_ptr=shift; # Gri +dbag span details (pointer.) my $bitmap_path = $gl_installation_path.$loc_control_value; if( $loc_ID eq ""){ if ($gl_installation_path ne ""){ $$loc_self_ptr = Wx::BitmapButton-> new($$loc_panel_ptr, wxID_ANY, Wx::Bitmap->new($bitmap +_path, wxBITMAP_TYPE_ANY), wxDefaultPosition, [16,16], ); } else { $$loc_self_ptr = Wx::Button-> new($$loc_panel_ptr, wxID_ANY, "Preview", wxDefaultPos +ition, wxDefaultSize, ); } } else{ $$loc_self_ptr = Wx::Button-> new($$loc_panel_ptr, $loc_ID, __trim($loc_control_value), wxDe +faultPosition, wxDefaultSize, ); } if ($loc_enabled == 1){ + # Set Enable according to parameter. $$loc_self_ptr->Enable(1); } else{ $$loc_self_ptr->Disable(); } if ($loc_minsize_x ne "") { # Set minim +um physical size of field in pixels $$loc_self_ptr->SetMinSize(Wx::Size->new($loc_minsize_x, $loc_ +minsize_y)) }; $$loc_self_ptr->SetSize($$loc_self_ptr->GetBestSize()); if ($loc_tooltip ne "") {$$loc_self_ptr->SetToolTipString($loc_too +ltip)}; #Set tooltip if (defined $loc_gbposition_ptr and $loc_gbposition_ptr ne '') { + # Add to GridBag Position and Span if (defined $loc_gbspan_ptr and $loc_gbspan_ptr ne '' ) { + # Add to GridBag Position $$loc_sizer_ptr->Add($$loc_self_ptr, $$loc_gbposition_ptr, $$l +oc_gbspan_ptr); # Add to grid } else { $$loc_sizer_ptr->Add($$loc_self_ptr, $$loc_gbposition_ptr); # +Add to grid } } else { $$loc_sizer_ptr->Add($$loc_self_ptr, 0, 0, 0); +# Add to grid } if (defined $loc_button_event_ptr) { # Set +button press event if ($loc_button_event_ptr ne "") { # Se +t button press event Wx::Event::EVT_BUTTON( $$loc_panel_ptr , $$loc_self_ptr, $loc_ +button_event_ptr ); } }; } sub on_click_richtext_preview{ my($self) = @_; # Set up header and footer. my $pv = $self->{printing}; $pv->SetHeaderText('Wx::Demo Richtext Printing', wxRICHTEXT_PAGE_ +ALL, wxRICHTEXT_PAGE_LEFT); $pv->SetFooterText('Page @PAGENUM@ of @PAGESCNT@', wxRICHTEXT_PAG +E_ALL, wxRICHTEXT_PAGE_RIGHT); $pv->SetShowOnFirstPage(0); # we can only set header / footer margins using wxRichTextHeaderFo +oterData my $hfdata = $pv->GetHeaderFooterData; $hfdata->SetMargins(100,100); $pv->SetHeaderFooterData($hfdata); # Create Printout Object my $po = $self->{printout}; $po->SetRichTextBuffer($self->{Ctl_Report_Text_Txt}->GetBuffer()); # Create Preview object my $preview = Wx::PrintPreview->new($po,$po,$pv->GetPrintData); $preview ->SetZoom(200); my $frame = Wx::PreviewFrame->new( $preview, $self, "Printing Demo + Preview", wxDefaultPosition,[1360,768],wxNO_BORDER); $frame->Initialize(); my $state = $frame->Show(1); # Destroy $frame; #$pv->PreviewBuffer($self->{Ctl_Report_Text_Txt}->GetBuffer()); } package main; unless(caller){ local *Wx::App::OnInit = sub{1}; my $app = Wx::App->new(); Wx::InitAllImageHandlers(); my $frame_1 = MyFrame->new(); $app->SetTopWindow($frame_1); $frame_1->Show(1); $app->MainLoop(); }

Replies are listed 'Best First'.
Re: wxPreviewFrame error on closing
by zentara (Cardinal) on Aug 30, 2010 at 10:51 UTC
    I don't have a running Wx available, but it sounds similar to a basic fine point in Gtk2. Since it's likely that your Wx build is based on the Gtk2 libs, I will mention it. You may have some confusion between the signal handlers between the "destroy" and "delete_event". An explanation by muppet is below.
    > my $window => Gtk2::Window -> new; > $window -> signal_connect ( 'delete_event', sub { Gtk2 -> main_quit; + } ); Connect this to 'destroy' instead of 'delete-event'. The handler for 'delete-event' is supposed to return a boolean value saying "i handled + this" or "i didn't handle this", that is typically used to inhibit destructi +on of the window. You're not doing that, though. The default action from 'delete-event' is to destroy the window, which will cause the 'destroy +' event to fire. Of course, you can also get 'destroy' from other places in y +our program. So, if you connect the quit of the main loop to the main win +dow's 'destroy' rather than 'delete-event', you get a more robust program.
    and here is a Gtk2 example which demonstrates the difference between delete and destroy. The delete_event is what is connected to the window manager's Close button. It sounds like you may need to intercept one of those signals in Wx.
    #!/usr/bin/perl use warnings; use strict; use Gtk2 -init; my $window = Gtk2::Window->new; $window->add( Gtk2::Label->new("I'm a banana!") ); $window->show_all; $window->signal_connect( delete_event => sub { return !ask( $_[0], "Really quit?" ); } ); $window->signal_connect( destroy => sub { Gtk2->main_quit } ); Gtk2->main; sub ask { my ( $parent, $question ) = @_; my $msgbox = Gtk2::MessageDialog->new( $parent, [], 'question', 'yes-no', $qu +estion ); my $response = $msgbox->run(); $msgbox->destroy; return $response eq 'yes'; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh

      Hi flash japh,

      Thanks so much for your response.

      I guess it's possible that this is the case, but if so it's outside my remit and I'd have to post it to the wxWidgets website. The destroy is handled automatically by the underlying wxWidgets c-code. wxPreviewFrame works fine with a wxPrintout object but not with a wxRichTextPrintout object - or at least my version of it.

      I guess I'd have to write a test to prove that your theory one way or the other. ANy ideas how I might do this?

      Thanks again.

      Have a good day.

      Steve

        ANy ideas how I might do this?

        Sorry, I don't do well with the OO style of Wx. The Wx maillinglist is probably your best bet.


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: wxPreviewFrame error on closing
by Anonymous Monk on Aug 29, 2010 at 20:55 UTC
    please try again, your copy/paste job got wrapped, its not valid perl anymore

      Whoops, sorry.

      Fixed now.

      Steve

Re: wxPreviewFrame error on closing
by Anonymous Monk on Sep 02, 2010 at 09:22 UTC

    I realize the problem with your code is that you're trying to share your printout object between your wxRichTextPrinting and wxPreviewFrame, and the docs confirm it

    So you'll probably have to subclass wxRichTextPrinting like
    BEGIN { package MyRichTextPrintout; use base qw[ Wx::RichTextPrintout ]; sub new { my ( $self, $buffer ) = @_; $self = $self->SUPER::new(); $self->SetRichTextBuffer($buffer); return $self; } sub DESTROY { $_[0]->SetRichTextBuffer(undef); return; } } sub on_click_richtext_preview { my ($self) = @_; my $preview = Wx::PrintPreview->new( MyRichTextPrintout->new( $self->{Ctl_Report_Text_Txt}->GetBuff +er() ), MyRichTextPrintout->new( $self->{Ctl_Report_Text_Txt}->GetBuff +er() ), ); $preview->SetZoom(200); my $frame = Wx::PreviewFrame->new( $preview, $self, "Printing Demo + Preview", wxDefaultPosition, [ 1360, 768 ], wxNO_BORDER ); $frame->Initialize(); my $state = $frame->Show(1); }
    I hope this works for you. If it doesn't you should use strace (or grind) to try to track down where the error occurs

    I gave it a shot but my wxwidgets/wxperl crashes after clicking preview (the window shows up and then the crash window on top of it), and I don't have a strace equivalent handy.

    I couldn't compile wxPerl against Alien::wxWidgets::Config::msw_2_9_0_uni_gcc_3_4, maybe I'll try 2.8.11 some time later

    Good luck

      Hi Anon,

      I'd like to thank you for the amazing amount of effort you've put into helping me with this. I'm very touched and I'm sorry I've only just noticed your post today. And then you post all your work under under "Anonymous"!

      Your code crashes at about the same time mine does (mine actually crashes on "EXIT" of preview), althought I don't get the error messgaes you do. Mine is Karmic Kubuntu and I think yours is Windows, so that may account for the difference. However, your error messages are very useful. I did try to run it on windows, but my wxPerl installation there is corrupt. I need to reinstall sometime.

      I'm going to work through your points over the next few days and I'll keep this thread updated with my progress.

      So thanks again Anon.

      Regards

      Steve

      Hi Anon,

      Well, I've followed through some of your links. The first thing I noticed was that wxPreviewFrame::OnCloseWindow isn't wrapped for wxPerl (or at least it doesn't seem to be. A fact not noted in the documentation. So I created my own version:

      sub on_click_richtext_preview_close { my($self,$event) = @_; $self->Destroy; }

      And called it with:

      Wx::Event::EVT_CLOSE( $frame , \&on_click_richtext_preview_close ) +;

      just before the initialise. It Seems to work, but the main frame, to which it is returned, is disabled (in my version of the code). Can I just enable it, or is that too easy?

      Regards

      Steve

        The first thing I noticed was that wxPreviewFrame::OnCloseWindow isn't wrapped for wxPerl

        It doesn't make sense to wrap it

        http://svn.wxwidgets.org/svn/wx/wxWidgets/trunk/src/common/prntbase.cpp

        IMPLEMENT_CLASS(wxPreviewFrame, wxFrame) BEGIN_EVENT_TABLE(wxPreviewFrame, wxFrame) EVT_CLOSE(wxPreviewFrame::OnCloseWindow) END_EVENT_TABLE() ... void wxPreviewFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event)) { if (m_windowDisabler) delete m_windowDisabler; // Need to delete the printout and the print preview wxPrintout *printout = m_printPreview->GetPrintout(); if (printout) { delete printout; m_printPreview->SetPrintout(NULL); m_printPreview->SetCanvas(NULL); m_printPreview->SetFrame(NULL); } delete m_printPreview; Destroy(); }
        It Seems to work, but the main frame, to which it is returned, is disabled (in my version of the code). Can I just enable it, or is that too easy?

        You could do that, but as you can see above the wxWindowDisabler is deleted, which re-enables the previously disabled windows, so it should work already :)

      Hi Anon,

      I've just been over all your code again, because it seems like the nearest to a solution so far.

      I got to the same point as you, crashing on preview. It's the line my $state = $frame->Show(1);.

      I picked out your other bits and pieces and used them selectively, but I got no further than before where I could get the preview to show, but I have to capture the preview EVT_COSE command on exit and handle it manually.

      However, two more clues are:

      1. The Printing demonstration command in the demo also uses Wx::PreviewFrame (actually PlPreviewFrame but that doesn't work with this code either) and has no code at all to handle the exit - but it exits perfectly and returns control to the calling window.
      2. The $i_RTC_panel->{printout}->GetDC; which I put in for a test, doesn't fail with a "no such method" style error, but it doesn't return anything (which it should). Just a null instead of the pointer to the MemoryDC associated with the preview.

      So I wonder if the two are linked.

      Regards

      Steve.

        So I wonder if the two are linked.

        Maybe, but why wonder when you can find out? Fire up strace or debugger and find out exactly where the error occurs in c/xs code... then you can either fix it, or file the appropriate bug report