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.
Related
i'm trying to take screenshot with Paint event (WM_Paint) event in QT but i dont know how,
i used this code to take screenshot but it is not really good for do this
it must do screenshot when some changes in desktop not take screenshot every 1000 ms with timer
void MainWindow::shootScreen()
{
originalPixmap = QPixmap(); // clear image for low memory situations
// on embedded devices.
originalPixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
//emit getScreen(originalPixmap);
updateScreenshotLabel();
}
void MainWindow::updateScreenshotLabel()
{
this->ui->label_2->setPixmap(originalPixmap.scaled(this->ui->label_2- >size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
}
Use QObject::installEventFilter on the widgets you are interested in, then check for the proper events. For example, in your MainWindow ui initialization:
void MainWindow::yourUiInitFunc()
{
exampleWidget = new QWidget;
...
exampleWidget->installEventFilter(this);
...
}
Then re-implement eventFilter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == exampleWidget && event->type() == QEvent::KeyPress)
shootScreen();
return QMainWindow::eventFilter(obj, event);
}
tank you Jon Harper your codes was useful but i changed somethings there
your code just work when some key press event on my project form but i added paint method to your if and now it work on windows events too
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && QEvent::Paint == event->type()) {
shootScreen();
}
return false;
}
I am able to get key value's from HAL through a call back function in Qt. Created event for that key by
QKeyEvent *event = new QKeyEvent (QEvent::KeyPress,
inputKey.keyValue,
Qt::NoModifier);
Note: inputKey.keyValue Key value received from HAL Layer.
Now I need to Register This key event in Qt, So that if any key press happened in IR Remote then in respective form, keyPressEvent(e) or event(e) will get invoke. and based on the key press, specific action will get execute.
Note: More than one form is there, where key press event will trigger And more than one keys are there "Page_Up, Page_Down, Ok Key etc....."
tried to invoke Postevent() and connect(.......) but nothing helped me. KeyPressEvent() is not getting executed.
E.g. like this:
// receiver is a pointer to QObject
QCoreApplication::postEvent (receiver, event);
You can find more info here.
You can reimplement QObject::event() or QWidget::keyPressEvent in your widget to receive key events.
Visit this link or link for more information. See the example code below which consists of two buttons and a label. Clicking pushButton sends 'enter pressed' and pushButton_2 sends 'letter A pressed'. Key events are received in the event() function and label is updated accordingly.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(sendKeyEvent()));
connect(ui->pushButton_2, SIGNAL(clicked()), this, SLOT(sendKeyEvent()));
}
void MainWindow::sendKeyEvent()
{
QObject* button = QObject::sender();
if (button == ui->pushButton)
{
QKeyEvent *event = new QKeyEvent (QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
QCoreApplication::postEvent (this, event);
}
else if (button == ui->pushButton_2)
{
QKeyEvent *event = new QKeyEvent (QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
QCoreApplication::postEvent (this, event);
}
}
bool MainWindow::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Enter) {
ui->label->setText("Enter received");
return true;
}
else if (keyEvent->key() == Qt::Key_A)
{
ui->label->setText("A received");
return true;
}
}
return QWidget::event(event);
}
You can create the event on the stack. Then use QCoreApplication::sendEvent to have the event immediately delivered:
QWidget w1, w2;
QKeyEvent event(QEvent::KeyPress, inputKey.keyValue, Qt::NoModifier);
QApplication::sendEvent(&w1, &ev);
QApplication::sendEvent(&w2, &ev);
Each sendEvent will call the widget's event method, that will then invoke the xxxxEvent protected methods as applicable. But don't do it yourself, as you then bypass the application-global event filters and depend on implementation details.
// WRONG!
w1.event(&ev);
// WRONG and won't compile since keyPressEvent is protected
w2.keyPressEvent(&ev);
Or, you can create it on the heap. Qt is managing the event's lifetime as soon as you post it. After you post, the event is not yours anymore and you can't reuse it. You must create multiple events for each post. The simplest way to avoid repetition is to create a local function that produces the events on demand.
QWidget w1, w2;
auto ev = [=]{ return new QKeyEvent(QEvent::KeyPress, inputKey.keyValue, Qt::NoModifier); };
QApplication::postEvent(&w1, ev());
QApplication::postEvent(&w2, ev());
The events will be added to the main thread's event queue. Once the control returns to QApplication::exec, they will be delivered to the widgets one-by-one. The widgets' event method will be called from QApplication::exec, not from postEvent.
The QWidget::event implementation decodes the event types and invokes the protected convenience handlers, such as keyPressEvent, enterEvent, etc. Its implementation follows the following pattern:
bool QWidget::event(QEvent * ev) {
switch (ev->type()) {
case QEvent::KeyPress:
keyPressEvent(static_cast<QKeyEvent*>(ev));
return true;
case QEvent::Enter:
enterEvent(static_cast<QEnterEvent*>(ev));
return true;
...
}
return QObject::event(ev);
}
When implementing handlers for event types that have such xxxxEvent convenience virtual methods, you should reimplement the virtual method, and not the event() itself. Thus your MainWindow should reimplement keyPressEvent:
void MainWindow::keyPressEvent(QKeyEvent * event) {
if (event->key() == Qt::Key_Enter)
ui->label->setText("Enter received");
else if (event->key() == Qt::Key_A)
ui->label->setText("A received");
QMainWindow::keyPressEvent(event);
}
If you wish your key events to be delivered to your window immediately, and that seems to be a reasonable approach, your sendKeyEvent method becomes much simpler:
void MainWindow::sendKeyEvent()
{
if (sender() == ui->pushButton) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
QApplication::sendEvent(this, &event);
}
else if (sender() == ui->pushButton_2) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
QApplication::sendEvent(this, &event);
}
}
There is a way to simplify things further, though. Recall that the QObject supports dynamic properties. You can thus easily assign the keys as a property of the buttons, and automatically send the event whenever a button with a key property has been pressed:
static const char keyPropKey[] = "key";
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::sendKeyEvent);
connect(ui->pushButton_2, &QPushButton::clicked, this, &MainWindow::sendKeyEvent);
ui->pushButton.setProperty(keyPropKey, (int)Qt::Key_Enter);
ui->pushButton.setProperty(keyPropKey, (int)Qt::Key_A);
}
void MainWindow::sendKeyEvent()
{
auto key = sender().property(KeyPropKey);
if (key.isValid()) {
QKeyEvent event(QEvent::KeyPress, key.toInt(), Qt::NoModifier);
QApplication::sendEvent(this, &event);
}
}
Finally, as a stylistic nitpick: there's no reason to have the ui member be a pointer. You'll avoid one heap allocation, and plenty of indirections, by using it as a direct member.
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();
}
}
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()));
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.