wxAcceleratorTable - what I am doing wrong? Topic is solved

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.
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

wxAcceleratorTable - what I am doing wrong?

Post 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);
}
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxAcceleratorTable - what I am doing wrong?

Post 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
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

Re: wxAcceleratorTable - what I am doing wrong?

Post 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.
catalin
Moderator
Moderator
Posts: 1618
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Re: wxAcceleratorTable - what I am doing wrong?

Post 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?
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

Re: wxAcceleratorTable - what I am doing wrong?

Post 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.
catalin
Moderator
Moderator
Posts: 1618
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Re: wxAcceleratorTable - what I am doing wrong?

Post 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...
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

Re: wxAcceleratorTable - what I am doing wrong?

Post by Jacek Poplawski »

That's interesting info, what's your system? Are you on Linux and using wxgtk?
catalin
Moderator
Moderator
Posts: 1618
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Re: wxAcceleratorTable - what I am doing wrong?

Post by catalin »

Jacek Poplawski wrote:what's your system?
MSW
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxAcceleratorTable - what I am doing wrong?

Post 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;
    }
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

Re: wxAcceleratorTable - what I am doing wrong?

Post 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"
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxAcceleratorTable - what I am doing wrong?

Post 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.
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

Re: wxAcceleratorTable - what I am doing wrong?

Post 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!).
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxAcceleratorTable - what I am doing wrong?

Post 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.
Jacek Poplawski
Knows some wx things
Knows some wx things
Posts: 38
Joined: Mon Jun 20, 2011 12:03 pm

Re: wxAcceleratorTable - what I am doing wrong?

Post by Jacek Poplawski »

In the hello world example, the Filter Events detects key only when is set focus to something.
DavidHart
Site Admin
Site Admin
Posts: 4252
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Re: wxAcceleratorTable - what I am doing wrong?

Post 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.
Post Reply