QT QcoreApplication postEvent() behaviour - c++

I have written this simple QT mainwindow, only if I pass QString argument to the QKeyEvent, it prints the key, I expect the key to be printed even without QString argument?
section 1 in below code does not seem to work (I don't get the key printed in the QLineEdit field; while section 2 works and "1" is printed! is this normal behavior? what happens to the event when its posted in first section of the code?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->ui->lineEdit->setFocus();
Qt::Key key = Qt::Key_1;
// 1
QKeyEvent *event = new QKeyEvent (QEvent::KeyPress, key ,Qt::NoModifier);
QCoreApplication::postEvent(QWidget::focusWidget(), event); // Does not work! No key is set in the widget
//
//2
QKeyEvent *event2 = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier, QKeySequence(key).toString());
QCoreApplication::postEvent(QWidget::focusWidget(), event2); // this one works!
}

Not all key-events have a text representation (delete, curser movement, shortcuts, ...). For those who have one, the QKeyEvent class stores it in its text. You have to provide that text, otherwise its a "textless" event.
QLineEdit will just add the text, and not deduce it from the event type (as can be seen here)

Related

How to send key to parent mainWindow from dialog in QT C++

I need a virtual keyboard for an embedded linux application (not QML). I couldn't find a better way, so now I am trying to create one. I want a dialog full of buttons that sends keys to parent mainWindow. It runs without errors, but happens nothing in lineEdit.
keyboard.cpp
Keyboard::Keyboard(QWidget *parent) :
QDialog(parent),
ui(new Ui::Keyboard)
{
ui->setupUi(this);
mainWindow = this->parent();
}
void Keyboard::on_btnA_clicked()
{
qDebug() << "Test1";
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
qDebug() << "Test2";
QApplication::sendEvent(mainWindow, &event);
qDebug() << "Test3";
}
And in mainWindow.cpp to open keyboard dialog:
keyboard->show();
ui->lineEdit->setFocus();
What is the problem? Thanks in advance.
Several things:
Sending the event to mainWindow requires mainWindow to handle passing the event to the QLineEdit object, without seeing the rest of the code I can't say if this is being done or not; the alternative is sending directly to the QLineEdit like this:
QApplication::sendEvent(lineEdit, &event);
The QKeyEvent constructor also requires a fourth parameter - the string to send, in the example case an "a".
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
should be
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "a");
to send an "a".
Depending on the exact implementation you may also need to send a QEvent::KeyRelease after the QEvent::KeyPress, i.e.
QKeyEvent event1(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "b");
QKeyEvent event2(QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier);
QApplication::sendEvent(edit, &event1);
QApplication::sendEvent(edit, &event2);
As (2) indicates, the key enumeration (i.e. Qt::Key_A) does not send an "a" as you would expect, the string that is sent is instead determined by the fourth parameter in the QKeyEvent constructor, i.e.
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "a");
QApplication::sendEvent(lineEdit, &event);
Is equivalent to
QKeyEvent event(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier, "a");
QApplication::sendEvent(lineEdit, &event);
Using QKeyEvent in this manner will probably lead so some unpleasantness in handling backspaces and deletes. It is probably more elegant to simply append the desired character to the QLineEdit text,
lineEdit->setText(lineEdit->text().append("a"));
and use QLineEdit::backspace() and QLineEdit::delete() to handle backspace and delete keys.
Example
#include <QtWidgets/QApplication>
#include <qwidget.h>
#include <qmainwindow.h>
#include <qlineedit.h>
#include <qboxlayout.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow* main = new QMainWindow;
QWidget* central = new QWidget();
QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight);
central->setLayout(layout);
QLineEdit* edit = new QLineEdit(central);
edit->setAlignment(Qt::AlignCenter);
layout->addWidget(edit);
edit->setText("sometext");
edit->backspace();
edit->setText(edit->text().append("a"));
main->setCentralWidget(central);
main->resize(600, 400);
main->show();
return a.exec();
}

Qt - Why can't I trigger mousePressEvent for my custom button in MainWindow

This is my first time to ask on Stack Overflow. If any suggestion about asking question, please let me know.
I am very new to Qt, and have some problems while using event. Hope someone can provide any thoughts.
Background:
I have my custom pushButton, which is rxPushButton in rx.h and rx.cpp. I use on_rxPushButton_clicked to change the image and it works pretty well.
In MainWindow, I need to use some rx so I include the class rx and I want to detect if I press left button of the mouse, I need to know which rx has been pressed and record its id in int rxId in MainWindow.
Problem:
I tried two ways to achieve my goal, including mousePressEvent and eventFilter. I found that I can't detect the mouse pressed signal on any rx, but I can detect it outside rx in other places in Mainwindow. I wonder if the events will conflict, but when I comment on_rxPushButton_clicked in rx.cpp, MainWindow still doesn't work for the problem. So I presume that maybe the space in the screen occupied by rx will not be in control of MainWindow (I can get Debug message "test1" but not "test2" in my code, check below).
How should I do to solve this problem if I need both things (change image in rx and modify one variable in MainWindow)? Or maybe it's just something wrong with my code and how to modify?
I hope to separate them if possible because I still need to include many objects in MainWindow in the future.
Here are some of my related codes:
rx.cpp
void rx::on_rxPushButton_clicked(void)
{
startLoading();
}
void rx::startLoading(void)
{
state = 1;
connect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
timer->start(LOADING_INTERVAL);
}
void rx::loading1(void)
{
if(state == 1)
{
state = 2;
ui->rxPushButton->setStyleSheet("border-image: url(:/images/Rx/Rxloading1.png);");
disconnect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
}
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
}
//rxList[0]->installEventFilter(this);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "test1";
if(rxList[0]->rect().contains(event->x(), event->y()))
qDebug() << "test2";
}
}
Change image in rx
Use QSignalMapper class that bundles signals from QWidgets (class rx) and re-emits them with widget (class rx) parameters corresponding to the object that sent the signal.
We connect each button's clicked() signal to the signal mapper's map() slot, and create a mapping in the signal mapper from each button to the button itself.
Finally we connect the signal mapper's mapped() signal to the custom widget's(rx object) clicked() signal. When the user clicks a button, the custom widget(rx object) will emit a single clicked() signal whose argument is the button (rx object) the user clicked.
Handle the button clicked event in MainWindow::slotButtonsClicked(QWidget *p) function.
Modify n variable in MainWindow:
You can update variable of MainWindow in mousePressEvent function
class MainWindow : public QWidget
{
public :
MainWindow (QWidget *parent);
QSignalMapper * mapper;
slots:
void slotButtonsClicked(QWidget *);
// ...
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mapper = new QSignalMapper(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
QObject::connect(rxList[i], SIGNAL(clicked()),mapper,SLOT(map()));
//Adds a mapping so that when map() is signalled from the sender, the signal mapped(widget ) is emitted.
mapper->setMapping(rxList[i], rxList[i]);
}
QObject::connect(mapper,SIGNAL(mapped(QWidget *)),this,SLOT(slotButtonsClicked(QWidget *)));
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "Clicked outside the button";
//Now you can modify n variable in MainWindow
}
MainWindow::mousePressEvent(event);
}
void MainWindow::slotButtonsClicked(QWidget *p)
{
rx *button = dynamic_cast<rx *>(p);
button->on_rxPushButton_clicked();
qDebug() << button <<" clicked on button";
//Now you can change image in rx
}
Hope this works :)
Event propagating prevent you to do it. When mouse is over Button, event sends to button, not to the MainWindow, thats why you never see test2 in debug output.
Since I don't know what do you need it's hard to say what you should do. You can process event in Button and send some signals or whatever, or set event filter and check if target object is your button, and so on.
Any way, read Qt event system docs for better understanding.

A lot of widgets seems to have default behavior for the space bar key press event. How can I override this without subclassing every widget?

I have a very specific piece of code that needs to be executed instantly, no matter what else is going on in the gui, when the space bar is pressed. I have the following snippet of code in each of my keypress event filters:
else if(keyPressed == Qt::Key_Space){
emit sigHalted();
}
This works fine, unless certain widgets have focus. The ones causing me issues are:
QTableWidget
QPushButton
If I'm editing an in a QTableWidget, and I hit space bar, then it adds a space bar to the item in the QTableWidget instead of executing the code above. If I click a button with the mouse, and then hit space bar, it acts as if I clicked that same button again instead of executing the code above.
I know I can fix this behavior by subclassing the widgets and overriding their event filters, but I would prefer to avoid this, because I have a lot of buttons and tables and I would have to go through and replace all of them with the new, subclassed versions. Is there a way I can catch the space bar keypress event before it goes to the widget's default behavior?
You must use eventFilter.
Enable the event:
code:
#include <QApplication>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
qApp->installEventFilter(this);
}
Declare the function:
code:
bool eventFilter(QObject *watched, QEvent *event);
Override the function:
code:
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::KeyPress){
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Escape){
//your code here
emit sigHalted();
return true;
}
else{
return false;
}
}
else
return QMainWindow::eventFilter(watched,event);
}

Have a combobox inside a message box

I want to create a combobox inside a message box and return the selected value to be used later.
I can do the same on the window itself but not sure how to do that inside a combobox.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->comboBox->addItem("Red");
ui->comboBox->addItem("Blue");
ui->comboBox->addItem("Green");
ui->comboBox->addItem("Yellow");
ui->comboBox->addItem("Pink");
ui->comboBox->addItem("Purple");
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QMessageBox::about(this,"Choose color of rectangle", ui->comboBox->currentText() );
}
If I understand you correct you would like to show a combobox in a separate dialog window for the user to select some option.
One of the ways to do that, would be to subclass QDialog. If a combo field and a button to accept is sufficient the class could look as below:
class CustomDialog : public QDialog
{
public:
CustomDialog(const QStringList& items)
{
setLayout(new QHBoxLayout());
box = new QComboBox;
box->addItems(items);
layout()->addWidget(box);
QPushButton* ok = new QPushButton("ok");
layout()->addWidget(ok);
connect(ok, &QPushButton::clicked, this, [this]()
{
accept();
});
}
QComboBox* comboBox() { return box; }
private:
QComboBox* box;
};
To use the class object you can call exec to display it modally. Then you can verify whether the user accepted the choice by pressing the ok button and take proper action.
QStringList itemList({"item1", "item2", "item3"});
CustomDialog dialog(itemList);
if (dialog.exec() == QDialog::Accepted)
{
// take proper action here
qDebug() << dialog.comboBox()->currentText();
}
Similar approach is implemented in the QMessageBox class where a number of options can be specified to alter the displayed contents (for example button configuration or check box existance).
EDIT:
To use the sample code in your own project you should put the latter section I posted into your on_pushButton_clicked() slot. Substitute the itemList with your color names list. Then put the CustomDialog class to a separate file which you include in main and you should be good to go.

Issue with QLineEdit clear signal

I'm working with the Qt KDE Necessitas project. I have a project built in Qt Creator and I am installing the apk on an emulator API-15 (also tested on API-10).
The following code is setup to clear the text of two different QLineEdit objects when a button is clicked, but this isn't the case. Randomly, only one of the two QLineEdit objects are cleared.
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
public slots:
void slotClear();
private:
QLineEdit* line1;
QLineEdit* line2;
//...
};
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVBoxLayout* mainLayout = new QVBoxLayout;
QFormLayout* form = new QFormLayout;
line1 = new QLineEdit;
form->addRow(tr("Line 1: "), line1);
line2 = new QLineEdit;
form->addRow(tr("Line 2:"), line2);
QPushButton* button = new QPushButton;
mainLayout->addLayout(form);
mainLayout->addWidget(button);
QWidget* centralWid = new QWidget(this);
centralWid->setLayout(mainLayout);
this->setCentralWidget(centralWid);
connect(button, SIGNAL(clicked()), this, SLOT(slotClear()));
}
void MainWindow::slotClear()
{
line1->clear();
line2->clear();
}
//...
Calling the function QLineEdit::setText("") produces the same results. Additionally, connecting the clicked() signal from the button directly to the clear() slot of the QLineEdit has no effect.
I haven't been programming in Qt for very long, so I am unsure if there is something I am doing wrong. Is anybody seeing something needs to be corrected in order to have the text cleared from BOTH QLineEdits? I am not sure if this is unique to Qt itself or Qt Necessitas. Any input would be greatly appreciated.
EDIT
I have also just noticed that entering text in one line, switching to another line and entering text there, and then switching back to the original line results in the original text being erased once the field is clicked (note, the button was never clicked). I think this is a pretty clear indication that something funky is going on.
EDIT 2
Registered as a bug with KDE