Child widget gets event for parent widget ?...
-
Hello,
I am a newbie about Qt, and I am working on a code I did not make.
I have a Widget asking for L/P.
So I have 3 widgets in this window (let's call it ParentBox):- an editable combo box for the user name
- a line edit box for the password
- and a button to validate
The validation shoud be done with the validation button, or by pressing "Enter" when the focus is on the password field.
To do that , two autoconnected slots are coded, the second one (the one matching to the edit field) calling the first one (matching to the validation button).
The second slot (called directly by the a click signal, or through the enter keypress in the edit field) builds an other dialog box (inherited from QDialog), opens its, and call processEvents(). Let's call this new dialog box ChildBox1. It is a waiting dialog box.
Later, in the same slot reacting to the L/P confirmation (with a click or an enter), when the password is wrong, is created an other dialog box (let's call it ChildBox2), whose type is same as the ChildBox1's one (let's call it ChildWidget). But it is displayed with the display() function.
ChildWidget overrides the eventFilter() function, and emits a signal when the key Enter is pressed. In the constructor, this signal is connected to a slot, which closes the window.
OK. Here is now the problem.
When I want to validate the password by pressing enter in the password field, the ChildBox2 window (an error message) is displayed so fast that the user cannot see it. The slot in ChildBox2 is called, as if the Enter used for the parent box had been used for both ParentBox and ChildBox2.I do not have this problem when I click the button to validate the L/P, because in this case, no signal is emitted, and I do not enter the slot closing the window.
I have the feeling that when I create ChildBox2 while the Enter event is being processed in ParentBox, the Enter event is still in the events queue, and is catched by ChildBox2.
Thank to helping me to understand what is happening.
-
@Oodini said in Child widget gets event for parent widget ?...:
and call processEvents().
I have not read all your post, but this is quite wrong/unnecessary, and possibly the cause of whatever issue. Why would you want to do this? It is "rare" to need to call
processEvents()
--- don't. -
@Oodini
To continue the same way as @JonBI dont think an evenfilter is needed here as well.
@Oodini said in Child widget gets event for parent widget ?...:
an error message is displayed so fast that the user cannot see it.
I suspect your childbox2 is a local variable .
-
@mpergand said in Child widget gets event for parent widget ?...:
@Oodini
To continue the same way as @JonB
I dont think an evenfilter is needed here as well.OK.
@Oodini said in Child widget gets event for parent widget ?...:
an error message is displayed so fast that the user cannot see it.
I suspect your childbox2 is a local variable .
Yes it is.
But why does it stay when I click on the validation button in ParentBox, and disappear when I press Enter ? -
@Oodini
You procedure seems overcomplicated.
You can check the text entered for each key stroke and enable the button when password is correct.
Here a quick example:class LoggingDialog: public QDialog { public: LoggingDialog() : QDialog(nullptr) { setWindowTitle("Logging"); auto vLayout= new QVBoxLayout(this); vLayout->setSpacing(14); combo=new QComboBox; vLayout->addWidget(combo); auto label=new QLabel("Enter your password"); vLayout->addWidget(label,0,Qt::AlignCenter); edit=new QLineEdit; edit->setTextMargins(2,0,2,0); edit->setEchoMode(QLineEdit::Password); vLayout->addWidget(edit); auto button=new QPushButton("OK"); button->setEnabled(false); vLayout->addWidget(button,0,Qt::AlignCenter); connect(button, &QPushButton::clicked, this, &QDialog::accept); connect(edit, &QLineEdit::textChanged,this, [button, this](const QString & text) { // enable the OK button only if password is correct button->setEnabled(checkPassword(text)); }); connect(combo, &QComboBox::currentTextChanged,this, [button, this](const QString & text) { button->setEnabled(checkPassword(edit->text())); }); } void addPerson(const QString& name, const QString& password) { combo->addItem(name,password); } QString currentName() { return combo->currentText(); } private: bool checkPassword(const QString& pass) { if(combo->currentData()==pass) return true; return false; } QComboBox* combo; QLineEdit* edit; }; ... LoggingDialog dialog; dialog.addPerson("John","jjjj"); dialog.addPerson("Marc","mmmm"); if(dialog.exec()==QDialog::Accepted) { qDebug()<<"Password correct for"<<dialog.currentName(); }
No extra child window/dialog needed.
-
@mpergand said in Child widget gets event for parent widget ?...:
You procedure seems overcomplicated.
Probably, but my role is to code the wished procedure, not to define it...
@mpergand said in Child widget gets event for parent widget ?...:
You can check the text entered for each key stroke and enable the button when password is correct.
I have to check the password by getting the correct L/P in a database.
I can't do this for each keystroke. -
@Oodini
We can try to make checkPassword() a callback function.
We can use an interface for that
or use a signal like this:signals: void checkPassword(const QString& name, const QString &pass,bool& ok);
then connect to it:
QObject::connect(&dialog,&LoggingDialog::checkPassword,[](const QString& name, const QString &pass,bool& ok) { qDebug()<<name<<pass; // check with the DB // and set ok accordingly }); if(dialog.exec()==QDialog::Accepted) { qDebug()<<"Password correct for"<<dialog.currentName(); }
The connection must be of type DirectConnection, but it's usely the default.
-
@mpergand I'd like nevertheless understanding why Child2Box catches the OK event targetting the ParentBox in the first place. There must be a fondamental concept in Qt I didn't get.
While I was waiting for a response on this thread, I tried an other implementation, simpler, so taht I can submit it to you. There is no more call to processEvents().
ParentBox is here called SessionManager, and the slot on_MB_Login_MyButton_clicked() is associated to both Enter key and button click.
The type of Childbox1 and Childbox2 is CInfo, and this one inherit of MessageDlg.
SessionManager and MessageDlg inherit of QDialog.Here is how are instancied the dialog boxes :
SessionManager --[Enter pressed]--> CInfo1 --[wrong password]--> CInfo2
Note that when Enter is pressed in SessionManager, CInfo2 doesn't exist yet.
The mentionned buttons are not QtPushButtons. They are customized classes inherting of QWidget.
In SessionManager :
void SessionManager::on_MB_Login_MyButton_clicked() { m_waitingDialog = new CInfo(this, g_ExoticLang, "", "toto", TYPE_NULL, false); connect(m_waitingDialog, &MessageDlg::showed, this, [&]() mutable { checkCryptedLP(isLPValid); }, Qt::QueuedConnection); m_waitingDialog->show(); return; } void SessionManager::checkCryptedLP(bool& result) { Sleep(2000); // Simulates long treatment result = false; if (result == false) { CInfo info(this, g_ExoticLang , tr("The password is not correct. Please enter the right password.") , tr("") , TYPE_OK, false); info.Display(); } m_waitingDialog->Close(); }
And for MessageDlg, very dirty :
MessageDlg::MessageDlg( QWidget* parent, QString policeType,QString string1,QString string2,unsigned int nType, bool bLowSizePolicy ) : QDialog( parent,Qt::FramelessWindowHint|Qt::MSWindowsFixedSizeDialogHint ) { ui.setupUi( this ); switch( nType ) { case TYPE_OK: case TYPE_YESNO: case TYPE_OKCANCEL: case TYPE_IGNORE: case TYPE_RETRYCANCEL: case TYPE_NULL: connect( this, SIGNAL(enterKeyPressed()), this, SLOT(on_MB_Ignore_MyButton_clicked())); break; default: break; } m_nType = nType; installEventFilter(this); // ... InitButtonsPosition(); } void MessageDlg::on_MB_Ignore_MyButton_clicked() { m_State = RET_OK; QDialog::done( RET_OK ); } void MessageDlg::InitButtonsPosition() { if( m_nType == ... ) else if( m_nType == TYPE_OK ) { ui.MB_Ignore->setEnabled(true); ui.MB_Ignore->setText(tr("OK")); ui.MB_Cancel->setEnabled(false); ui.MB_Cancel->hide(); ui.MB_Retry->setEnabled(false); ui.MB_Retry->hide(); ui.MB_Ignore->setFocus(); ui.MB_Ignore->move( 209, ui.MB_Ignore->y()); } else if( m_nType == ... ) else if( m_nType == ... ) else if( m_nType == ... ) else if( m_nType == ... ) if( m_nType == ... ) }
The problem is that the dialog box about the wrong password ( a CInfo, so a MessageDlg) catches the Enter key pressed to validate the password field in SessionManager, whereas this MessageDlg doesn't exist yet when this key is pressed.
-
@Oodini
Seems you want to reinvent the wheel :)class LoggingDialog: public QDialog { Q_OBJECT public: LoggingDialog() : QDialog(nullptr) { setWindowTitle("Logging"); auto vLayout= new QVBoxLayout(this); vLayout->setSpacing(14); combo=new QComboBox; vLayout->addWidget(combo); auto label=new QLabel("Enter your password"); vLayout->addWidget(label,0,Qt::AlignCenter); edit=new QLineEdit; edit->setTextMargins(2,0,2,0); edit->setEchoMode(QLineEdit::Password); vLayout->addWidget(edit); auto button=new QPushButton("OK"); //button->setEnabled(false); vLayout->addWidget(button,0,Qt::AlignCenter); connect(button, &QPushButton::clicked, this,[this]() { bool ok=false; emit checkPassword(currentName(),edit->text(),ok); if(ok) { accept(); } else { // wrong password QMessageBox msgBox(this); msgBox.setWindowModality(Qt::WindowModal); msgBox.setText("Invalid password !"); msgBox.setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel); if(msgBox.exec()==QMessageBox::Cancel) reject(); } }); } QString currentName() { return combo->currentText(); } signals: void checkPassword(const QString& name, const QString &pass,bool& ok); private: QComboBox* combo; QLineEdit* edit; }; ... LoggingDialog dialog; QObject::connect(&dialog,&LoggingDialog::checkPassword,[](const QString& name, const QString &pass,bool& ok) { ok=false; // msgbox will show up }); if(dialog.exec()==QDialog::Accepted) { qDebug()<<"Password correct for"<<dialog.currentName(); } else { qDebug()<<"operation cancelled"; }