Qt sending keyPressEvent - c++

I want to append chars to QLineEdit by sending KeyEvent.
I'm using code like this:
ui.myEdit->setFocus();
for(size_t i = 0; i < 10; ++i) {
QKeyEvent keyPressed(QKeyEvent::KeyPress, 'a', Qt::NoModifier);
QWidget::keyPressEvent(&keyPressed); // or
//QApplication::sendEvent(QApplication::focusWidget(), &keyPressed);
}
Why there is no change in myEdit?

You can change the change the text of QLineEdit simply by :
ui->myEdit->setText(ui->myEdit->text().append("a"));
But if you really want to change it by sending QKeyEvent you can try this :
QKeyEvent * eve1 = new QKeyEvent (QEvent::KeyPress,Qt::Key_A,Qt::NoModifier,"a");
QKeyEvent * eve2 = new QKeyEvent (QEvent::KeyRelease,Qt::Key_A,Qt::NoModifier,"a");
qApp->postEvent((QObject*)ui->myEdit,(QEvent *)eve1);
qApp->postEvent((QObject*)ui->myEdit,(QEvent *)eve2);

Your approach is not wise.
Setting the focus yourself may annoy more than one user which loose focus from one UI element for the other.
By calling keyPressEvent directly you are skipping many layers of processing from the framework. Only misbehavior await down this path.
To reply to
I want to append chars to QLineEdit
You can obtain the line edit text, modify at your will and set it back.
QString currentText = ui.myEdit->text();
QString toappend = "aaaaaaaaaa";
QString nextText = currentText + toappend;
ui.myEdit->setText(nextText);
or one line
ui.myEdit->setText(ui.myEdit->text()+mystring);

Synthesizing a key press event to append characters to a line edit is asking for endless trouble. You'd need to retain the state of the control to ensure that you are in fact appending characters. If the cursor is not at the end, you'll be inserting or prepending characters. If any modifiers are active, you may cause the widget to act as if, say, a clipboard shortcut was activated. Say if you "append" an X while Ctrl/⌘ is held down, you'll cause any selected text to disappear from the line edit.
In other words: if you want to append something to a textedit, simply append it, don't synthesize keystrokes.
lineEdit->setText(lineEdit->text() + "appended");
That's it. To do it properly via appending keystrokes requires about a page of code, and even then it can't but rely on Qt's implementation details.

Related

Performantly appending (rich) text into QTextEdit or QTextBrowser in Qt

QTextEdit can be appended text to simply using append(). However, if the document is rich text, every time you append to the document, it is apparently reparsed. This seems like a bit of a trap in Qt.
If you're using the edit box as a log window and appending text in fast successions as a result of external signals, the appending can easily hang your app with no intermediate appends shown until each of the appends have completed.
How do I append rich text to a QTextEdit without it slowing down the entire UI?
If you want each append to actually show quickly & separately (instead of waiting until they've all been appended before they are shown), you need to access the internal QTextDocument:
void fastAppend(QString message,QTextEdit *editWidget)
{
const bool atBottom = editWidget->verticalScrollBar()->value() == editWidget->verticalScrollBar()->maximum();
QTextDocument* doc = editWidget->document();
QTextCursor cursor(doc);
cursor.movePosition(QTextCursor::End);
cursor.beginEditBlock();
cursor.insertBlock();
cursor.insertHtml(message);
cursor.endEditBlock();
//scroll scrollarea to bottom if it was at bottom when we started
//(we don't want to force scrolling to bottom if user is looking at a
//higher position)
if (atBottom) {
scrollLogToBottom(editWidget);
}
}
void scrollLogToBottom(QTextEdit *editWidget)
{
QScrollBar* bar = editWidget->verticalScrollBar();
bar->setValue(bar->maximum());
}
The scrolling to bottom is optional, but in logging use it's a reasonable default for UI behaviour.
Also, if your app is doing lots of other processing at the same time, appending this at the end of fastAppend, will prioritize actually getting the message displayed asap:
//show the message in output right away by triggering event loop
QCoreApplication::processEvents();
This actually seems a kind of trap in Qt. I would know why there isn't a fastAppend method directly in QTextEdit? Or are there caveats to this solution?
(My company actually paid KDAB for this advice, but this seems so silly that I thought this should be more common knowledge.)

QLineEdit editingFinished signal twice when changing focus?

I've found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler. In my case I am a bit stuck as I am getting the editFinished signal twice even when my slot handler is doing nothing.
For a test, I have an array of QLineEdit which use a signalMapper to connect the editingFinished() signals to a single slot. The signalMapper passes the array index so I can see where the signal came from.
eg:
testenter::testenter(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testenter)
{
// setup the UI according to the .h file
ui->setupUi(this);
signalMapper = new QSignalMapper(this);
// init the labels and edit boxes
for (int i = 0; i < 10; i++)
{
pm_label[i] = new QLabel(ui->scrollArea);
QString text = QString("Number %1").arg(i);
pm_label[i]->setText(text);
pm_label[i]->setGeometry(10,20+i*30, 50, 20);
pm_label[i]->show();
pm_editBox[i] = new QLineEdit(ui->scrollArea);
pm_editBox[i]->setGeometry(80,20+i*30, 50, 20);
pm_editBox[i]->show();
signalMapper->setMapping(pm_editBox[i], int(i));
connect(pm_editBox[i], SIGNAL(editingFinished()), signalMapper, SLOT(map()));
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(data_entry(int)));
}
void testenter::data_entry(int entry)
{
//dummy
}
When run in the debugger, if I enter data into one box then either hit return or select another box with the mouse (ie change focus) , then it calls data_entry twice, the first time with index of the box that is losing focus and the 2nd time with the box which gets the focus.
So my question: Am I missing something? Is this expected behaviour or a bug?
If a bug, anyone know a way round it as I wanted to use this signal to do custom validation on data when it is entered (by either return, tab or mouse click to change focus).
First off, no this isn't expected behavior, i.e. selecting a QLineEdit should not cause it's editingFinished signal to be emitted.
There are a couple of possible things that may cause this problem:
You've accidentally connected a signal twice to a slot
The slot map() is causing the newly selected box to lose focus
In the same vain, if you're debugging and using a break point to detect when the slots are getting called you may be causing the QLineEdit to lose focus when the active application changes from your QWidget to your debugger, again causing the signal to be sent again.
If you're having problems because of a doubly connected slot, which doesn't seem to be the case because you're specifically getting a signal from two different QLineEdits, you can make sure that this isn't happening by specifying the connection type, the connect method actually has an additional optional argument at the end which allows you to change the type from a DefaultConnection to a UniqueConnection.
That being said, data validation is something that Qt has an established mechanism for, and I suggest that you use it if possible, look into extending the QValidator abstract base class Ref Doc. You then tell each of your QLineEdit's to use the same validator.
I have run into the same issue. It really does emit the signal twice, which is a known bug: https://bugreports.qt.io/browse/QTBUG-40 which however has not been addressed for a very long time.
Finally I found that the best solution in my case is to change the signal from editingFinished to returnPressed. As a side effect this behaves much more predictably from the user perspective. See also here: http://www.qtforum.org/article/33631/qlineedit-the-signal-editingfinished-is-emitted-twice.html?s=35f85b5f8ea45c828c73b2619f5750ba9c686190#post109943
The OP "found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler." Well, that is my situation also, and here is where I ended up. So, at the risk of going off topic...
In my situation, when my slot receives the editingFinished signal sent from the QLineEdit, I launch a modal QMessageBox to ask the user something. The appearance of that message box is what triggers the QLineEdit to send the second, undesirable editingFinished signal.
A post in the bug report (https://bugreports.qt.io/browse/QTBUG-40) mentioned by #V.K. offers a workaround which helped me. The following is my implementation of the workaround. I let Qt magic mojo automatically connect the QLineEdit signal to my MainWindow slot.
void MainWindow::on_textbox_editingFinished( void )
{
QLineEdit * pTextbox = qobject_cast<QLineEdit *>( QObject::sender() );
if ( !pTextbox->isModified() )
{
// Ignore undesirable signals.
return;
}
pTextbox->setModified( false );
// Do something with the text.
doSomething( pTextbox->text() );
}
void MainWindow::doSomething( QString const & text )
{
QMessageBox box( this );
box.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
box.setText( "Are you sure you want to change that text value?" );
if ( box.exec() == QMessageBox::Yes )
{
// Store the text.
m_text = text;
}
}

Setting text input focus within a QWebView

I'm trying to display a Google login dialog in a QWebView, and as I recall, Google likes to set your keyboard focus to the first input field on the page (in this case, the e-mail field).
Unfortunately, the QWebView widget doesn't actually respect this behaviour, and therefore loads the page with keyboard focus on nothing at all:
So I decided dig about a little, and inserted this code snippet into my class logic:
void GoogleAuthDialog::pageLoaded(bool ok) {
if (ok) {
ui->webView->setFocus();
ui->webView->page()->mainFrame()->setFocus();
QWebElement el = ui->webView->page()->mainFrame()->findFirstElement("input:not([type=hidden])");
if (!el.isNull()) {
el.setFocus();
el.evaluateJavaScript("this.focus()");
el.evaluateJavaScript("this.click()");
}
}
}
And the following declaration in my header file:
...
private slots:
void pageLoaded(bool);
Back in the class code, I connected the appropriate signal from the QWebView to my slot:
connect(ui->webView, SIGNAL(loadFinished(bool)), this, SLOT(pageLoaded(bool)));
Yes, I am throwing every possible thing I can think of at it to redirect keyboard focus to the first input box.
Unfortunately, the code did not seem to work, as while it did focus the right input box, I could not type anything inside of it until I clicked it myself, or pressed Tab:
Next I bound the function to my Control key, and proceeded to produce strange results.
If I placed focus into the password field manually, and pressed the Control key, I noticed that I would continue to have keyboard focus in the password field, but have 'visual' focus in the e-mail field:
Also, when I typed something in this 'state', occasionally a letter might 'leak' into the e-mail field before the visual and keyboard focus would 'reset' to the password field:
Is there a proper way of redirecting keyboard focus to an input field of my choosing?
I managed to redirect the keyboard input focus by simulating tab focusing via QKeyEvent:
void GoogleAuthDialog::pageLoaded(bool ok) {
if (ok) {
//Gets the first input element
QWebElement el = ui->webView->page()->currentFrame()->findFirstElement("input:not([type=hidden])");
if (!el.isNull()) {
el.setFocus();
}
// Simulate a forward tab, then back.
QKeyEvent *forwardTab = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QKeyEvent *backwardTab = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier);
QCoreApplication::postEvent(ui->webView, forwardTab);
QCoreApplication::postEvent(ui->webView, backwardTab);
}
}
In my opinion, this does seem like a 'hack-ish' solution, so if there is a 'right' way to do this, I'm all ears.
pgh is right. Just set focus on the QWebView object during your app's initialization.
m_webView->setFocus();

QLineEdit not able to Undo after calling SetText() in Qt

In my application I am having many QLineEdit widgets. The user can edit them.
I have observed that if I add text to a QLineEdit through GUI & then if that QLineEdit has the cursor then if I press ctrl+z then it undoes the text so that there is no text in it.
Now if I set the text by code by SetText("some text"), then undo does not work by ctrl+z method. Even if I undo by code still it does not clear the text. What is the problem? Am I missing something?
Thank You.
undo probably thinks that the value you set by SetText should be treated as default start value, that doesn't need undoing. Normal behavior as it seems for me.
Here is SetText() definition:
This property holds the line edit's text.
Setting this property clears the selection, clears the undo/redo history, moves the cursor to the end of the line and resets the modified property to false. The text is not validated when inserted with setText().
The text is truncated to maxLength() length.
By default, this property contains an empty string.
Calling setText() resets the modified flag to false.
Perhaps if you set setModified ( bool ) to true, will fix this for you
Edit:
Found workaround : insert(QString text) works with undo() just fine

How to issue signal each time a row is edited in QListWidget?

class genericTaskList : public QListWidget
{
Q_OBJECT
public:
QListWidgetItem *defaultText;
genericTaskList (QWidget *parentWidget)
{
setParent (parentWidget);
setFixedSize (445, 445);
defaultText = new QListWidgetItem ("Double click here to compose the task");
defaultText->setFlags (defaultText->flags () | Qt :: ItemIsEditable);
insertItem (0, defaultText);
QObject :: connect (this, SIGNAL (currentRowChanged (int)), this, SLOT (addDefaultText (int)));
}
public slots:
void addDefaultText (int rr)
{
std::cout << "\ndsklfjsdklfhsdklhfkjsdf\n";
insertItem (++rr, defaultText);
}
};
This code is supposed to issue a signal each time the row gets edited.
After I call "insertItem" in the constructor, the signal is issued.
But, that's it. It never gets issued after that - no matter how many times I edit the row.
What am I missing?
At first it seems like QListWidget::itemChanged is the way to go, but soon you run into a problem: the signal is sent for everything - inserts, removes, changing colors, checking boxes, etc! So then you end up trying to put in flags and filter everywhere by intercepting various signals to find out if editing was the actual event. It gets very messy.
There is also QAbstractItemModel::dataChanged , which would seem like a good solution. It even has a parameter "const QVector& lstRoles" so you could scan for Qt::EditRole and see if it was really edited. Alas, there's a catch - it gets called for everything just like QListWidget::itemChanged and unfortunately, for QListWidget anyway, the roles parameter is always empty when it's called (I tried it). So much for that idea...
Fortunately, there's still hope... This solution does the trick! :
http://falsinsoft.blogspot.com/2013/11/qlistwidget-and-item-edit-event.html
He uses QAbstractItemDelegate::closeEditor, but I prefer using QAbstractItemDelegate::commitData.
So make a connect like so...
connect(ui.pLstItems->itemDelegate(), &QAbstractItemDelegate::commitData, this, &MyWidget::OnLstItemsCommitData);
Then implement the slot like this...
void MyWidget::OnLstItemsCommitData(QWidget* pLineEdit)
{
QString strNewText = reinterpret_cast<QLineEdit*>(pLineEdit)->text();
int nRow = ui.pLstItems->currentRow();
// do whatever you need here....
}
Now you have a slot that gets called only when the list item's text has been edited!
currentRowChanged indicates the row selection has changed, not the content of the row. Perhaps you want to use currentTextChanged or itemChanged instead.
The reuse of the word current and changed in the QT docs is quite confusing.
Warning: A QListWidgetItem can only be added to a QListWidget once. Adding the same QListWidgetItem multiple times to a QListWidget will result in undefined behavior.
So even if it will emit the signal I think you should better to add newly created Item.
And when do you want the new row to be inserted ? -
as soon as item is double clicked or finishing edit - they differ.