How to detect which key was pressed by an user?
Tried to search web but couldn't find really anything interesting.
Thanks.
If you want to detect keypresses globally (useful for application shortcuts etc), you'll need to make one of your QObjects the eventFilter for the application, by first overloading QObject::eventFilter:
bool cKeyPressEater::eventFilter(QObject *Object, QEvent *Event)
{
if (Event->type() == QEvent::KeyPress)
{
QKeyEvent *KeyEvent = (QKeyEvent*)Event;
switch(KeyEvent->key())
{
case Qt::Key_F1:
//do something
break;
default:
break;
}
}
}
...and then installing that object as the eventFilter for your application:
QObject *KeyPressEater = GetYourEventFilterObject();
QCoreApplication::instance()->installEventFilter(KeyPressEater);
Otherwise, as #Mat says above, just overload QWidget::keyPressEvent. You'll need to setFocusPolicy and actually have focus in order to get the key presses.
Related
I have a Form with buttons called "Up", "Down", etc.
I need to process keyboard buttons (arrow_up, arrow_down, etc). Parallelly I want to set focus for relevant buttons.
For Example: If I pressed the arrow_down button on keyboard then the "Down" button would be a focus on my Form.
My variant how to do this:
bool ClientWindow::eventFilter(QObject *obj, QEvent *e)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *event= dynamic_cast<QKeyEvent*>(e);
switch(event->key()) {
case Qt::Key_Up: ui->up->setFocus(); ;break;
case Qt::Key_Down: ui->down->setFocus(); break;
case Qt::Key_Left: ui->left->setFocus(); break;
case Qt::Key_Right: ui->right->setFocus(); break;
}
}
return QObject::eventFilter(obj, event);
}
But, focus isn't set, and
1) If I return true from eventFilter then focus wouldn't set.
2) If I return QObject::eventFilter(obj, event) then focus would be transferred to next Object.
How to set focus for the relevant button?
Since you're trying to change a standard feature of Qt (arrow keys move focus just like TAB key does), you may want to try an extreme solution.
The point is: are your arrow key press events even being filtered at all? My guess is no. Something above (or before) the widget is consuming them and keeps the standard behavior unchanged (focus move from button to button according to their tab-indexes as usual).
I would try installing your filter onto the QApplication object, in the widget constructor this way:
qApp->installEventFilter(this);
The filter should look like this:
bool Widget::eventFilter(QObject *obj, QEvent *e)
{
if (e->type() == QEvent::KeyPress)
{
if(qApp->activeWindow() == this)
{
QKeyEvent *event= dynamic_cast<QKeyEvent*>(e);
switch(event->key()) {
case Qt::Key_Up: ui->up->setFocus(); break;
case Qt::Key_Down: ui->down->setFocus(); break;
case Qt::Key_Left: ui->left->setFocus(); break;
case Qt::Key_Right: ui->right->setFocus(); break;
default:
return qApp->eventFilter(obj, e);
}
return true;
}
}
return qApp->eventFilter(obj, e);
}
Please notice this line:
if(qApp->activeWindow() == this)
which should prevent the rule in the filter to be applied whenever the arrow keys get pressed (i.e. when the widget isn't the topmost one). You can ignore that, if your application hasn't other widget than this one.
Final advice: try to never change the standard UI behavior. Users do expect things to work as usual, and solutions that look absolutely cool to the developer, sometimes (often), brings no improvements at all and only confuse the user.
I Have a Form with several LineEdits and other Elements and want to jump from one to the next by pressing the return key.
I find Return key instead of/additional to the tab key more user friendly.
I can probably do it if i use returnPressed() and setFocus() but i hope there is a better more elegant solution for this.
Is there a way to modify the built in tab order to also work with
return?
If not, what are the best ways to accomplish the above?
You're probably looking for the event handler. ( QEvent )
Example from the doc
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_Tab) {
// special tab handling here
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
// custom event handling here
return true;
}
return QWidget::event(event);
}
It's easily adjustable to any key.
For example if return is pressed simulate a tab key press event.
As mentioned, it is more efficient to override QWidget::keyPressEvent
Some code:
protected:
void keyPressEvent(QKeyEvent *event) override;
void MyWidget::keyPressEvent(QKeyEvent *event){
if(event->key()==Qt::Key_Return){
this->focusNextChild();
}
}
My task sounds simple: I want to programmatically press keyboard buttons in Qt/C++. So I want some lines of code that makes the GUI think some keyboard keys was pressed.
I do not want to use windows specific API if possible.
Can this be done? If so how/where should I get started?
The only reliable way to simulate/generate a user key event, is to use the QtTest module.
#include <QtTest>
#define QT_WIDGETS_LIB
#include <qtestkeyboard.h>
...
// For example:
QTest::keyClick(QApplication::focusWidget(), key, Qt::NoModifier);
// or even more low level:
qt_handleKeyEvent(widget->windowHandle(), QEvent::KeyPress, key, Qt::NoModifier);
qt_handleKeyEvent(widget->windowHandle(), QEvent::KeyRelease, key, Qt::NoModifier);
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_Tab) {
// special tab handling here
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
// custom event handling here
return true;
}
return QWidget::event(event);
}
This is a simple example from here. The main idea is that you have to send KeyPress event to your window object. Here is another good example.
I have a Qt program with a QGraphicScene inside a QGraphicsView on top of QMainWindow.
The events are handled by the QMainWindow using an eventFilter function.
The function body looks similar to this code:
bool Window::eventFilter(QObject *, QEvent *event) {
QEvent::Type type = event->type();
if (type == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch(keyEvent->key()) {
case Qt::Key_A:
case Qt::Key_B:
case Qt::Key_C:
case Qt::Key_D:
// call a function that uses the current mouse position on the graphics scene
break;
default:
QLocale loc = QApplication::keyboardInputLocale();
if(loc.language() != QLocale::English) {
QString message = "A non-English key was pressed";
showMessage(message, QMessageBox::Warning);
}
}
return true;
}
return false;
}
I recently added the "default" part and since then the coordinates that are used in the A,B,C,D cases are totally wrong. In addition, if I add a simple cout print anywhere in the function the bug disappears and the correct mouse coordinates are used.
What can possibly cause this?
Extraneous Information:
I am attempting to build an application using Qt. This application features a QMdiArea and a child-window. My child-window will have a menu which can be integrated into the QMdiArea or segregated and attached to the child itself. Though, this is a bit more detail than needed...
Problem:
I would like my child-widget to have a menu with a shortcut, "CTRL+W." But, because I am using a QMdiArea, the shortcut is already used causing:
QAction::eventFilter: Ambiguous shortcut overload: Ctrl+W
How can I get rid of this shortcut and claim it within my child widget instead?
Update:
Here is what I've tried with no luck:
class MDI : public QMdiArea
{
Q_OBJECT
private:
bool event(QEvent *tEvent)
{
if (tEvent->type() == QEvent::KeyPress)
{
QKeyEvent* ke = static_cast<QKeyEvent*>(tEvent);
if (ke->key()== Qt::Key_W && ke->modifiers() & Qt::ControlModifier)
emit KeyCW();
return true;
}
return QMdiArea::event(tEvent);
}
public:
signals:
void KeyCW();
};
This works if I do something as simple as change Qt::Key_W to Qt::Key_L. The key-combo is received and event is thrown. With W, it just never happens. I've also tried moving event to QMainWindow as well as an eventFilter in the subwindow to QMdiArea. It seems that it is a little overly complicated to do something as simple as remove default key-handlers from within QMdiArea.
You can disable this shortcut like this:
for( QAction *action : subWindow->systemMenu()->actions() ) {
if( action->shortcut() == QKeySequence( QKeySequence::Close ) ) {
action->setShortcut( QKeySequence() );
break;
}
}
You could get rid of the pre-defined close action of the QMdiSubWindow altogether by using Qt::CustomizeWindowHint as additional flag when adding the subwindow.
QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(internalWidget2,
Qt::Widget | Qt::CustomizeWindowHint |
Qt::WindowMinMaxButtonsHint);
Subclass QMdiArea and reimplement keyPressEvent(). That should work.
void keyPressEvent(QKeyEvent* event){
if(event->key() == Qt::Key_W and event->modifiers() & Qt::ControlModifier){
// handle it
}else{
return QMdiArea::keyPressEvent(event);
}
}
You could also use event filters. I don't enough about your class hierarchy, but I hope you get the idea.
bool CustomMdiArea::eventFilter(QObject *object, QEvent *event){
if(object == yourChildWindow && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_W and keyEvent->modifiers() & Qt::ControlModifier) {
//handle it
return true;
}else{
return false;
}
}
return false;
}
From what I can tell, what I am looking for is not possible short of writing my own MDIArea.
The shortcut is set in QMdiSubWindowPrivate::createSystemMenu() during
the construction of a QMdiSubWindow, I doubt that you can remove it
without having to patch Qt libs.
Hopefully at some point someone will disprove this or QT will make changes. Meanwhile, it looks like we will all need to stay away from these pre-assigned shortcuts.
I was able to work around this by setting the shortcut context for my close action. By setting it to Qt::WidgetShortcut, I no longer get the ambiguous shortcut overload. Here is how I'm setting up my close action now:
closeAction = new QAction(tr("&Close"), this);
closeAction->setShortcut(Qt::CTRL|Qt::Key_W);
closeAction->setShortcutContext(Qt::WidgetShortcut);
connect(closeAction, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow()));