Hi,
The code example demonstrates the specific issue you are having
perfectly well.
When you assign a validator to a Window, a copy of the validator object
is created. This is why you need a Clone method.
After you assign TCtrl_v as the validator for your Window, the instance
is cloned and the cloned instance is used to validate Window input / output.
The TCtrl_v instance itself is never used to validate.
So, to fix your particular error:
replace
if( not $TCtrl_v->TransferFromWindow ) {
with
if( not $TCtrl->GetValidator->TransferFromWindow ) {
and
replace
$TCtrl_v->TransferToWindow;
with
$TCtrl->GetValidator->TransferToWindow;
For general use of validators I'll be adding a tutorial to the wxPerl
site in the next couple of days - however the key points are
Create your containers (e.g. Wx::Panel instances) with the extra style
wxWS_EX_VALIDATE_RECURSIVELY
To transfer data to window, call $panel->TransferDataToWindow on the top
level panel.
To transfer data from window call
if( $panel->Validate ) {
$panel->TransferDataFromWindow;
}
on the top level panel.
Wx::Dialog does much of this automatically. You might also like to
experiment with $panel->InitDialog.
Your exact implementation will depend on your data and it's source.
For example, your data must be available when you call TransferDataToWindow.
Hope it helps.
Regards
Mark
On 16/06/2013 12:03, Mary Guardi wrote:
=begin comment
Dear Mark
Thank you for your prompt reply. This program I'm running is not small
(~580 lines), and I didn't want to clog the forum.
I any case, I went through the program now, and sifted out the
irrelevant parts ( irrelevant for this question) (even so it's ~140
lines). Here it is - it's runnable.
Please note the following: the program contains 4 packages, the relevant
ones are "TC_test" and "MyValidator".
The program logic (trivially simple) goes like this: package "main"
calls package "MyForm", which displays a frame. Clicking the button
"select_btn" calls sub "prep_query", which calls package "TC_test".
package "TC_test" creates a text control called "$TCtrl", to which
"TCtrl_v": a "MyValidator" validator is attached.
Like I said in the OP, before leaving sub "new TC_test" it tries to
executes:
$TCtrl_v->TransferToWindow;
and breaks saying: "Can't call method "SetValue" on an undefined value
at ... .pl line 150."
Many TIA
Helen
=end comment
=cut
use strict;
use warnings;
use Wx;
package MyForm;# ------------------------------------- package MyForm::
use strict;
use warnings;
use 5.014;
use parent -norequire, 'Wx::Frame';
use Wx::Event qw(EVT_BUTTON);
use Wx qw[:everything];
sub new { #1 ---------------------------new MyForm::
my $class = shift;
my $self = $class->SUPER::new( undef,-1, 'Frame class: MyForm',
wxDefaultPosition, wxDefaultSize );
my $panel = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize);
# ...
my $select_btn = Wx::Button->new( $panel, -1, 'DB Select query',
wxDefaultPosition, wxDefaultSize);
EVT_BUTTON( $self, $select_btn,
sub { #3
prep_query();
} #3 end sub
);# end EVT_BUTTON continue_btn call
# ...
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
my $sizer1_h = Wx::BoxSizer->new(wxHORIZONTAL);
$sizer1_h -> Add ( $select_btn, 0, wxALL, 5 );
# ...
$sizer->Add( $sizer1_h, 1, wxEXPAND);
$panel->SetSizer($sizer);
$panel->Fit;
return $self;
sub prep_query {#2preparing query
my $select_query_frame = TC_test->new;
$select_query_frame->Show(1);
}#2 end sub prep_query
}#1 end sub new MyForm::
# ...
1;
# end package MyForm
package TC_test;# ----------------------------------- package TC_test::
use strict;
use warnings;
use Wx;
use 5.014;
use parent -norequire, 'Wx::Frame';
use Wx::Event qw(EVT_BUTTON EVT_CLOSE);
use Wx qw( wxDefaultPosition wxDefaultSize wxTE_PROCESS_ENTER);
# ...
sub new {# -------------------------------------- new TC_test::
my $class = shift;
# ...
my $self = $class->SUPER::new( undef,-1, 'Frame class: TC_test',
wxDefaultPosition, wxDefaultSize);
my $panel2 = Wx::Panel->new($self,-1,
wxDefaultPosition, wxDefaultSize);
# ...
my $TCtrl = Wx::TextCtrl->new($panel2, -1, q{},
wxDefaultPosition, [20,-1], wxTE_PROCESS_ENTER);
my $TCtrl_v = MyValidator->new( \$main::text_value);
$TCtrl->SetValidator( $TCtrl_v);
my $sizer_main = Wx::FlexGridSizer->new(7,7,5,5);
# ...
$sizer_main->Add($TCtrl, 0);
# ...
$sizer_main->Fit($self);
$sizer_main->SetSizeHints($self);
# ...
EVT_CLOSE( $self,
sub {#2
if( not $TCtrl_v->Validate ) {
Wx::LogMessage( "Data is invalid" );
return;
}
if( not $TCtrl_v->TransferFromWindow ) {
Wx::LogMessage( "Error in data transfer" );
return;
}
$self->Wx::Window::Destroy;
}#2 end sub of EVT_CLOSE
);#1end EVT_CLOSE call
$TCtrl_v->TransferToWindow;
# here it breaks saying: "Can't call method "SetValue" on an undefined
value at ... etc...".
return $self;
}#1 end sub new TC_test::
1;
# end package TC_test#------------------- end package TC_test
package MyValidator;#----------------- package MyValidator
# helped by: http://articles.mongueurs.net/magazines/linuxmag71.html
use strict;
use Wx qw(wxOK wxICON_ERROR);
use parent -norequire, 'Wx::PlValidator';# Classe de base
sub new {# Constructeur
my $class = shift;
my $var_r = shift;# Ref de la variable associe
my $this = $class->SUPER::new;
$this->{var_r} = $var_r;# Pour consultation ultrieure
return $this;
} # end sub new
sub Clone {# Indispensable pour les Validators car ils sont clon?s en
interne
my $this = shift;
my $new = MyValidator->new( # Transmet la reference a la variable associe
$this->{var_r} );
return $new;
} # end sub Clone
sub Validate {# Appel?e par Wx::Dialog->Validate
my( $this, $dialog ) = @_;
my $V_var = $this->GetWindow->GetValue;# La methode GetWindow
retourne le widget concerne
# Verification personnalisee
if ($V_var !~ /^[A-Z ]+$/) {# ici uniquement des caracteres
alphabetiques majuscules et l'espace
Wx::MessageBox( "Erreur: $V_var n'est pas une valeur admissible.", #
Affichage d'un Pop-up informatif
"Error", wxOK|wxICON_ERROR, undef );
return 0;# Signale notre probleme au Dialog
}
1;# Signale que tout va bien
} # end sub Validate
sub TransferFromWindow {# Copie le contenu du controle dans la variable
associe
my $this = shift;
${$this->{var_r}} = $this->GetWindow->GetValue;
1;# Signale que tout va bien
} # end sub TransferFromWindow
sub TransferToWindow {# Copie le contenu de la variable associ?e dans le
controle
my $this = shift;
$this->GetWindow->SetValue( ${$this->{var_r}} );
1;# Signale que tout va bien
} # end sub TransferToWindow
1;
# end package MyValidator;#------- end package MyValidator
package main;# ---------------------------------------------- package
main::
use strict;
use warnings;
# ...
# déclaration et initialisation de la variable interne associée au Validator
$main::text_value = 'FOO BAR';
my $app = Wx::SimpleApp->new;
my $frame = MyForm->new;
$frame->Show(1);
$app->MainLoop;
1;
On Sun, Jun 16, 2013 at 4:48 AM, Mark Dootson <mark.doot...@znix.com
<mailto:mark.doot...@znix.com>> wrote:
Hi,
On 15/06/2013 22:23, Helen Craigman wrote:
Dear esteemed WxPerl experts and users:
I have been trying to incorprate a validator on a TextCtrl
widget, using
PlValidator (following this:
......
This gives the error: "Can't call method "SetValue" on an undefined
value at..." etc.
As far as my own usage is concerned, classes derived from
Wx::PlValidator work perfectly.
For me to either help you to get your Wx::PlValidator derived class
working, or identify some bug in Wx::PlValidator, you would have to
post some code that can be run and demonstrates the failure you are
experiencing.
The code snippet you have posted doesn't show a validator being
assigned to a Wx::TextCtrl - so it cannot be an accurate
representation of the code that fails for you. You clearly have
worked out how a Validator should work if you get as far as the
error reported, and you probably just need a simple adjustment in
your code to make it work. But without seeing the actual failing
code I have no clue what might be causing the failure.
I feel that I am always giving negative answers to the Wx questions
you ask on this list and Perl Mongers - and I feel really bad about
this as you have clearly figured out 99% of what is required yourself.
I can't help if you don't post some actual code - or even a snippet
taken from actual code. I'd guess you are missing a Clone method in
your derived class, but who knows. The code you posted can't be the
code you are running.
Regards
Mark