Catching key events globally

From WxWiki

Jump to: navigation, search

Keyboard events go to the component that currently has focus and do not propagate to the parent; if you are trying to catch key events globally it can thus be a little tricky. Here are a few ways to solve this problem - keep in mind there are probably more than presented here.

Contents

[edit] Filter events

Source: http://wxforum.shadonet.com/viewtopic.php?p=66017#66017

Override wxApp::FilterEvent. This function is called early in event-processing, so you can do things like this (for F1):

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

This will work most of the time. It will fail if

  • Your application is not in the foreground. If you wish to catch keys even when your app is in the background, you will need to use platform-specific code
  • Your window-manager grabs the key for itself, in which case your app won't see it.
  • (Using wxGtk---I don't know about other platforms) the key is pressed while a modal wxDialog is showing, and the dialog doesn't have keyboard focus. The solution is to call SetFocus() on one of the dialog's children before calling ShowModal().

[edit] Recursive connect

You can recursively ->Connect() all components in a frame, therefore you will catch events no matter where the focus is.

void
MyDialog::createChilds(void)
{
  // create the childs: panels, textctrls, buttons, ...
 
  // ...
 
  /////////////////////////////////////
  // after creation:
  this->connectKeyDownEvent(this);
 
}
 
 
void
MyDialog::connectKeyDownEvent(wxWindow* pclComponent)
{
  if(pclComponent)
  {
    pclComponent->Connect(wxID_ANY,
                          wxEVT_KEY_DOWN,
                          wxKeyEventHandler(MyDialog::onKeyDown),
                          (wxObject*) NULL,
                          this);
 
    wxWindowListNode* pclNode = pclComponent->GetChildren().GetFirst();
    while(pclNode)
    {
      wxWindow* pclChild = pclNode->GetData();
      this->connectKeyDownEvent(pclChild);
     
      pclNode = pclNode->GetNext();
    }
  }
}
 
void
MyDialog::onKeyDown(wxKeyEvent& event)
{
  // do your event-processing here
 
}

Code author : clyde729 (Source : http://wxforum.shadonet.com/viewtopic.php?t=9361)


Also note that many components will only receive key events if they have the wxWANTS_CHARS style flag enabled; then, you need to catch EVT_CHAR rather than or in addition to EVT_KEY_DOWN. For catching Enter presses on text controls, use style flag wxTE_PROCESS_ENTER, and catch event EVT_TEXT_ENTER.

[edit] Make a custom event handler

This code is inspired from code by Rostfrei. With it, you can simply create a class like the CEventPropagator below, then call the 'registerFor' function when your frame/panel is created (note that in the code below the 'registerFor' function is not recursive, so will only affect direct children, though it could be modified to be)

// Inspired from code by 'rostfrei'
 
#include "wx/wx.h"
#include <iostream>
 
// ------------------------------------------------------------------------------------
// The CEventPropagator cutom event handler : it forces key events to propagate up
 
class CEventPropagator : public wxEvtHandler
{
public:
    CEventPropagator();
    static void registerFor(wxWindow* win);
 
private:
    void onKeyDown(wxKeyEvent& aEvent);
    void onKeyUp(wxKeyEvent& aEvent);
};
 
 
// ------------------------------------------------------------------------------------
 
CEventPropagator::CEventPropagator()
{
    // Event connections
    this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEventPropagator::onKeyDown));
    this->Connect(wxEVT_KEY_UP,   wxKeyEventHandler(CEventPropagator::onKeyUp));
}
 
void CEventPropagator::onKeyDown(wxKeyEvent& aEvent)
{
    //printf("CEventPropagator::onKeyDown\n");
    aEvent.ResumePropagation(1);
    aEvent.Skip();
}
 
void CEventPropagator::onKeyUp(wxKeyEvent& aEvent)
{
    //printf("CEventPropagator::onKeyUp\n");
    aEvent.ResumePropagation(1);
    aEvent.Skip();
}
 
void CEventPropagator::registerFor(wxWindow* win)
{
    wxWindowListNode* childNode = win->GetChildren().GetFirst();
    while (childNode)
    {
        childNode->GetData()->PushEventHandler(new CEventPropagator());
        childNode = childNode->GetNext();
    } 
}
 
// ------------------------------------------------------------------------------------
// Example of use
 
class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(NULL, wxID_ANY,  wxT("Hello wxWidgets"), wxPoint(50,50), wxSize(800,600))
    {
        wxPanel* panel = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        
        for (int n=0; n<15; n++)
        {
            sizer->Add(new wxTextCtrl(panel, wxID_ANY, wxT("Some Widget")), 0, wxALL, 5);
        }
        panel->SetSizer(sizer);
                          
        CEventPropagator::registerFor(panel);
        
        panel->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MyFrame::onKeyDown), NULL, this);
 
    }
    
    void onKeyDown(wxKeyEvent& evt)
    {
        std::cout << "Pressed key {" << evt.GetKeyCode() << "}\n";
        evt.Skip();
    }
    
};
 
class MyApp: public wxApp
{
    wxFrame* m_frame;
public:
    
    bool OnInit()
    {
        m_frame = new MyFrame();
        m_frame->Show();
        return true;
    } 
    
};
 
IMPLEMENT_APP(MyApp)

[edit] System-wide Hot Key

bool wxWindow::RegisterHotKey(int hotkeyId, int modifiers, int virtualKeyCode)

Note: This is currently (wxWidgets 2.8.11) Windows only

[edit] Menu Hot key

You can also make a hot key part of a menu item

editMenu->Append(MENU_EDIT_COPY,  _("Copy\tCtrl-C"));

[edit] Accelerator table

MyFrame::MyFrame(...) : wxFrame
{
    wxAcceleratorEntry entries[14];
    entries[0].Set(wxACCEL_CTRL, (int) 'O', MENU_LOAD_FILE);
    ...
    entries[12].Set(wxACCEL_NORMAL, WXK_SPACE, KEY_SPACE);
    entries[13].Set(wxACCEL_NORMAL, WXK_ESCAPE, KEY_ESC);
    wxAcceleratorTable accel(14, entries);
    SetAcceleratorTable(accel);
}
 
...
 
EVT_MENU(KEY_ESC,MyFrame::CheckAbort)
EVT_MENU(KEY_SPACE,MyFrame::CheckAbort)

Source : http://wxforum.shadonet.com/viewtopic.php?t=18140 Author : celstark

[edit] Regular Polling

This is helpful to get a game-like behaviour : you can simply poll for key states using wxGetKeyState() (for instance in a wxTimer, or in idle events, etc.)

Personal tools