How to access "user data" from an event handler?

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

How to access "user data" from an event handler?

Post by Radek » Tue Sep 20, 2011 5:00 pm

I would like to process menu items in a single handler. I tried the following:

Code: Select all

  id = menu1->FindItem(wxT("aaa"));     // id is an int
  pp = menu1->FindItem(id);                 // pp is a wxMenuItem *
  Connect(
           id,
           wxEVT_COMMAND_MENU_SELECTED,
           wxCommandEventHandler(MyFrame::OnMenuItem),
           pp
         );
The "user data" parameter is wxObject * so that passing wxMenuItem * is okay. It compiles, running debugger shows that both id and pp have reasonable values. Now, the handler. If a can retrieve pp in it then I can reach both the menu item properties and the pointer to the menu the item belongs. Good. Therefore:

Code: Select all

void MyFrame::OnMenuItem( wxCommandEvent& event )
{
  wxMenuItem *pp   = reinterpret_cast<wxMenuItem *>(event.???);
  wxString    item = pp->GetItemLabel();

  ...
I have tried event.GetClientData(), event.GetClientObject() (not very promising but I was trying) but the debugger shows that pp = NULL. How can I reach pp from the event handler? Even if I can reach the menu item otherwise, how can I reach user data from the handler in general?

DavidHart
Site Admin
Site Admin
Posts: 3881
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: How to access "user data" from an event handler?

Post by DavidHart » Tue Sep 20, 2011 6:07 pm

Hi,

See this thread, near the end (search for m_callbackUserData).

Regards,

David

RainRat
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Thu Jan 06, 2011 11:26 pm

Re: How to access "user data" from an event handler?

Post by RainRat » Tue Sep 20, 2011 6:26 pm

I think what you want is wxEvent::GetEventObject(). This is in the parent class of wxCommandEvent.

I would use dynamic_cast to cast the pointer and check that the event object is the correct type. It returns a NULL pointer if it is not of the specified type.

Code: Select all

void MyFrame::OnMenuItem( wxCommandEvent& event )
{
  wxMenuItem *pp = dynamic_cast<wxMenuItem *>(event.GetEventObject());
  if(pp) {
    wxString item = pp->GetItemLabel();
    // ...
  }
}

Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: How to access "user data" from an event handler?

Post by Radek » Tue Sep 20, 2011 6:39 pm

I've just tried GetEventObject(). It returns a non-null pointer but the pointer is different from the pointer to the wxMenuItem which should invoke the handler. A subsequent attempt to GetItemLabel() causes a GP fault so that the pointer does not point to a wxMenuItem most likely. The dynamic_cast will return NULL in my case, because the reinterpret_cast does.

RainRat
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Thu Jan 06, 2011 11:26 pm

Re: How to access "user data" from an event handler?

Post by RainRat » Tue Sep 20, 2011 7:23 pm

You should not use reinterpret_cast! static_cast can be used to downcast (from wxObject* to wxMenuItem*) if you are certain that the pointer will always actually point to the derived class. dynamic_cast with the NULL check is safest.

If dynamic_cast<wxMenuItem*> returns NULL, then the pointer is not of type wxMenuItem. Are you sure that your first calls to menu1->FindItem are returning valid id and wxMenuItem*?

Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: How to access "user data" from an event handler?

Post by Radek » Wed Sep 21, 2011 3:15 pm

The reinterpret_cast is okay here, RainRat. static_cast can create temporary objects so that I would lose control during a debug session. reinterpret_cast is a hint for the compiler: consider the memory at the pointer containing an object of the given class. No checking, no conversions, no other additional actions.

That's exactly what I want. I am passing wxMenuItem by means of Connect() and I want to retrieve this wxMenuItem in the handler. If I get a NULL pointer or a GP fault then something is wrong with my code. The pointer has not been passed for some reason (it I get NULL) or I have retrieved a pointer which I did not want (if I get GP fault). The question is why.

As to the FindItem() calls. They are okay. I have checked them by the debugger. At present, I am using event.GetId() for getting the menu item ID, then menu1->FindItem() for getting the corresponding wxMenuItem. It works but it isn't what I wanted.

User avatar
doublemax
Moderator
Moderator
Posts: 13985
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to access "user data" from an event handler?

Post by doublemax » Wed Sep 21, 2011 3:44 pm

The "userData" passed to the Connect() call is written to the event table and later copied to the protected member variable wxEvent::m_callbackUserData. A comment in the header files says, it's for internal use only.

However, there is no method to retrieve that value, but i also don't see that it's used anywhere by wxWidgets internally.

Maybe you could make a feature request at http://trac.wxwidgets.org/ and ask for a public method to be added to wxEvent to read that value, so it can be used inside the event handler.
Use the source, Luke!

Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: How to access "user data" from an event handler?

Post by Radek » Wed Sep 21, 2011 5:18 pm

The data m_callbackUserData is even public but I hesitate to use it in my coding if it isn't mentioned in the official reference. The question seems to be why does Connect() have a parameter which cannot be utilized later? What are we supposed to pass using this parameter? And is the parameter even intended for passing some user data if it relates to "callback something"?

Oh, well ...

RainRat
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Thu Jan 06, 2011 11:26 pm

Re: How to access "user data" from an event handler?

Post by RainRat » Wed Sep 21, 2011 9:46 pm

Edit: From your other thread, I see that you already did this, but I posted before reading that thread.

Am I missing something here? Doesn't each of your wxMenuItems have a unique ID? Why can't you use the event id to get the wxMenuItem in your handler?

Code: Select all

void MyFrame::OnMenuItem( wxCommandEvent& event )
{
  int id = event.GetId();
  wxMenuItem *pp = menu1->FindItem(id);
  // or
  // wxMenuItem *pp = GetMenuBar()->FindItem(id);

  wxString item = pp->GetItemLabel();
  ...
}

Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: How to access "user data" from an event handler?

Post by Radek » Thu Sep 22, 2011 7:30 am

It's learning purposes and also a bit planning ahead. The FindItem() call needs a wxMenu object which the menu item ID belongs. As long as you have only one menu, this is no problem. If you have two or more popup menus then you need to find the wxMenu first.

It can be done by keeping a table with wxMenuItem IDs and the corresponding wxMenu pointers in the frame object. It can be done by assigning wxMenuItem IDs yourself and keeping info about the corresponding wxMenu in the IDs (IDs 100..199 = first menu, etc.) We know similar practices from "message loops" in winblows or OS/2. Isn't it possible to do it better in wxWidgets?

And the general question: Is it possible to pass user data from Connect() to the handler? :mrgreen:

Post Reply