How to detect global key sequence press in Qt? - c++

I want to detect whether a key sequence is pressed and want to perform certain task on that event in Qt. Currently I can detect keypresses for certain widgets but how to detect global keypresses. By global I mean that even if the application is minimized or hidden , it should detect key press.
I tried making an eventFilter for the application, by first overloading QObject::eventFilter like this:
bool GlobalEventFilter::eventFilter(QObject *Object, QEvent *Event)
{
if (Event->type() == QEvent::KeyPress)
{
QKeyEvent *KeyEvent = (QKeyEvent*)Event;
switch(KeyEvent->key())
{
case Qt::Key_F1:
cout<<"F1 press detected"<<endl;
return true;
default:
break;
}
}
return QObject::eventFilter(Object,Event);
}
and then installing that object as the eventFilter for my application:
QApplication a(argc,argv);
a.installEventFilter(new GlobalEventFilter());
and I also tried the doing this:
QCoreApplication::instance()->installEventFilter(new GlobalEventFilter());
In both the cases I am able to detect key presses when my application window is open but it fails when window is minimized or hidden. How to solve this?

See the documentation of QKeyEvent:
Key events are sent to the widget with keyboard input focus when keys are pressed or released.
That means, if you wish to use QKeyEvent, you need to have the keyboard focus. Filtering those events does not change this conceptually either. I am not sure where you got the filtering idea from.
You might want to look into alternative solutions, for instance how it is implemented in kwin, etc. In general, be careful with this use case. It could do weird things without the end users noticing it.
You could take a look at this class:
QxtGlobalShortcut Class Reference
You could write something like this then:
main.cpp
#include <QxtGlobalShortcut>
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
class MyGlobalShortcutHandler : public QObject
{
Q_OBJECT
public:
explicit MyGlobalShortcutHandler(QObject *parent = 0)
: QObject(parent)
{
m_globalShortcut.setShortcut(QKeySequence("Ctrl+Shift+X"));
m_globalShortcut.setEnabled(true);
connect(&m_globalShortcut, SIGNAL(activated()), SLOT(handleGlobalShortcut()));
}
public slots:
void handleGlobalShortcut()
{
qDebug() << "Global shortcut handled";
}
private:
QxtGlobalShortcut m_globalShortcut;
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
QMainWindow mainWindow;
MyGlobalShortcutHandler myGlobalShortcut();
mainWindow.show();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += qxt
QXT = core gui
SOURCES += main.cpp
Build and Run
qmake-qt4 && make && ./main
This code should be working with Qt 4, at least. It prints out the debug statement for me. You could easily verify this on your end.
To get the libqxt library, please visit the following link:
http://dev.libqxt.org/libqxt/wiki/Home

This feature is not implemented in Qt. You can use Qxt. Qxt is an extension library for Qt providing a suite of cross-platform utility classes to add functionality not readily available in Qt. It has Global Shortcut (hot keys) which detects key presses even if the application is minimized.
After compiling and linking your application to Qxt, you can include QxtGlobalShortcut:
#include <QxtGlobalShortcut>
Example usage:
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));

Related

can't close app from dialog box (Qt app)

Getting right to it I have a MainWindow and a dialog window which is executed if a condition is met but the problem is I can't get the app to quit if the cancel button from the dialog window is clicked. I've tried putting qApp->quit() in the slot function for the cancel button. I've tried connecting the cancel button slot to the predefined close slot for the MainWindow object via a clickme() signal from the dialog class. (as shown below)
qt application does not quit I read the answer to this question which I think got me close because it made me realize that I can't quit the app before showing the MainWindow but making that change didn't solve the problem. I even tried to explicitly emit the clickme() signal from cancel button slot but that actually caused the OS to throw a signal which threw an error at me saying "the inferior stopped because it received a signal from the operating system signal name: SIGSEGV
signal meaning: segmentation fault"
Here's my code:
Notice warning; // this is the object for the dialog window also all of this code is in main.cpp
warning.setModal(true);
QObject::connect(&warning, SIGNAL(clickme()), &warning, SLOT(on_Cancel_button_clicked()));
QObject::connect(&warning, SIGNAL(clickme()), &w, SLOT(close()));
warning.exec();
Also before that code is
MainWindow w;
w.show();
Also while writing this question I tried this
QObject::connect(&warning, SIGNAL(clickme()), qApp, SLOT(quit()));
But that still didn't work. If you need more info just let me know.
Update: I'm starting to think that the reason I'm having so much trouble with this connect signal/slot function is because it's not designed to connect two windows of two different classes and I should rework my app to do everything from the MainWindow class which is a shame because when I picture a GUI program I picture multiple windows connected to each other regardless of whether or not the object representing each window is from the same class as the others yet I have such a hard time trying do that with the QT framework when it comes to trying to connect objects of different classes.
Update: please forgive me. I assume that the code that I originally thought was the answer would work and took a break from working on the program before actually testing out that code. Going back to it now I discovered that it doesn't work. The code I'm referring to is the following
QMessageBox msg;
msg.setText("Continue?");
msg.addButton(QMessageBox::Yes);
msg.addButton(QMessageBox::No);
QObject::connect(&msg, &QMessageBox::rejected,
&app, &QApplication::quit); // this line doesn't work for me and I don't know why
QObject::connect(&msg, &QMessageBox::accepted, [&dlg]{
(new QLabel("I'm running")).show();
});
QApp->quit(); should work. Remove warning.setModal(true); The dialog becomes modal when you call exec(). SetModal(true) should be used with show() according to Qt docs. So this may be causing your problem.
I think I've found the problem.
Probably, you're calling exec() twice:
To enter the QApplicationevent loop
To execute the dialog.
Use show() instead of exec() for the dialog. You have an example below where you can check the signal/slot works well. In your application, you need the slot to close the window, but:
With the line of code dialog.exec();, the app keeps running. This is your issue.
With the line of code dialog.show();, the app stops.
By the way, I saw your last question update, but it is not correct. In fact, of course you can connect two different classes.
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QApplication>
#include <QMainWindow>
#include <QAbstractButton>
#include <QDebug>
#include "dialog.h"
class Window : public QMainWindow
{
Q_OBJECT
public:
Window()
{
dialog = new Dialog();
dialog->setText("Continue?");
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
auto onClick = [this]() {
auto role = dialog->buttonRole(dialog->clickedButton());
if (role == QMessageBox::NoRole) {
qDebug() << "QMessageBox::NoRole";
QApplication::quit();
}
if (role == QMessageBox::YesRole) {
qDebug() << "QMessageBox::YesRole";
}
};
QObject::connect(dialog, &QMessageBox::buttonClicked, onClick);
dialog->show(); // this must be show(), not exec()
}
virtual ~Window() { delete dialog; }
private:
Dialog *dialog;
public slots:
void windowSlot() { qDebug() << Q_FUNC_INFO;
close();
}
};
#endif // WINDOW_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QMessageBox>
class Dialog : public QMessageBox
{
Q_OBJECT
public:
Dialog() {}
virtual ~Dialog() {}
};
#endif // DIALOG_H
main.cpp
#include <QApplication>
#include <QtGui>
#include "window.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.setWindowTitle("window");
window.show();
return app.exec();
}
Update #1: a very interesting post.

QT 5.6.1 app crashes after exiting slot only on x86 arch but not x64

I am facing an issue that i cannot understand and i need your lights. I have found no answers on my problem on others topics yet.
Context :
I am using QT 5.6.1 compiled dynamically (x86 and x64) on a Windows 10 x64.
Problem :
I have made a basic program compiled twice (x86 and x64) that just display a Window with a button and connects the button to the "clicked" signal. My Window is correctly displayed but when i hit my button to fire signal the app crashed just after exiting the SLOT connected to the SIGNAL (qDebug is correctly called from the slot). But i am only facing this issue with x86 QT dll's...
Here is the code:
.cpp:
MyWidget::MyWidget()
{
QMainWindow *pqMainWindow= new QMainWindow(this);
QPushButton *pqButton= new QPushButton("MyButton");
/* Setting up the window */
pqMainWindow->setWindowModality(Qt::WindowModal);
pqMainWindow->setGeometry(geometry());
pqMainWindow->move(QPoint(100, 100));
/* Connecting signal clicked to slot */
QObject::connect(pqButton, SIGNAL(clicked(bool)), this, SLOT(_onMyActionTriggered(bool)));
pqMainWindow->setCentralWidget(pqButton);
/* Showing the window */
pqMainWindow->show();
}
MyWidget::~MyWidget()
{
/* Nothing to do yet */
}
void MyWidget::_onMyActionTriggered(bool bValue)
{
qDebug("Slot <_onMyActionTriggered> called");
}
int __cdecl main(int argc, char **argv)
{
QApplication qapp(argc, argv);
MyWidget widget;
return qapp.exec();
}
.h
class MyWidget: public QWidget
{
Q_OBJECT;
public:
MyWidget();
virtual ~MyWidget();
private slots:
void _onMyActionTriggered(bool bValue);
private:
};
Here is the call trace :
Qt5Widgets!QAction::activate+0x103
Qt5Widgets!QToolButton::nextCheckState+0x1a
Qt5Widgets!QAbstractButton::click+0x103
Qt5Widgets!QAbstractButton::mouseReleaseEvent+0x7e
Qt5Widgets!QToolButton::mouseReleaseEvent+0xd
Qt5Widgets!QWidget::event+0xa8
Qt5Widgets!QSizePolicy::QSizePolicy+0x83b
Qt5Core!QCoreApplication::translate+0x30f56
Qt5Gui!QGuiApplicationPrivate::processMouseEvent+0x6c1
USER32!SetManipulationInputTarget+0x53
USER32!DispatchMessageW+0x251
USER32!DispatchMessageW+0x10
qwindows!qt_plugin_query_metadata+0x2065
Qt5Core!QCoreApplication::exec+0x160
qt_auth_test!wmain+0x7c
qt_auth_test!QObject::event+0xb5
KERNEL32!BaseThreadInitThunk+0x24
ntdll!RtlUnicodeStringToInteger+0x21e
ntdll!RtlCaptureContext+0xe1
I have compiled QT myself but i have the same result when using dll and lib downloaded on QT Website.
Thanks everyone for your help, i've finally found the issue.
Here is the answer :
My program uses the stdcall (/Gz) convention but QT uses cdecl (/Gd). The generated moc file was compiled with the stdcall convention and this is what caused the issue (stack problem when releasing parameters because of different convention between callee and caller).
Now my program is compiled using the stdcall convention (because it is mandatory for me) BUT the generated moc files are compiled with cdecl and i have set the __cdecl keyword on my private slots headers! Now It works perfectly!
Once again, thank for your time. I hope this topic will help someone one day with a similar issue.
Setting window modality on a child of a hidden widget makes no sense. Perhaps that's a problem. Try the following, and check if/when it crashes. There definitely is a Qt 5.6 bug on OS X at least: when the button's modality is reverted, the widget still doesn't receive input and you must exit the application by other means.
#include <QtWidgets>
int main(int argc, char **argv)
{
int n = 0;
QApplication app{argc, argv};
QPushButton widget{"Widget"};
QPushButton button{"Button", &widget};
button.setWindowFlags(Qt::Window);
QObject::connect(&button, &QPushButton::clicked, [&]{
button.setText(button.text().append("."));
++n;
if (n == 5) {
button.setWindowModality(Qt::WindowModal);
button.hide();
button.show();
}
else if (n == 10) {
button.setWindowModality(Qt::NonModal);
button.hide();
button.show();
}
else if (n == 15)
app.quit();
});
button.move(widget.geometry().bottomRight());
widget.show();
button.show();
return app.exec();
}

Qt and dead keys in a custom widget

Issue
I'm unable to make dead keys work in my Qt program, while on the same system Qt applications (konsole or kmail for instance) are correctly processing them.
How to reproduce
testcase.pro
TEMPLATE = app
TARGET = testcase
INCLUDEPATH += .
QT += core widgets gui
HEADERS += testcase.hpp
SOURCES += testcase.cpp
testcase.hpp
#include <QWidget>
class TestWindow: public QWidget
{
Q_OBJECT
public:
TestWindow(QWidget* parent=0, Qt::WindowFlags flags=0);
void keyPressEvent(QKeyEvent* event);
};
testcase.cpp
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QKeyEvent>
#include "testcase.hpp"
TestWindow::TestWindow(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
setAttribute(Qt::WA_KeyCompression);
}
void TestWindow::keyPressEvent(QKeyEvent* event)
{
qDebug() << event;
}
int main(int argc, char** argv)
{
QApplication app(argc, argv);
TestWindow mainWin;
mainWin.show();
return app.exec();
}
Compile the above program (qmake; make), launch it. Dead keys give for instance:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 45, 0, ""e"", false, 1)
I was expecting
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
This would also be acceptable:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
What I've tried
I'm using a Ubuntu 14.10 system with locale fr_FR.UTF-8
I've tried
with Qt 5.3.0 and Qt 4.8.6 as provided on the system.
unseting XMODIFIERS (the default value is #im=ibus is being reported an issue by some)
changing the locale (again, google find reports were the part after the dot is an issue, I've tried the 4 variants UTF-8, utf-8, UTF8 and utf8)
with and without the setAttribute(Qt::WA_KeyCompression); in the constructor.
None changed my observable behavior.
Searching the web show mainly (only?) system related issues. As written above, I've tried the proposed solution, that doesn't solve my problem and the fact that other Qt applications I've tried are able to process the dead key makes me think I miss something in my code, especially that with a slightly more complex example, I'm able to use dead keys with Qt provided widgets (for instance a QLineEdit).
This is a partial answer, but as nobody answered that could be useful for someone later.
Dead keys are handled by input methods which are also handling the compose key and the way Chinese characters and others may be entered.
The widget must signify that it handles input method:
setAttribute(Qt::WA_InputMethodEnabled, true);
Then it has to overwrite two virtual members:
void inputMethodEvent(QInputMethodEvent*);
QVariant inputMethodQuery(Qt::InputMethodQuery) const;
To handle the dead keys and compose, it seems to be enough to have
void TestWindow::inputMethodEvent(QInputMethodEvent* event)
{
if (!event->commitString().isEmpty()) {
QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier,
event->commitString());
keyPressEvent(&keyEvent);
}
event->accept();
}
QVariant TestWindow::inputMethodQuery(Qt::InputMethodQuery) const
{
return QVariant();
}
but that's were my answer is incomplete:
I'm not sure it is really enough even for that
I'm sure it is not enough for more complex writing system and I lack the prerequisite to understand the documentation I've found.

Using Qt's signals in a non Qt application (C++)

I have a lot of existing code using Qt, and more specifically Qt signals and slots to time specific actions.
Now I need to use this code within a new application which is not a Qt application (and cannot be - I am writing a plugin to visual studio). Anyway - how can I get the existing code to actually intercept the signals and activate the relevant slots?
Do I need to somehow create a dummy Qt application? If so - how do I cause it to process the signals without becoming a blocking loop to the rest of my code?
It seems that if you write something like this, the "Test" message is still printed even though there is no event loop, so this could be a clue:
#include <QObject>
#include <QCoreApplication>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent) : QObject(parent) {}
void testMethod() { emit testSignal(); }
signals:
void testSignal();
public slots:
void testSlot() { qDebug() << "Test"; }
};
#include "main.moc"
int main(int argc, char **argv)
{
// QCoreApplication coreApplication(argc, argv);
MyClass myObject(0);
QObject::connect(&myObject, SIGNAL(testSignal()), &myObject, SLOT(testSlot()));
myObject.testMethod();
// return coreApplication.exec();
return 0;
}
This way, you would still need Qt, but you could avoid having a "blocking" event loop. However, it might be simpler to just rearrange the code from the signal-slot layering to direct calls, depending on how many direct calls you would need to do for a signal emitted.
This is a common problem when using ASIO and Qt together. The solution I have found is to make a Broker object, and run the Qt and ASIO loops on their own threads. The Broker is the target for emit calls to the ASIO message queue, and the emitter to the Qt message queue. Take care of the syncronisation/data copying in the Broker.

Why doesn't my QDeclarativeItem get any mouse/keyboard events?

This is a reduced down, minimum complete example demonstrating my problem:
I have an application which hosts a QDeclarativeView; file events.cpp:
#include <QApplication>
#include <QDeclarativeView>
#include "TestItem.h"
int main(int argc,char* argv[]) {
QApplication app(argc,argv);
qmlRegisterType<TestItem>("Testing",1,0,"Tester");
QDeclarativeView page;
page.setSource(QUrl("page.qml"));
Q_ASSERT(page.status()==QDeclarativeView::Ready);
page.show();
return app.exec();
}
That TestItem is a subclassed QDeclarativeItem defined in file TestItem.h:
#ifndef _TestItem_h_
#define _TestItem_h_
#include <iostream>
#include <QDeclarativeItem>
#include <QPainter>
class TestItem : public QDeclarativeItem {
Q_OBJECT
public:
TestItem() {
setFlag(QGraphicsItem::ItemHasNoContents,false);
std::cerr << "[TestItem created]";
}
void paint(QPainter* painter,const QStyleOptionGraphicsItem*,QWidget*) {
painter->drawLine(0,0,width(),height());
painter->drawLine(0,height(),width(),0);
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*) {
std::cerr << "[TestItem::mousePressEvent]";
}
void keyPressEvent(QKeyEvent*) {
std::cerr << "[TestItem::keyPressEvent]";
}
};
#endif
and the page.qml file loaded into the QDeclarativeView is simply:
import QtQuick 1.0
import Testing 1.0
Tester {
width: 200
height: 200
}
It's all built (with Qt 4.8 on Debian-Wheezy amd64) with qmake file
CONFIG += debug
QT += core gui declarative
TARGET = events
TEMPLATE = app
SOURCES += events.cpp
HEADERS += TestItem.h
and, once built, when I run ./events I get a window displaying the Tester's painted 'X', as expected:
and a [TestItem created] logged to the console. However, clicking within the window or pressing keys completely fails to invoke either of the mouse or key event handlers.
I'm completely mystified. Is some extra magic (in the C++ or QML domains) needed to get mouse/keyboard events routed to these sort of "plugin" QDeclarativeItem classes? I certainly don't have any problems defining a MouseArea in the QML file and having it do stuff to QML state, and the code this is reduced from has no problems with signals and slots interoperating between the C++ item and QML code... but when it comes to mouse/keyboard events, there's just no sign of them on the C++ side.
To get (left) mouse events, all that is needed is to add
setAcceptedMouseButtons(Qt::LeftButton);
in the TestItem constructor. This is a little surprising as the documentation for the inherited QGraphicsItem::setAcceptedMouseButtons says "By default, all mouse buttons are accepted", but maybe something else in the setup messes with the state.
To get keyboard events, setFocus(true) just needs to be invoked. The documentation seems to imply setFlag(QGraphicsItem::ItemIsFocusable,true) should also be called, but it doesn't actually seem to be necessary in my test.