QLineEdit editingFinished signal twice when changing focus? - c++

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;
}
}

Related

How to have QLabel update as various numbered pushbuttons are clicked

I have a dialpad with numbers 1-9 and 0, and a QLabel above it to show the numbers when clicked(same as a keypad on any phone). All are push buttons. What is the easiest way to get the QLabel to show the numbers as the push buttons are clicked?
For example, if 2 then 0 then 7 is clicked, the label would update in real time with 207. The format of the Qlabel should follow standard phone numbers, 000-000-0000. I understand how to setText for one number at a time, but they keep overriding each other.
Any help is appreciated.
Thank you in advance
What you are looking for is a QSignalMapper. It maps multiple inputs through a single interface and does the sender dispatching for you.
QSignalMapper *mapper(new QSignalMapper(parent));
for (int i=0; i<10; ++i){
QPushButton *button = some_new_button_function();
connect(button, &QPushButton::clicked, mapper, &QSignalMapper::map);
mapper->setMapping(button, i);
}
connect(mapper, QOverload<int>::of(&QSignalMapper::mapped),
[this](int i){/*here your append code*/});
The easiest is to connect the clicked signal of the buttons to a slot (possibly a lambda) that changes the text of the QLabel (using setText()). If you want to append to the current text, then just do setText(label.text() + "new text");.
You have to connect the signals clicked() emitted by each QPushButton to a slot that update the QLabel text.
A brief example
In the parent constructor:
connect(qpb1, &QPushButton::clicked, this, &MyClass::handleQLabel);
And the possible slot implementation:
void MyClass::handleQLabel()
{
QPushButton * qpb = qobject_cast<QPushButton*>(sender()); // Find the sender of the signal
if(qpb != nullptr)
this->myLabel->setText(qpb->text()); // Write anything you want in the QLabel
else
{
// Do what you want.
}
}
This will do the job.
Of course if you don't want use sender() (for multi-threading concerns for example) you can either create one slot by QPushButton and do the same number of connect (heavy and quite a dirty workaround), or create a subclass of QPushButton to add a custom signal to emit with an identifier of the QPushButton and get it with a slot for example.
I hope it can help :)
QLineEdit might better suit your needs in this case if you also want your data representation to follow phone number standard such as "000-000-0000". You can make it read-only, disable interaction flags if you like (but from UI/UX perspective it is better not to, since mostly there is no reason to disallow copying), and also you can set input mask you like. Given your current situation, you can base your needs on the following example:
// Set your format.
ui->lineEdit->setInputMask("000-000-0000");
// Make sure that your text would be in the format you like initially.
ui->lineEdit->setText("999-999-9999");
// Text will be not editable.
ui->lineEdit->setReadOnly(true);
// And here, you can use QSignalMapper as other members have suggested. Or you can just connect multiple buttons somehow. The choice is yours to make.
connect(ui->pushButton, &QPushButton::clicked, ui->lineEdit, [this]
{
// Just keep in mind taht when returning text, some of the mask elements might be here, too.
ui->lineEdit->setText(ui->lineEdit->text().replace("-", "") + "1");
});

Connecting two signals with different arguments

I want to get a QTreeView widget to emit a clicked(const QModelIndex&) signal whenever a pushbutton is clicked. This is so that I can get a list of all the items that are selected within the QTreeView at the time of clicking the pushbutton. Now I thought I could connect two signals with distinct arguments (Qt Connect signals with different arguments), however when I try to call
connect(ui.pbAddVideo, SIGNAL(clicked()), ui.treeView_video, SIGNAL(clicked(const QModelIndex&)));
I get the error message:
QObject::connect: Incompatible sender/receiver arguments QPushButton::clicked() --> QTreeView::clicked(QModelIndex)
Have I misunderstood the whole signal forwarding concept?
As always, many thanks.
Firtsly, what index you must send by clicking a button from your tree?
Secondly, since c++11 standart you can do something like that:
connect(ui.pbAddVideo, &QPushButton::clicked, [=] { emit ui.treeView_video->clicked(ui.treeView_video->currentIndex()); });
I would solve your problem with the following approach:
First you have to handle the button click:
connect(ui.pbAddVideo, SIGNAL(clicked()), this, SLOT(onLoadVideo()));
In the slot you need to get the list of selected items from the tree view and do something with them:
void MyClass::onLoadVideo()
{
QItemSelectionModel *selectionModel = ui.treeView_video->selectionModel();
QModelIndexList selectedVideos = selectionModel->selectedIndexes();
foreach (QModelIndex *video, selectedVideos) {
// Play the video.
}
}
You are connecting one SIGNAL() to another SIGNAL() which is perfectly ok but their parameters should match. In your case the second signal has a parameter(i.e QModelIndex) that the first signal does not have.
Have I misunderstood the whole signal forwarding concept?
Yes.
When a signal is emitted, Qt takes the signal's arguments and passes them to the connected slot/signal.
Valid signal-slot connection
For example, suppose you connect the QSlider::valueChanged(int) signal to the QSpinBox::setValue(int) slot. When the valueChanged() signal is emitted, this is effectively how the slot gets called:
// "Emitting a signal" == "Calling the signal function"
QSlider::valueChanged(int sliderSignalValue)
{
QSpinBox *receiver = findReceiver();
receiver->setValue(sliderSignalValue);
}
Valid signal-signal connections
Similarly, if you connect the QSlider::valueChanged(int) signal to the QSpinBox::valueChanged(int) signal, the code behaves like this:
QSlider::valueChanged(int sliderSignalValue)
{
QSpinBox *receiver = findReceiver();
emit receiver->valueChanged(sliderSignalValue);
}
Now, if you want to connect in the opposite direction (connect(ui.treeView_video, SIGNAL(clicked(const QModelIndex&)), ui.pbAddVideo, SIGNAL(clicked()));, it's perfectly fine:
QTreeView::clicked(const QModelIndex& viewSignalValue)
{
QPushButton *receiver = findReceiver();
emit receiver->clicked(); // No problem. The viewSignalValue is simply ignored.
}
Invalid signal-slot connection
However, for the connection that you wanted to make, the code will need to behave like this:
QPushButton::clicked()
{
QTreeView *receiver = findReceiver();
emit receiver->clicked(/*???*/); // ERROR: You need to pass a QModelIndex value!
}
You've got a parameter mismatch. QTreeView::clicked() requires a QModelIndex value, but QPushButton::clicked() cannot provide this. Therefore, you cannot connect these two together.
Does this make sense?
Many thanks to #vahancho whose answer I have adopted. There is no point in using the "clicked()" signal from the qTreeView since I do not need to wait for this to access the data inside. Hence:
connect(ui.pbAddVideo, SIGNAL(clicked()), this, SLOT(addVideo()));
void VigilWidget::addVideo() {
QItemSelectionModel *selectionModel = ui.treeView_video->selectionModel();
QModelIndexList selectedVideos = selectionModel->selectedIndexes();
foreach (QModelIndex video, selectedVideos) {
qDebug().nospace() << video.data(0);
}
}
As to my question about how signal to signal connections work, thanks to everyone for taking the time to explain this :)

How to catch information for Qt designer

I have created a Qdialog box using the Qt creator designer as shown below:
When I need to display it, I'm instantiate the class dialogoverwrite (.cpp, .h and .ui)
DialogOverwrite *OverwriteDialog = new DialogOverwrite;
OverwriteDialog->exec();
OverwriteOption = OverwriteDialog->result()
My issue is that I want to get the QDialogButtonBox result but I do not know how. the current code, returning the result of the OverwriteDialog but it's not returning any QDialogButtonBox::Yes, QDialogButtonBox::YesToAll ...
How to catch the QButtonGroup result and not the QDialog result.
In the same way, If I want to change the label value from "File(s) and/or Folder(s)" to another label, how to access to this QLabel ?
Thanks for your help
When you pressed QDialogButton it was emit signal clicked(QAbstractButton*) by catching this signal you can identify which action button pressed.
Please go through following link it would be help you.
Qt: How to implement QDialogButtonBox with QSignalMapper for non-standard button ??
Well the standard way to do this is to handle the result by connecting it. So you could do:
connect(this, SIGNAL(clickedDialogButton(QAbstractButton*)),
SLOT(dialogButton(QAbstractButton* aButton)));
Next you would create a function in your class called dialogButton (for example) and have that handle the result:
void MyUI::dialogButton(QAbstractButton* aButton) {
// Obtain the standard button
StandardButton button = buttonBox−>standardButton(button);
// Switch on the type of button
switch (button) {
case QDialogButtonBox::YesToAll:
// Do the thing you would like to do here
break;
// add some more cases?
}
}
You could also check for the signal given by the QButtonGroup. Something like: void QGroupButton::buttonClicked(QAbstractButton* button) would work in the same way.

QT Signals & slots in diffrent class

My program has 2 classes. One of them is MainWindow and another is Calc.
In main window I use automatic generated function on_PushButton_clicked. This function should send two values: double & char to function in Calc.
first:
void MainWindow::on_OneButton_clicked(){
QObject::connect(ui->ZeroButton , SIGNAL(clicked()), this, SLOT(...)) );
ui->TextEdit->insertPlainText("1");
}
second :
void Calc::Add(double val, char oper){
//compute something
}
It's my first app with Qt and I do not know how can I connect them. I've searched similar question on this forum, but can't found.
Sorry if i'm wrong.
First of all, you have to well understand what signal/slot mecanism is, and what you are doing.
Signal/slot mecanism is a Qt concept to link a function (signal) to another function (slot). To "make a link" between a signal and a slot, you have to connect them using QObject::connect(...).
When you use automatic generated function on_PushButton_Clicked() with Qt designer, you, in fact, "make a link" between the signal clicked() emitted when the pushButton is clicked, with a slot on_PushButton_Clicked(). However, the connection between this signal and this slot doesn't appear in your code so it may be confusing and that's why I'm pointing it out.
When you write this:
void MainWindow::on_OneButton_clicked(){
QObject::connect(ui->ZeroButton , SIGNAL(clicked()), this, SLOT(...)) );
ui->TextEdit->insertPlainText("1");
}
You create a connection with zeroButton when clicked and a slot, each time you clic on your button. As a connection is valid till an object is destructed, if you clic again on your pushButton, you'll have a second connection between zeroButton when clicked and your slot.
A better way to create connection is to use connect(...) function when you create your object (mainWindow in your case).
To make it simple for your calculator, you can create 9 buttons for digits, 4 buttons for operators, and 1 button to compute everything.
In your mainwindow constructor, you could have something like:
connect(ui->pushButton1, SIGNAL(clicked()), this, SLOT(onPushButton1Clicked()));
.... // Every other signal for each button
connect(ui->pushButtonEqual, SIGNAL(clicked(), this, SLOT(onPushButtonEqualClicked());
And in your body
void MainWindow::onPushButton1Clicked()
{
// concatenate current value + 1
ui->textEdit->insertPlainText(ui->textEdit->toPlainText() + "1");
}
void MainWindow::onPushButtonEqualClicked()
{
// check textedit content (need a digit + operator + digit)
...
// compute result
...
// write result in TextEdit
...
}
I hope it will help a little bit ;)

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.