On 15/03/11 13.56, Mark Dootson wrote:

   Hi,

I attempted to wrap the missing callbacks in wxVListBox and came across
the following issues. I've explained at length as info may be useful for
someone else like me trying to understand how to help with wxPerl.

An illustrative snippet of my code is

#----------------------------------------------

package MyList;
use base qw( Wx::PlVlistBox );

sub new {
my $self = shift->SUPER::new( @_ );
$self->{mycounter} = 100;
return $self;
}

sub OnDrawItem {
my( $self, $dc, $rect, $item ) = @_;
....
$self->{mycounter} ++;
print qq(Counter = $self->{mycounter}\n);
}

#-----------------------------------------------

With Wx 0.98 from CPAN, this fails at
$self->{mycounter} = 100; because $self is not a hash but it is an
object of type Wx::VListBox

Looking at the wxPerl code, I figure out that this is because
wxPlVlistBox has a typemap of O_WXEVTHANDLER.

If I want Wx::PlVlistBox::new to return me the equivalent of bless {},
$class - I have to add a line to the constructor

wxPli_create_evthandler( aTHX_ RETVAL, CLASS );

amongst other things, when this is parsed by the preprocessors, I get

wxPli_evthandler_2_sv

applied in Wx::PlVlistBox::new so I get back the blessed hash / class I
expected.

With that applied everything appears to work as expected - except that
the printed output from OnDrawItem starts at 1 - not 100. It increments
as expected on each call though.

Investigation tells me the following.

The contructor for my Wx::PlVlistBox based class calls

wxPli_make_object

this returns - sv_bless( ret, stash )

where ret is my way of accessing the underlying wxWidgets structure
directly and stash I can think of as containing what I would get back if
I did 'bless {}, $class;' in Perl.

The returned value is stored in wxPerl's class for holding on to object
data - in member m_self.

Within wxPerl, whenever I call a method that is going to return a
wxWidgets object, some methods give me the correct 'stash' stored in
m_self.

O_WXEVTHANDLER types are a little different.

There is no method added by default to the constructor to convert the
the return value to the associated 'stash'. So this explains the first
result I got running unaltered code.

When I add wxPli_create_evthandler to the constructor, the following
happens.

wxPli_make_object is called again.

wxWidgets provides a two accessors for classes that inherit from
wxEvtHandler or use a mixin class wxClientDataContainer.

This gives wrappers for wxWidgets like wxPerl and wxPython somewhere to
store their associated data within the wxWidgets object data itself.

So, wxPli_create_evthandler gets a new sv_bless( ret, stash ) and stores
this in ->SetClientData.

So there's effectively two sv_bless( ret, stash) values stored.

The original one in m_self and the second one in m_self->GetClientData.
The 'stash' pointers stored therein point to different stashes.

This is fine because all this is happening in the constructor. The
methods that return the objects to your Perl code will always call
->GetClientData for objects that inherit from wxEvthandler if client
data exists.

Except for the callback code. I think this always returns the stash from
m_self and does not call GetClientData.

So, I can make my example code apparently work by giving wxPLVListBox
and wxVListBox a typemap of O_WXOBJECT - so there's no use of
SetClientData.

But, now of course I have Perl data and no automatic destruction.

From looking at the code, using O_WXOBJECT it should work correctly, what problems are you seeing/thinking of?

So, the first question would be, how do I tell which is the appropriate
typemap to use for any given class. Obviously I can only use
O_WXEVTHANDLER for objects which inherit from wxEvtHandler. But plenty
of classes that inherit from wxEvtHandler also have typemaps of
O_WXOBJECT.

You probably have noticed that wxPli_object_2_sv calls wxPli_evthandler_2_sv if appropriate, so O_WXOBJECT can be applied to any wxEvtHandler subclass, it just does some extra (unnecessary) work before calling GetClientObject().

A general scan of the typemaps tells me that it appears that
classes that inherit from wxControl are O_WXEVTHANDLER and classes that
are more 'container' classes have a typemap of O_WXOBJECT. (for wxWindow
inheriting classes, of course).

It's just that O_WXEVTHANDLER came later, and not all classes have been converted.

O_WXEVTHANDLER looks like the typemap I should always choose if possible.

  Yes.

There is a potential problem (a variation of what you experienced): the current typemaps for the classes in the wxPlVListBox inheritance tree are:

wxObject          O_WXOBJECT
wxEvtHandler      O_WXEVTHANDLER (currently O_WXOBJECT)
wxWindow          O_WXEVTHANDLER (currently O_WXOBJECT)
wxPanel           O_WXEVTHANDLER (currently O_WXOBJECT)
wxVScrolledWindow O_WXEVTHANDLER
wxVListBox        O_WXEVTHANDLER
wxPlVListBox      O_WXOBJECT or O_WXEVTHANDLER

if wxPlVListBox uses O_WXOBJECT, a method returning (for example) wxWindow* applies the O_WXEVTHANDLER typemap and returns the wrong Perl value; if wxPlVListBox uses O_WXEVTHANDLER, you get your original problem.

  I see three possible solutions:
1) add a different virtual callback handler for wxEvtHandler-derived
   classes (that gets the perl object from GetClientObject)
2) change wxPli_create_evthandler to transparently use the existing
   instance if present, and call wxPli_make_object otherwise
3) add a separate wxPli_set_evthandler (or something) that needs to
   be used instead of wxPli_create_evthandler for
   wxEvtHandler-derived classes with virtual methods

  In all three cases, wxPlVListBox uses O_WXEVTHANDLER.

I prefer 2 (simpler, easier for XS++/code generation); the only advantage of 3 is slightly faster runtime (but I doubt it's noticeable); I listed 1 for completeness, because it wat the first thing I thought of, but it can't think of any advantages over 2 or 3.

It appears that I have to use O_WXOBJECT if I want Perl data and Callbacks?

Is this issue quite difficult to solve - and therefore why it is
implemented as O_WXEVTHANDLER but without any Perl stash in the first
place?

I can't remember... probably I didn't notice your original problem so I just committed the code.

Regards,
Mattia

Reply via email to