Page 1 of 2

wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 10:31 am
by Jacek Poplawski
Hello,

i wanted to add keyboard support for my application, I read a lot about reading keys in wxWidgets and tried all possible methods, included the one from wiki which should always work, and I failed.
So I just added wxAcceleratorTable to hello world application to make it work, but still there is no effect.
Could you look at the code and tell me what I am doing wrong?

I use wxgtk 2.8.12 on Arch Linux

Code: Select all

#include "wx/wx.h"

class MyApp: public wxApp
{
    virtual bool OnInit();
};

class MyFrame: public wxFrame
{
public:

    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);

    void OnQuit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);

    DECLARE_EVENT_TABLE()
};

enum
{
    ID_Quit = 1,
    ID_About,
};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(ID_Quit, MyFrame::OnQuit)
    EVT_MENU(ID_About, MyFrame::OnAbout)
END_EVENT_TABLE()

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    MyFrame *frame = new MyFrame( _("Hello World"), wxPoint(50, 50),
                                  wxSize(450,340) );

    wxAcceleratorEntry entries[4];
    entries[0].Set(wxACCEL_CTRL,  (int) 'N',     ID_About);
    entries[1].Set(wxACCEL_CTRL,  (int) 'X',     wxID_EXIT);
    entries[2].Set(wxACCEL_SHIFT, (int) 'A',     ID_About);
    entries[3].Set(wxACCEL_NORMAL,  WXK_DELETE,    wxID_CUT);
    wxAcceleratorTable accel(4, entries);
    frame->SetAcceleratorTable(accel);

    frame->Show(true);
    SetTopWindow(frame);
    return true;
}

MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame( NULL, -1, title, pos, size )
{
    wxMenu *menuFile = new wxMenu;

    menuFile->Append( ID_About, _("&About...") );
    menuFile->AppendSeparator();
    menuFile->Append( ID_Quit, _("E&xit") );

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, _("&File") );

    SetMenuBar( menuBar );

    CreateStatusBar();
    SetStatusText( _("Welcome to wxWidgets!") );
}

void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    Close(TRUE);
}

void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
    wxMessageBox( _("This is a wxWidgets Hello world sample"),
                  _("About Hello World"),
                  wxOK | wxICON_INFORMATION, this);
}

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 11:27 am
by DavidHart
Hi Jacek,

It probably will work if you give the frame focus by clicking on it, or add a SetFocus() call to your code.

However frames/dialogs/panels don't much like having keyboard focus, so by default any child control that exists will get the focus instead. For most apps, that's what you want; but it does mean that you need to put your accelerator table in the child control instead of the frame, or use one of the other methods from wxWiki. They'll all need you to set the focus somewhere, though.

Regards,

David

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 11:50 am
by Jacek Poplawski
Just like I wrote I already tried. SetFocus was also thing I tried and as you can see in the example there are no other widgets inside frame.

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 12:03 pm
by catalin
FWIW looking at the code the only combinations that should work are Ctrl+N and Shift+A, both for OnAbout. The other ids are either not assigned (wxID_CUT, wxID_EXIT) or misspelled (ID_QUIT != wxID_EXIT).
Did you check the ones for OnAbout?

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 12:12 pm
by Jacek Poplawski
Yes the goal was to make at least about to work, since it works from menu and is easy to detect.
Is there any problem with compiling this code on your system...? It's just a hello world example with wxAcceleratorTable example from website.

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 12:26 pm
by catalin
It's flawless on my machine. Both accelerators for OnAbout work.
Could you be running the wrong exe? Check your paths, maybe something is setup in the wrong way...

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 12:29 pm
by Jacek Poplawski
That's interesting info, what's your system? Are you on Linux and using wxgtk?

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 12:40 pm
by catalin
Jacek Poplawski wrote:what's your system?
MSW

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 2:12 pm
by DavidHart
It probably will work if you give the frame focus by clicking on it, or add a SetFocus() call to your code.
No, you're right: it doesn't.

However adding a wxTextCtrl and giving that the accelerator table does work (wxGTK-2.8.12, debian squeeze):

Code: Select all

    bool MyApp::OnInit()
    {
        MyFrame *frame = new MyFrame( _("Hello World"), wxPoint(50, 50),
                                      wxSize(450,340) );
        wxTextCtrl* text = new wxTextCtrl(frame, wxID_ANY, wxT("Some text"));
        wxAcceleratorEntry entries[4];
        entries[0].Set(wxACCEL_CTRL,  (int) 'N',     ID_About);
        entries[1].Set(wxACCEL_CTRL,  (int) 'X',     wxID_EXIT);
        entries[2].Set(wxACCEL_SHIFT, (int) 'A',     ID_About);
        entries[3].Set(wxACCEL_NORMAL,  WXK_DELETE,    wxID_CUT);
        wxAcceleratorTable accel(4, entries);
        text->SetAcceleratorTable(accel);

        frame->Show(true);
        SetTopWindow(frame);
        return true;
    }

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 2:27 pm
by Jacek Poplawski
David thanks for finding it out.

I am author of http://code.google.com/p/delaboratory/
could you try to add this accelerator table for instance to main_frame.cc?

main_frame has lots of children, I tried to add it to the image panel or to the frame, but it never worked, that's why I started with hello world to find where exactly is the problem, BTW there is also a list in my application (right panel), should I try to put accelerator there somehow?

what exactly are the rules of keyboard access in wxWidgets? I read wiki and ideas about propagating events to all widgets, but I had impression that accelerator should be something more general, more "top level"

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 3:11 pm
by DavidHart
could you try to add this accelerator table for instance to main_frame.cc?
I don't see why not (unless some of the panels have child controls), but you'd need one for each panel. And if it won't work in any that do have children, as a child will have focus.
what exactly are the rules of keyboard access in wxWidgets?
The intention is that keypresses stay local to the focused control, so an eventhandler in a parent panel or frame won't see them. (This doesn't seem to happen in wxMSW, which might be considered a bug.)

However wxAcceleratorTable produces wxCommandEvents, not wxKeyEvents; and these do propagate. So if every relevant control is given an accelerator table, you can handle the resulting events in just one place (assuming the controls all have the same parent).

That's fine for 2 or 3 children, but less useful for lots (though if they're all of the same type of control, you can subclass and do it only once, of course). That's where the wxWiki page can help.
e.g. I use the wxApp:FilterEvent method to catch F1:

Code: Select all

int MyApp::FilterEvent(wxEvent& event)
{
if  (frame && event.GetEventType()==wxEVT_KEY_DOWN && ((wxKeyEvent&)event).GetKeyCode()==WXK_F1 )
  { frame->OnHelpF1( (wxKeyEvent&)event ); return true; }

return -1;
}
which is less painful than creating dozens of accelerator tables.

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 3:17 pm
by Jacek Poplawski
From my orignal post "included the one from wiki which should always work", and I meant the one you just pasted, I put this method into delaboratory.cc and never received any keyboard events (but I tested that mouse events where there!).

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Tue Sep 06, 2011 3:51 pm
by DavidHart
It works reliably for me (and other people). Try again, perhaps, but click on the app first.

If it still doesn't work for you there, try adding it to your 'minimal' sample.

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Wed Sep 07, 2011 11:04 am
by Jacek Poplawski
In the hello world example, the Filter Events detects key only when is set focus to something.

Re: wxAcceleratorTable - what I am doing wrong?

Posted: Wed Sep 07, 2011 11:37 am
by DavidHart
Filter Events detects key only when is set focus to something.
Yes. There's no known solution to that in wxGTK: it doesn't have hotkeys.