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

I'm trying to retool to Perl from Ansi C (dabbled only slightly in C++ years ago.) I am writing an app that uses a database. I pull the needed data from the database at the beginning of Package MyFrame, and then attempt to handle an EVT_COMBOBOX, but can't seem to pass data to the event handler. I basically need to get the selected combo box entry to the event handler sub so that I can use it in the sub. How is this done? (I'm familiar with passing pointers in C. I think that "aliasing" may be analogous to that, but I don't know how it's done in Perl.) I've made a few vain attempts, but can't seem to find examples for Perl WxWidgets that apply. So far my code is simply in a proof-of-concept state (cut and paste from examples with a few adjustments of my own). So sub Combobox1action is generic and has nothing in it but place holding code. It does execute in its present state, but I'm at a stalemate with it. Below is the MyFrame package.

package MyFrame; use base 'Wx::Frame'; use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX ); my $sth; my $tablename; my $sqlcommand; my @lessonnumber; my @lessonsimplename; my @lessontitle; my @startingpagenumber; my $nameargument; my $increment = 0; my $dbh = DBI->connect("dbi:mysql:*********", "root","*******") or die "Can't connect to MySQL database: $DBI::errstr\n"; $sqlcommand = "SELECT lessonstring, lesson_number, description, starti +ng_pagenumber FROM lesson_titles"; $sth = $dbh->prepare ($sqlcommand) or die "Cannot perform SQL INSERT: $DBI::errstr\n"; $sth->execute( ) or die "Cannot perform execute\n"; for ( $increment = 1; $increment <= 132; $increment++ ) { ( $lessonsimplename [ $increment-1 ], $lessonnumber[ $increment-1 + ], $lessontitle [ $increment-1 ], $startingpagenumber [ $increment-1 ] ) = $sth->fetchrow_array() +; } $sth->finish; $dbh->disconnect or warn "Disconnect failed: $DBI::errstr\n"; #for ( $increment = 1; $increment <= 132; $increment++ ) { # print ($lessonsimplename [ $increment ].": ".$lessontitle [ $incr +ement ]." p. ".$startingpagenumber [ $increment ]."\n"); #} sub new { my $ref = shift; my $self = $ref->SUPER::new( undef, # parent window -1, # ID -1 means any '********', # title &Wx::wxDefaultPosition, # defaul +t position [200, 200] # size ); my $panel = Wx::Panel->new( $self, # parent window -1, # ID ); my $button1 = Wx::Button->new( $panel, # parent window -1, # ID 'Click me 1!', # label [50, 20], # position &Wx::wxDefaultSize # default s +ize ); my $button2 = Wx::Button->new( $panel, # parent window -1, # ID 'Click me 2!', # label [50, 50], # position &Wx::wxDefaultSize # default s +ize ); my $button3 = Wx::Button->new( $panel, # parent window -1, # ID '*********', # label [50, 80], # position &Wx::wxDefaultSize # default s +ize ); my $combobox1 = Wx::ComboBox->new( $panel, -1, $lessonsimplename[ 0 ], [50, 110], [-1, -1], \@lessonsimplename, 0, &Wx::wxDefaultValidator, '' ); EVT_BUTTON( $self, $button1, \&Button1Click ); EVT_BUTTON( $self, $button2, \&Button2Click ); EVT_BUTTON( $self, $button3, \&Button3Click ); EVT_COMBOBOX( $self, $combobox1, \&Combobox1action ); return $self; } sub Button1Click { my( $self, $event ) = @_; $self->SetTitle( 'Button 1 Clicked' ); } sub Button2Click { my( $self, $event ) = @_; $self->SetTitle( 'Button 2 Clicked' ); } sub Button3Click { my( $self, $event ) = @_; $self->SetTitle( '*********' ); } sub Combobox1action { my( $self, $event ) = @_; $self->SetTitle( 'Combo box selected' ); }

Thanks.

Replies are listed 'Best First'.
Re: Passing arguments to event handlers in WxPerl
by zentara (Cardinal) on Sep 18, 2014 at 19:25 UTC
    I basically need to get the selected combo box entry to the event handler sub

    I can't run your code, but from the question, I will offer this. In most GUI's, the widget that sends the event, is the first item listed in the callback's subroutine's @_ arguments.


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

      Sorry about the misunderstanding. This is just one package from the code. There is a little more. I can post all of it if you would like. You will have trouble running it without my database. The code for the event handler looks like this:

      sub Combobox1action { my( $self, $event ) = @_; $self->SetTitle( 'Combo box selected' ); }
      It appears that $self and $event are the only parameters contained in @_. Unfortunately, I cannot seem to access the GetValue() method(?) through either of these. From the limited wxPerl documentation, GetValue() is supposed to return the selected item in the combo box. I can't get that to happen. Neither $self->GetValue(), nor $event->GetValue() is recognized.
        Can you make a working code example, that dosn't use any OO program? That would identify wheter it's a Wx problem, or an improper object model which you use.

        You could also check the reference type of $self to see what you are getting.

        sub Combobox1action { my( $self, $event ) = @_; print $self, "\n"; $self->SetTitle( 'Combo box selected' ); }
        If $self prints out to be a WxWidget Combobox, your code should work. But if $self turns out to be the package you are holding your code in, MyFrame, a WxWidget frame, then you will need to find a way of clearly identifying the Combobox.

        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: Passing arguments to event handlers in WxPerl
by Laurent_R (Canon) on Sep 18, 2014 at 22:30 UTC
    I'm familiar with passing pointers in C. I think that "aliasing" may be analogous to that, but I don't know how it's done in Perl.

    Sort of, but not quite. Something really similar to passing pointers in C is to pass references to variables or data structures (or to subroutines or other things). When using a function in a module that uses TK (or for many other purposes, for that matter), you might end up writing something similar to this:

    my_function( [\&callback_func, $cb_param1, $cb_param2], $arg2, $arg3);
    Here, three parameters are passed to my_function: a reference to a data structure to be explained shortly and two additional arguments, $arg2 and $arg3. The first argument is a reference to an array that contains three elements: a reference to a callback subroutine (defined in the user program, but used by the my_function subroutine within the module), and two arguments passed to that callback subroutine. Of course, the my_function has to be built in order to be able to process these three arguments and especially the three items contained in the first argument.

    Aliasing is somewhat different: it is an internal Perl process whereby the data is essentially passed by reference, so that if your function modifies its incoming argument, the variable that was passed to the function will be modified within the caller function. The main reason for doing that is that if you pass a large array, Perl does not make a copy of the array, but just provides to the called function an alias to that array. This can be significantly faster. The consequence is that if you modify that array, it will be modified in the caller function. For that reason, it is often considered good policy to start a subroutine with a copy of the arguments into local variables, to avoid to inadvertently modify the caller's execution environment (and obtain a reliable pass-by-value type of semantic). It is the responsibility of the programmer to decide whether or not to do it, depending on the desired effect. To be complete (but without going into gory details), I should add that aliasing also occurs in other circumstances, such as the use of for loops or expression modifiers, map and grep constructs, etc.

    But aliasing an array passed to a subroutine (or to a for loop) is done automatically by Perl, you have relatively little control on it. Whereas passing an actual reference gives the developer full control on what happens. The most obvious example is that if you pass, say, two arrays to a subroutine, the subroutine will see only one @_ array containing a flattened list of the values of the two arrays, with no means of distinguishing between the values from array1 and from array2. Sometimes, it is OK, sometimes not. In contrast, if you pass a reference to each array, the called subroutine can fully distinguish the two arrays.

    Another less important but nonetheless noticeable difference is that passing a reference to a very large array is significantly faster than the simple aliasing provided by Perl (which is itself significantly faster than the passing-by-value option for the obvious reason that passing-by-value is just aliasing followed by data copy into local variables). On a benchmark that I once made, simple aliasing was 18 to 20 times faster than passing-by-value, and actual reference passing was almost 500 times faster than aliasing. Having said that, aliasing is really pretty fast, you would need to pass really huge amount of data to really worry about the difference.

Re: Passing arguments to event handlers in WxPerl (is something you never have to do; wx-combobox-event.pl)
by Anonymous Monk on Sep 19, 2014 at 02:24 UTC

    Passing arguments to event handlers in WxPerl is something you never have to do :)

    Having code outside of subroutines is something you never have to do :) (really shouldn't )

    EVT_COMBOBOX ... How is this done?

    As documented :) my wxWidgets / wxPerl / wxGlade tutorial, wxperl_usage / wxperl-usage / wxPerl::Usage / Class Method Browser , available methods, method invocation syntax, link to docs

    sub Combobox1action { my( $frame, $comboevent ) = @_; my $combobox = $comboevent->GetEventObject; my $selection = $comboevent->GetString; $frame->SetTitle( "Combo box selected ($selection)" ); }


    http://docs.wxwidgets.org/2.8.12/wx_wxcommandevent.html#wxcommandeventgetstring
    http://docs.wxwidgets.org/2.8.12/wx_wxevent.html#wxeventgeteventobject

    Re^2: How do i color font in a listbox? ( wx-listctrl-coloritems-findselect.pl )

    wx-combobox-event.pl

    #!/usr/bin/perl -- ## ## ## perltidy -olq -csc -csci=3 -cscl="sub : BEGIN END " -otr -opr -ce +-nibc -i=4 -pt=0 "-nsak=*" ## perltidy -olq -csc -csci=10 -cscl="sub : BEGIN END if " -otr -opr +-ce -nibc -i=4 -pt=0 "-nsak=*" ## perltidy -olq -csc -csci=10 -cscl="sub : BEGIN END if while " -otr + -opr -ce -nibc -i=4 -pt=0 "-nsak=*" #!/usr/bin/perl -- use strict; use warnings; use Wx 0.86 qw( ); Main( @ARGV ); exit( 0 ); sub Main { my $app = Wx::SimpleApp->new; my $f = Wx::Frame->new( undef, -1, "", ); my $cb = Wx::ComboBox->new( $f, -1, "", [-1,-1,], [-1,-1,], [ 22 .. 33 ], ); $cb->SetFocus; $f->SetSizer( Wx::BoxSizer->new( Wx::wxVERTICAL() ) ); $f->GetSizer->Add( $cb, 0, Wx::wxEXPAND() ); $f->GetSizer->SetSizeHints( $f ); $f->Show( 1 ); $app->SetTopWindow( $f ); #~ Wx::Event::EVT_COMBOBOX( $app, $cb, \&Combobox1action ); Wx::Event::EVT_COMBOBOX( $f, $cb, \&Combobox1action ); $app->{cb} = $cb; $app->MainLoop; } ## end sub Main sub Combobox1action { my( $frame, $comboevent ) = @_; my $combobox = $comboevent->GetEventObject; my $selection = $comboevent->GetString; #~ my $selection = $combobox->GetValue; $frame->SetTitle( "Combo box selected ($selection)" ); } ## end sub Combobox1action __END__
      I guess I'm still trying to develop an OO way of thinking. One more question. Is there an ideal place to do database operations? Should I do them right at the beginning and load up a bunch of variables?
      Thanks for the response. It think that you are on to the problem. Do the variables in MyFrame that I loaded with the data from the database have the right scope for the event handler sub to access them?
        Got it!!! Thanks. Here is the code:
        sub Combobox1action { my( $self, $event ) = @_; my $selection; $selection = $event->GetString(); $self->SetTitle( 'Combo box selected ( '.$selection.' )' ); }
        I guess I still need to clean up a little, but at least I've moved forward. Thanks all. Any other final suggestions are welcome.
        I found the answer to the scope question.