New events in own class Topic is solved

Are you writing your own components and need help with how to set them up or have questions about the components you are deriving from ? Ask them here.
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

New events in own class

Post by Lightman »

I'm developing my own class (myFrameSpin) derived from wxWindow (it contains a wxTextCtrl and wxSpinButton).
1) I need to declare my own event in myFrameSpin of a standard event type (something like wxSpinEvent; no need to create own event type), so that this event must be "visible" on the "Management" tab when I edit my frame/dialog with this control using wxSmith / Code::Blocks. How can I do that ?
When I select my custon control in wxSmith, I see a lot of events (they must be parent class's events); I need my event to be published here.
I've been searching for solution for half a day, and found nothing useful :(. Tried to find something in wx sources, but that was too complicated for me :(.
2) How should I call such an event from inside myFrameSpin class ?

Thanks in advance!

P.S: I'm a bit new to wxWidgets (several months of coding), so sorry if that was a stupid question :)
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Re: New events in own class

Post by Auria »

Hi,

to me this actually sounds like a wxSmith question more than a wxWIdgets question (if I understand correctly, the question is "how can I bind my custom events from wxSmith"). I might recommend asking this directly to the author of wxSmith
"Keyboard not detected. Press F1 to continue"
-- Windows
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

Well, it's not very important about wxSmith, so we can change the first quection to:
1) how can I declare my own events in my component ? (I will Connect() to them later in run-time).
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: New events in own class

Post by DavidHart »

Hi,

It won't help you with wxSmith/C::B, but there's a custom event page in wxWiki: see here for simple events, and here for complicated ones.

Regards,

David
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

Thanks for links, I had already read that, but still unable to understand :)
I'd learned events sample, but it handles class events inside this class only, and I need to connect to another class to catch its events.
Possibly it will be more understandable if I list some code here:
myframespin.h:

Code: Select all

class myFrameSpin : public wxWindow
{
    public:
        ...
    private:
        wxSpinButton* btn;// spin button
        // Event handler for spin button:
        void OnSpin(wxSpinEvent& event);
        // Event I want to connect to from outside:
        void myOnChange(wxSpinEvent& event) { wxBell(); };        
    DECLARE_EVENT_TABLE()
};
myframespin.cpp:

Code: Select all

...
BEGIN_EVENT_TABLE(myFrameSpin, wxWindow)
EVT_SIZE(myFrameSpin::OnSize)
EVT_SPIN(wxEVT_COMMAND_SPINCTRL_UPDATED, myFrameSpin::myOnChange)
END_EVENT_TABLE()

myFrameSpin::myFrameSpin(wxWindow *parent, wxWindowID id, const wxPoint &position, const wxSize& size, const wxString& name)
{   // Constructor:
    Create(parent,id,position,size,0,name);
    ...    
    // Create children:
    btn = new wxSpinButton(this);
    Connect(btn->GetId(),wxEVT_SCROLL_THUMBTRACK,(wxObjectEventFunction)&myFrameSpin::OnSpin);
    //connecting handler for btn - it works well.
    ...
}
void myFrameSpin::OnSpin(wxSpinEvent& event)
{   // spin changed:
    SetPosition(event.GetInt());// doing some internal work,
    myOnChange(event);// calling myOnChange Event;
    // actually, I need to call some handler in a dialog/form containing this myFrameSpin
    // to handle changed value like any other native wxWidgets control - on slider position change, etc.
}
MyFrameSpinTestMain.cpp:

Code: Select all

BEGIN_EVENT_TABLE(MyFrameSpinTestDialog,wxDialog)
    //(*EventTable(MyFrameSpinTestDialog)
    //*)
END_EVENT_TABLE()

MyFrameSpinTestDialog::MyFrameSpinTestDialog(wxWindow* parent,wxWindowID id)
{
    //(*Initialize(MyFrameSpinTestDialog)
    ...
    spin = new myFrameSpin(this,ID_CUSTOM1,wxDefaultPosition,wxSize(115,26),_T("ID_CUSTOM1"));
    ...
    Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnAbout);
    Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnQuit);
    //*)
    Connect(ID_CUSTOM1,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnSpinChange);
    // here I'm trying to connect my handler to replace "dummy" event handler in myFrameSpin spin.
}
...
void MyFrameSpinTestDialog::OnSpinChange(wxSpinEvent& event)
{   // OnSpinChange:
    wxMessageBox(_("IT WORKS !!!"),_(""));
}
When I change (wxSpinButton) myFrameSpin::btn position, OnSpin event occurs, calls myOnChange(wxSpinEvent& event), and I hear a bell. How can I connect my MyFrameSpinTestDialog::OnSpinChange(wxSpinEvent& event) handler to myFrameSpin spin ?
Had I declared events correctly ? Where had I mistaken ?
Thanks in advance !
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: New events in own class

Post by DavidHart »

So (unless I still misunderstand) you don't actually need to declare your own event or similar; you just need to Connect() the standard wx event.

The way to think about Connect() is that you are trying to connect one object with another. More specifically, you're trying to connect events emitted by the origin object to the destination object for handling.

Having read that, you can probably see what you're doing wrong in:
Connect(ID_CUSTOM1,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnSpinChange);
which happens in a myFrameSpin method, so it has the myFrameSpin as both the origin and the destination (the destination is the eventSink parameter, which you don't specify, so it defaults to being the same as the origin object).

What you should do is:
spin->Connect(ID_CUSTOM1,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnSpinChange, NULL, this);
where 'spin' is the origin, and 'this' the destination.

Even better would be:
spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnSpinChange, NULL, this);
as in this situation there's no need to specify the ID of the event to connect; it's only 'spin' that gets connected anyway.

Best of all, hide the ugly cast:
spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(MyFrameSpinTestDialog::OnSpinChange), NULL, this);
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

I had understood that you said, but if I do so, I'll be unable to handle btn's OnSpin event inside myFrameSpin.

Let me shortly explain the idea of myFrameSpin: it's a wxWindow with an wxTextCtrl and wxSpinButton. When user enters something to wxTextCtrl (not shown in listed sources) or clicks wxSpinCtrl (OnSpin event for internal use), I need first to catch this event inside myFrameSpin (as shown in my sources above, OnSpin event), and only after that - run another event (which must be declared in myFrameSpin to be accessible and connectible from the parent dialog, MyFrameSpinTestDialog). For this purpose, I had declared the following in MyFrameSpin.h:

Code: Select all

class myFrameSpin : public wxWindow
{   ...
        // Event I want to connect to  from outside:
        void myOnChange(wxSpinEvent& event) { wxBell(); };
This is not an event of any other control but my myFrameSpin, and I need to connect to it from parent dialog.
I'm not sure if I had declared it right in myframespin.cpp:

Code: Select all

EVT_SPIN(wxEVT_COMMAND_SPINCTRL_UPDATED, myFrameSpin::myOnChange)
and even not sure I need to declare it here at all.
Also maybe I had wrongly connected to it from MyFrameSpinTestDialog constructor:

Code: Select all

    Connect(ID_CUSTOM1,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnSpinChange);
    // here I'm trying to connect my handler to replace "dummy" event handler in myFrameSpin spin.
When I run it, I can hear wxBell() when clicking wxSpinButton, so it looks like Connect() was done wrong, or I had declared myOnChange event in a wrong way.

Of course, I can Connect() to both wxTextCtrl and wxSpinButton from the parent dialog, but it's a good idea to process all myFrameSpin's events inside itself, and generate events for its parent only after that. (for example, I need first to convert user-entered text to value, change wxSpinButton's position and only after that call parent's handler to handle value change event).

Sorry, maybe my bad English skills make my posts poorly understandable :)
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: New events in own class

Post by DavidHart »

Let me shortly explain the idea of myFrameSpin: it's a wxWindow
Don't do that! wxWindow is a baseclass, and shouldn't be used directly as a container.

If you want myFrameSpin to be a visible control, that also contains the wxTextCtrl and wxSpinButton, use wxPanel.
If myFrameSpin isn't supposed to be seen, but is just a destination for events, derive from wxEvtHandler instead.
I'm not sure if I had declared it right in myframespin.cpp:
EVT_SPIN(wxEVT_COMMAND_SPINCTRL_UPDATED, myFrameSpin::myOnChange)
and even not sure I need to declare it here at all.
That isn't a declaration, it's an event-table entry...
I need first to catch this event inside myFrameSpin (as shown in my sources above, OnSpin event), and only after that - run another event
Why does it need to be another event? Why not post the same event direct to the new destination(s)?

I get the feeling that you are confusing an event (derived from wxEvent) with a class, in this case myFrameSpin. Maybe it's just the way you translate into English, though.
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

Thanks, I had corrected my code and now myFrameSpin is derived from wxPanel. Yes, it's supposed to be a visible control.
Why does it need to be another event?
First (my) way: I have one myFrameSpin on a wxFrame, and one myOnChange event handler on parent wxDialog, which just gets a value ready to be used (because events from its children are treated inside myFrameSpin - text-to-value recognition, range checking, etc.). Few (N) myFrameSpins - N handlers.
Second (yours) way: I have one myFrameSpin on a wxFrame, and at least two event handlers (1st to handle text change, 2nd to handle spin clicks) on parent wxDialog. Few (N) myFrameSpins - at least N*2 handlers with almost the same code - to treat user-entered text, do the range checking, etc. What's the idea ? :shock:

As an example, let's take a look at wxSlider. If I need to catch its position change, I'll Connect() to wxEVT_SCROLL_THUMBTRACK and get a ready value - trackbar position. Do you think it's better to catch all mouse events upon this slider and do all the wxSlider's job inside all these handlers ? :)
I'm sure I really know what I need to do, but I just don't know how to do it.
Can you please just help me how to declare and call one custom event in myFrameSpin ?..
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: New events in own class

Post by DavidHart »

First (my) way:...
Second (yours) way:...
IIUC both 'ways' pass an event to the dialog. My way reuses the original wxSpinEvent, yours uses a new event.
Can you please just help me how to declare and call one custom event in myFrameSpin
OK. What sort of custom event do you need? What information should it contain?

If you need to pass lots of complicated data, you probably need a proper custom event (where you subclass e.g. wxCommandEvent and arrange for the storage in the derived class).

If you just need to pass an int or a wxString (or both), you don't need a proper custom event; just use a wxCommandEvent, and give it a 'custom' event-type so that it can be recognised easily.
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

I need to pass just one integer, so I think wxCommandEvent will be the best choice.
I think there are no need to declare custom event type - that's just a waste of time here, I'll be comfortable with wxCommandEvent.
There are only four things left:

1) to declare my event (of wxCommandEvent type) in myFrameSpin; is this code right? (myFrameSpin.h):

Code: Select all

class myFrameSpin : public wxPanel
{   ...
    private:
        void myOnChange(wxSpinEvent& event);
2) to list this event in myFrameSpin.cpp event table; a) is this necessary ?, b) how exactly ?

3) to Connect() some handler to myOnChange event from parent wxDialog; is this code right? (MyFrameSpinTest.cpp, ):

Code: Select all

MyFrameSpinTestDialog::MyFrameSpinTestDialog(...)
{   ...
    Connect(ID_CUSTOM1,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnFrameChange);
}
I understand that instead of wxEVT_COMMAND_SPINCTRL_UPDATED I need to specify the exact value as I did in event table, see 2).

4) to call (raise, execute, launch, initiate - what's the right word ?) this event from inside of myFrameSpin; I'm doing it like this:

Code: Select all

void myFrameSpin::OnSpin(wxSpinEvent& event)
{   // spin changed:
    // (some internal work)
    wxCommandEvent e(event);// create wxCommandEvent based on event, see above
    myOnChange(e);// compiler reports error here, see below
}
but I see it's wrong, and compiler tells me "no matching function to call to myOnChange()", so I had to replace

Code: Select all

void myOnChange(wxSpinEvent& event);
with

Code: Select all

void myOnChange(wxSpinEvent& event) { wxBell(); };
When I click on wxSpinButton, OnSpin calls myOnChange() and I hear a bell. But MyFrameSpinTestDialog::OnFrameChange does not executes |=> it was not properly connected |=> I did something wrong...
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: New events in own class

Post by DavidHart »

I need to pass just one integer, so I think wxCommandEvent will be the best choice.
Well... You say that you want to use your own event here, so: Yes, it's the best choice.
I think there are no need to declare custom event type - that's just a waste of time here, I'll be comfortable with wxCommandEvent.
You're making the common error of confusing the class wxCommandEvent with 'event-type', which is an int. You'll need some way of telling the destination class what wxCommandEvents to catch.
2) to list this event in myFrameSpin.cpp event table; a) is this necessary ?, b) how exactly ?
Either use Connect(), or use an event table entry. Not both.
3) to Connect() some handler to myOnChange event from parent wxDialog; is this code right? (MyFrameSpinTest.cpp, ):
Perhaps, but it depends what you're trying to do.

What I think you should be doing is:
  • 1) Post an event from the myFrameSpin instance to the MyFrameSpinTestDialog instance.
    2) Connect() the MyFrameSpinTestDialog instance to MyFrameSpinTestDialog::OnFrameChange for that event-type/ID
If so, then your Connect() is correct, if you post a wxSpinEvent with that ID and event-type.
4) to call (raise, execute, launch, initiate - what's the right word ?) this event from inside of myFrameSpin;
The word is 'send' or 'post'. You should do something like:
wxPostEvent(pFSTDialog, event);
where pFSTDialog is a pointer to the destination instance, and event is the event containing the data.
But MyFrameSpinTestDialog::OnFrameChange does not executes |=> it was not properly connected |=> I did something wrong...
What is the signature of MyFrameSpinTestDialog::OnFrameChange?
Better still, please attach a file containing your current code. It will be much easier to understand if it can be seen all at once, in one place; not fragments contained inside several posts.
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

Thanks a lot, I'm almost in...
Still hadn't understood:
1) How could I know which event-type (int) should I use in Connect(), if I don't specify any event-type (int) in wxPostEvent() ?
I was trying to post less code to save your time :), but if you wish, I can post almost everything:

myframespin.h:

Code: Select all

#ifndef MYFRAMESPIN_H
#define MYFRAMESPIN_H
#include <wx/window.h>
#include <wx/spinbutt.h>
#include "myunit.h"

class myFrameSpin : public wxPanel
{
    public:
        /** Default constructor */
        myFrameSpin(wxWindow *parent,
                   wxWindowID id = -1,
                   const wxPoint& position = wxDefaultPosition,
                   const wxSize& size = wxDefaultSize,
                   const wxString& name = wxT("myClipSlider"));
        /** Default destructor */
        virtual ~myFrameSpin();
        void SetFps(double nfps) { fps=nfps; Update(); };
        double GetFps() { return fps; }
        void SetPosition(int32 npos);
        int32 GetPosition() { return pos; };
        void SetDuration(int32 ndur) { dur=ndur; Update(); };
        void myUpdate();// update the control
    private:
        wxTextCtrl* edit;// text control
        wxSpinButton* btn;// spin buttons
        double fps;// fps
        int32 pos;
        int32 dur;
        void myOnSize();
        // Event handlers for children:
        void OnSpin(wxSpinEvent& event);
        void OnEdit(wxCommandEvent& event);
    DECLARE_EVENT_TABLE()
    void OnSize(wxSizeEvent &event);
};
#endif // MYFRAMESPIN_H
myframespin.cpp:

Code: Select all

#include "myframespin.h"
BEGIN_EVENT_TABLE(myFrameSpin, wxPanel)
EVT_SIZE(myFrameSpin::OnSize)
END_EVENT_TABLE()
myFrameSpin::myFrameSpin(wxWindow *parent, wxWindowID id, const wxPoint &position, const wxSize& size, const wxString& name)
{   // Constructor:
    Create(parent,id,position,size,0,name);
    ...
    // Create children:
    btn = new wxSpinButton(this);
    Connect(btn->GetId(),wxEVT_SCROLL_THUMBTRACK,(wxObjectEventFunction)&myFrameSpin::OnSpin);
    edit = new wxTextCtrl(this,wxNewId(),_(""),wxDefaultPosition,wxDefaultSize,wxTE_RIGHT|wxTE_PROCESS_ENTER/**/);
    Connect(edit->GetId(),wxEVT_COMMAND_TEXT_ENTER,(wxObjectEventFunction)&myFrameSpin::OnEdit);
}
void myFrameSpin::myOnSize() { ... }
myFrameSpin::~myFrameSpin()
{   // Destructor:
    delete edit;
    delete btn;
}
void myFrameSpin::OnSize(wxSizeEvent &event) { myOnSize(); }
void myFrameSpin::myUpdate() { ... }
void myFrameSpin::OnSpin(wxSpinEvent& event)
{   // spin changed:
    SetPosition(event.GetInt());
}
void myFrameSpin::OnEdit(wxCommandEvent& event)
{   // edit changed:
    int32 p=StringToPosition(event.GetString(),fps);
    if(p>=0)    SetPosition(p);// set entered position
        else    myUpdate();// wrong input - return edit text back
}
void myFrameSpin::SetPosition(int32 npos)
{   // Set position:
    if(npos==pos) return;
    // position changed:
    pos=npos;
    myUpdate();
    // post an event:
    wxCommandEvent e(pos);
    wxPostEvent(GetParent(), e);
}
MyFrameSpinTestMain.cpp:

Code: Select all

MyFrameSpinTestDialog::MyFrameSpinTestDialog(wxWindow* parent,wxWindowID id)
{
    ...
    spin = new myFrameSpin(this,ID_CUSTOM1,wxDefaultPosition,wxSize(115,26),_T("ID_CUSTOM1"));
    ...
    Connect(ID_CUSTOM1,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnFrameChange);
    // See question 1) about the line above
}
void MyFrameSpinTestDialog::OnFrameChange(wxCommandEvent& event)
{   // my spin position changed:
    wxMessageBox(_("IT WORKS !!!"),_(""));
}
MyFrameSpinTestDialog::OnFrameChange never executes :(
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: New events in own class

Post by DavidHart »

1) How could I know which event-type (int) should I use in Connect(), if I don't specify any event-type (int) in wxPostEvent() ?
You specify it in the event's constructor. See http://wiki.wxwidgets.org/Custom_Events ... plest_case.
but if you wish, I can post almost everything:
Thanks, that's much easier (though still not a compilable program, so what follows isn't tested).

Try the following changes:

Code: Select all

        // post an event:
        wxCommandEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED); // Reuse this event-type, for convenience
        e.SetInt(pos); // Store the data in the event
        wxPostEvent(GetParent(), e);

Code: Select all

        MyFrameSpinTestDialog::MyFrameSpinTestDialog(wxWindow* parent,wxWindowID id)
    {
        ...
        spin = new myFrameSpin(this,ID_CUSTOM1,wxDefaultPosition,wxSize(115,26),_T("ID_CUSTOM1"));
        // Connect the correct event-type. We don't care about the ID, which wasn't set anyway
        // *Do* set the correct eventSink parameter, 'this'. It's good practice, and prevents the commonest error
        Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,wxSpinEventHandler(MyFrameSpinTestDialog::OnFrameChange), NULL, this);
    }
    void MyFrameSpinTestDialog::OnFrameChange(wxCommandEvent& event)
    {   // my spin position changed:
        wxMessageBox(wxString::Format(wxT("pos is %i"), event.GetInt()), wxT("")); // The data is in the 'int' member of the event
    }
Let me know if this works. Then I'll show you an easier way to do the same thing ;) .
Lightman
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sun Aug 14, 2011 11:24 am

Re: New events in own class

Post by Lightman »

Great, now it works, thanks!!
But I had to write
  • Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&MyFrameSpinTestDialog::OnFrameChange, NULL, this);
instead of
  • Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,wxSpinEventHandler(MyFrameSpinTestDialog::OnFrameChange), NULL, this);
...and what about an easier way of doing the same thing ? ;) This solution doesn't looks difficult - it just needs a bit more knowledge when I had.
Post Reply