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

Hi,

I'm trying to create a simple two-stage GUI using Tk. The first box is for fetching a user selection. When the selection is made, I destroy the first box and create a second. I'm getting segfaults when processing input on the second box.

This is my first attempt at Tk so perhaps the approach is simply wrong or there's a better way. Any suggestions?

Thanks, Stefan

#!/usr/bin/perl -w use Tk; use strict; my $MW; my $Array_Index=""; MAIN: { debug("+MAIN"); my $my_ref; my $label ; my @file_references ; my $button ; my $i; $MW = MainWindow->new; $MW->title("test box 1"); $label = $MW -> Label(-text=>"Choose one option", -font=>"courierfont") -> pack; @file_references = ("Ref1", "Ref2", "Ref3", "Ref4"); $i=1; foreach $my_ref (@file_references) { $button = $MW -> Button(-text => "$my_ref", -command => [ \&do_button, "$my_ref,$i"])-> pack; $i++; } MainLoop(); debug("==MAIN - the first box has been closed"); if (length("$Array_Index") > 0) { $MW=MainWindow->new; $MW->OnDestroy([\&abort_routine]); $MW->title("test box 2"); $label = $MW -> Label(-text=>"This is the second box") -> pack; $label = $MW -> Label(-text=>"Index = $Array_Index", -font=>"courierfont") -> pack; $button = $MW -> Button(-text => "Shutdown and close", -command => [ \&abort_routine])-> pack(-side => 'bottom'); MainLoop(); } debug ("-MAIN"); } sub do_button { debug("+do_button"); my $params = shift; (my $ref, $Array_Index) = split (/,/,$params); debug("do_button Array_Index: $Array_Index"); # DO SOMETHING USEFUL HERE TO START A PROCESS $MW->destroy(); debug("-do_button"); } sub abort_routine { debug("+abort_routine"); debug("abort_routine Array_Index: $Array_Index"); # DO SOMETHING USEFUL HERE TO CLEAN UP THE PROCESS $MW->destroy(); debug("-abort_routine"); } sub debug { my @msg = shift; print @msg, "\n"; }

Replies are listed 'Best First'.
Re: Getting segfaults when destroying and recreating Tk box
by GrandFather (Saint) on Sep 05, 2007 at 00:17 UTC

    When I run that code I see a window with 4 buttons and text propmting me to "Choose on option". If I click any button I get a smaller window containing the text

    This is the second box Index = 1

    and a single button ("Shutdown and close"). At this point the console shows:

    +MAIN +do_button do_button Array_Index: 1 -do_button ==MAIN - the first box has been closed

    When I click the shutdown button the window closes and the following additional text is generated:

    abort_routine Array_Index: 1 +abort_routine abort_routine Array_Index: 1 -abort_routine -abort_routine -MAIN

    No errors nor unusual behavior occur. $Tk::VERSION is 804.027 and Perl -v reports 'This is perl, v5.8.7 built for MSWin32-x86-multi-thread'.


    DWIM is Perl's answer to Gödel
      Thanks for your comment....
      When I click the shutdown button the window closes and the following additional text is generated:
      abort_routine Array_Index: 1
      +abort_routine
      abort_routine Array_Index: 1
      -abort_routine
      -abort_routine
      -MAIN
      I get a different series of responses than you....

      Firstly when I run the code and the first box opens...

      +MAIN
      Then when I click on something in the first box...
      +do_button do_button Array_Index: 1 -do_button ==MAIN - the first box has been closed
      Then in the last box, when I click on the "shutdown and close" button...
      +abort_routine abort_routine Array_Index: 1 Segmentation fault
      Alternately, if I slimply click the close box, I instead see...
      Segmentation fault
      You see this is quite different from your response, ending in a segfault. But I notice you're using windows and I'm using Linux.

      I'm running Fedora 7, perl -v says "This is perl, v5.8.8 built for x86_64-linux-thread-multi", the Tk rpm which is installed is tk-8.4.13-5.fc7.src.rpm from the livna rpm repository.

      Is there any other info from me that would help work out what is going on?

        I suspect there is something nasty happening when you re-enter abort_routine. You could add:

        my $inAbort;

        at the top of the script and change abort_routine to:

        sub abort_routine { return if $inAbort; $inAbort = 1; debug("+abort_routine"); debug("abort_routine Array_Index: $Array_Index"); # DO SOMETHING USEFUL HERE TO CLEAN UP THE PROCESS $MW->destroy(); debug("-abort_routine"); }

        so that abort_routine is not re-entered during the destroy or (probably better) change the button command to initiate a different sub that just performs $MW->destroy ();:

        ... $button = $MW->Button( -text => "Shutdown and close", -command => [ \&do_close ] )->pack( -side => 'bottom' ); ... sub abort_routine { debug("+abort_routine"); debug("abort_routine Array_Index: $Array_Index"); # DO SOMETHING USEFUL HERE TO CLEAN UP THE PROCESS debug("-abort_routine"); } sub do_close { $MW->destroy(); }

        DWIM is Perl's answer to Gödel
Re: Getting segfaults when destroying and recreating Tk box
by zentara (Cardinal) on Sep 05, 2007 at 14:11 UTC
    Well your last script version dosn't segfault for me on linux, but I must tell you that you are thinking about Tk-programming the wrong way, and it is biting you. Why? You are destroying the mainloop, then trying to recreate it before it is destroyed. This is asking for weird problems with system timing, etc, and produces unpredictable results.

    NEVER write scripts that try to create-destroy-create a MainWindow. The MainWindow in Tk IS the eventloop. The eventloop MUST continue to run for proper operation, and trying to destroy it, with some code saying "restart yourself at death" is not going to be reliable.

    The way you approach your type of script, is to make one mainwindow,and then some "toplevel" windows to present information. You can destroy a toplevel window, without it affecting the mainwindow's eventloop; but repeated create/destroy cycles of a toplevel window, may cause a memory gain, so you should always design scripts to reuse windows...... don't destroy them, just withdraw them, then repack them with different widgets.

    Here is a way to use your code .

    and a better way to do it


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Arghhh. Thanks for the tip. It all makes sense now and indeed, the segmentation faults disappear when following your suggested strategy of avoiding destroys.

      I note that if you ever destroy a TopLevel window, you eventually provoke another segmentation fault. However if you withdraw the TopLevel windows instead of destroying them, you can go on seemingly indefinitely by withdrawing the old TopLevel window and then defining a subsequent TopLevel window.

      Then to exit the MainLoop gracefully, you destroy the original (hidden and withdrawn) MainWindow.

      Thanks,
      Stefan

        I note that if you ever destroy a TopLevel window, you eventually provoke another segmentation fault.

        That dosn't sound right. You may have a Tk version that needs patching, or you are doing something weird like trying to access a widget packed in the toplevel, after you destroy the toplevel.

        If you want to be happy with Tk, stop using destroy to eliminate windows. To exit gracefully, use plain old "exit" or "Tk::exit", they clean up. If you use $mw->destroy, you may be leaving the calling shell alive. Of course, that may be useful in certain circumstances.

        #!/usr/bin/perl -w use Tk; my $mw = MainWindow->new(-title=>"#1"); $mw->Button(-text=>"Destroy Me", -command=> sub { $mw->destroy })->pack; $mw->Button(-text=>"Exit Me", -command=> sub { Tk::exit })->pack; MainLoop; print "ha ha still going\n"; <>;

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum