How to require a password to close a window? - c++

I would like to protect a window from closing, so when the user attempts to do that, a password has to be entered in order to complete the action. There is no need for a username to be entered.
I know how to intercept the QWidget::closeEvent, as in this example:
void MainWindow::closeEvent(QCloseEvent *event) {
QMessageBox msgBox;
msgBox.setText(tr("Do you want to close the window?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
msgBox.setModal(true);
int ret = msgBox.exec();
if (ret == QMessageBox::Yes)
event->accept();
else
event->ignore();
}
The problem is, that the example uses QMessageBox, which does not allow a text to be entered, hence it does not suit my needs.
How to modify the code in order to prompt for a password?

Solution
Instead of QMessageBox use QDialog and shape it to your needs.
Example
Here is an example I have prepared for you in order to demonstrate how the proposed solution could be implemented:
void MainWindow::closeEvent(QCloseEvent *event) {
QDialog dialog(this);
auto *layoutDialog = new QVBoxLayout(&dialog);
auto *lineEdit = new QLineEdit(&dialog);
auto *label = new QLabel(tr("Enter password to close the window:"), &dialog);
lineEdit->setEchoMode(QLineEdit::Password);
layoutDialog->addWidget(label);
layoutDialog->addWidget(lineEdit);
layoutDialog->addStretch();
connect(lineEdit, &QLineEdit::editingFinished, [&]() {
dialog.done(lineEdit->text() == "11223344");
});
dialog.resize(250, 100);
if (dialog.exec())
event->accept();
else
event->ignore();
}
Note: Of course you have to come up with a smarter way of how to keep the password safe.

Related

QMessageBox result as a buttonrole

I wanna get result of QMessagebox as a buttonrole. but result always 16384 or 65536
I dont wantto use standartresults, want to use only buttonrole kind of buttons. what am I doing wrong here ?
(Im very newbie in QT)
void MainWindow::on_pushButton_clicked()
{
QMessageBox msgBox;
QPushButton *a=msgBox.addButton("OK",QMessageBox::ActionRole);
QPushButton *b=msgBox.addButton("CANCEL",QMessageBox::RejectRole);
int result=msgBox.question(this,"Hola","My 1st Msg");
//result always return 16384 or 65536(integer) PROBLEM HERE
if(result==QMessageBox::RejectRole)
this->setWindowTitle("rejected");
else
this->setWindowTitle("accepted");
}
The question method is static. It doesn't use the message box you have above. The first 3 lines of your method essentially don't do anything.
Here's what your method really does:
void MainWindow::on_pushButton_clicked()
{
int result = QMessageBox::question(this,"Hola","My 1st Msg");
[...]
}
Alas, the QMessageBox has a long-standing bug: it ignores (!) the custom button roles when it comes to acceptance or rejection of the dialog. While the role is passed to the underlying QDialogButtonBox, it is not interpreted correctly when the button is clicked.
Although you can get the role back using QMessageBox::buttonRole, the QMessageBoxPrivate::_q_buttonClicked invokes QDialog::done with the index of the button.
Thus, the first button you add will cause the dialog to be rejected, the second one will cause it to be accepted, and further buttons will cause neither. The acceptance/rejection ignores the role completely and is only based on the index of the button, due to the order it was added in.
Thus you should not use the rejected/accepted signals, unless the first two buttons map directly to these roles in this order, and should use the buttonClicked signal and obtain the role of the button directly:
void MainWindow::on_pushButton_clicked()
{
auto box = new QMessageBox{this};
box->setAttribute(Qt::WA_DeleteOnClose);
box->addButton("OK", QMessageBox::ActionRole);
box->addButton("CANCEL", QMessageBox::RejectRole);
box->setIcon(QMessageBox::Question);
box->setWindowTitle("Hola");
box->setText("My 1st message.");
box->show();
connect(box, &QMessageBox::buttonClicked, [=](QAbstractButton *button){
switch (box->buttonRole(button)) {
case QMessageBox::AcceptRole: return setWindowTitle("accept-role");
case QMessageBox::ActionRole: return setWindowTitle("action-role");
case QMessageBox::RejectRole: return setWindowTitle("reject-role");
}
});
}
Alas, there's another problem: the dialog will be also rejected by closing it via the platform's window manager (the close button on the dialog's title bar). So you need to be able to use the rejected signal, but not when it's in error. It's best to factor this functionality out to a MessageBoxAdapter class that will only emit correct accepted and rejected signals:
// https://github.com/KubaO/stackoverflown/tree/master/questions/messagebox-roles-40753898
#include <QtWidgets>
class MessageBoxAdapter : public QObject {
Q_OBJECT
public:
MessageBoxAdapter(QObject *parent = nullptr) : QObject(parent) {
watch(parent);
}
void watch(QObject *obj) {
auto box = qobject_cast<QMessageBox*>(obj);
if (!box) return;
connect(box, &QMessageBox::rejected, [=]{
if (!box->clickedButton()) emit rejected();
});
connect(box, &QMessageBox::buttonClicked, [=](QAbstractButton *button){
auto role = box->buttonRole(button);
if (role == QMessageBox::AcceptRole) emit accepted();
else if (role == QMessageBox::RejectRole) emit rejected();
emit roleClicked(role);
});
}
Q_SIGNAL void accepted();
Q_SIGNAL void rejected();
Q_SIGNAL void roleClicked(QMessageBox::ButtonRole role);
};
And some user interface to try it out:
struct Ui : public QWidget {
QVBoxLayout layout{this};
QTextBrowser browser;
QPushButton button{"Open"};
MessageBoxAdapter adapter{this};
public:
Ui() {
layout.addWidget(&browser);
layout.addWidget(&button);
connect(&button, &QPushButton::clicked, this, &Ui::onClicked);
connect(&adapter, &MessageBoxAdapter::accepted, [=]{ browser.append("accepted"); });
connect(&adapter, &MessageBoxAdapter::rejected, [=]{ browser.append("rejected"); });
connect(&adapter, &MessageBoxAdapter::roleClicked, [=](QMessageBox::ButtonRole role){
browser.append(QStringLiteral("clicked role=%1").arg(role));
});
}
void onClicked() {
auto box = new QMessageBox{this};
adapter.watch(box);
box->setAttribute(Qt::WA_DeleteOnClose);
box->addButton("OK", QMessageBox::AcceptRole);
box->addButton("ACTION", QMessageBox::ActionRole);
box->addButton("CANCEL", QMessageBox::RejectRole);
box->setIcon(QMessageBox::Question);
box->setWindowTitle("Hola");
box->setText("My 1st message.");
box->show();
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"

Why my data can not transfer between forms with SIGNAl/SLOT?

I have main window and 1 dialog in Qt(in Linux OS).I want to send some thing from main window to my dialogs. When the user press menu button, then my button clicked emit a signals. this is my code in main.cpp:
MainWindow w;
MyDialog m;
//------------------------------
//this connection send key button press mood from MainWindow
QObject::connect(&w,SIGNAL(pressMood(QString)),
&m,SLOT(getPressMood(QString)));
w.show();
and this is my mainwindos.h:
signals:
void pressMood(QString mood) ;
mainwindow.cpp:
void MainWindow::on_btnMenu_clicked()
{
if(database->checkEmpty())
{
menu mn;/*=new menu();*/
mn.showFullScreen();
}
else
{
MyDialog *d=new MyDialog(this);
d->show();
d->raise();
d->activateWindow();
emit pressMood("menu");
if(d->Accepted>0)
{
if(loginResult)
{
menu *mn=new menu();
mn->showFullScreen();
}
}
else
QMessageBox::warning(this, tr("Login failed"), "Sorry.Your authenticate is not valid.", QMessageBox::Ok);
}
}
//--------------------------------------------
void MainWindow::on_btnPassword_clicked()
{
//emit sendID2(result);
CardDialog *d=new CardDialog(this);
emit pressMood("pass");
d->show();
d->raise();
if(d->Accepted<=0)
QMessageBox::warning(this, tr("Login failed"), "Sorry.Your authenticate is not valid.", QMessageBox::Ok);
}
I do not use dialog.exec() because I do not need to show modal.
MyDialog.h:
public slots:
void getPressMood(QString mood);
and MyDialog.cpp:
//=================================================
void MyDialog::getPressMood(QString mood)
{
mood=mood;
//ui->lblMood->setText(mood);;
//ui->lblMood->hide();
}
void MyDialog::on_buttonBox_accepted()
{
//QString mood=ui->lblMood->text();
bool st=database->checkPassword(ui->txtID->text(),ui->txtPass->text(),"3");
int id=(ui->txtID->text()).toInt();
//this user is valid to go to menu page
//s/he is admin
if((st)&&
mood=="menu" &&
database->checkAdmin(id))
{
.......
}
when I trace my code line by line. the emit signal is works , it sends the string data to my slot in another form and also the getpressedmood() slot is also worked. But the global var mood become NULL when the dialog show, also I decide to save data in label. In trace mood I see the string is send but when the dialog shows the label becomes to default value.
I can not find the mistake. Could you help me?
It solved.
my mistake is connect the signal and slot in main.cpp. The answer is :
MyDialog *d=new MyDialog(this);
//should connect here not in main.cpp
QObject::connect(this,SIGNAL(pressMood(QString)),
d,SLOT(getPressMood(QString)));
emit pressMood("menu");
d->show();
d->raise();
d->activateWindow();

Qt Tab rename when double clicked

i am using visual Studio with Qt.
i do not have access to Qt designer. its all done through coding (C++);
i have an opensource software called easypaint.
i got stuck at trying to rename tabs. I want to be able to rename tabs when user double clicks on the tab itself.
i created a new function to filter the doubleClick event :
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
`enter code here`{
if (event->type() == QEvent::MouseButtonDblClick) {
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
then i added this line to a function that initializes the TabWidget:
installEventFilter(mTabWidget);
can anyone please guide me through this.
Thank you
Most likely Qt doesn't allow an inline editor to open on the tab's name. So you'd most likely have to create and run a very small QDialog to query for the new name:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == mTabWidget &&
event->type() == QEvent::MouseButtonDblClick) {
// query and set tab(s) names
QTabWidget *tab = qobject_cast<QTabWidget *>(obj);
if(tab)
{
QDialog dlg;
QVBoxLayout la(&dlg);
QLineEdit ed;
la.addWidget(&ed);
QDialogButtonBox bb(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
la.addWidget(&bb);
dlg.setLayout(&la);
if(dlg.exec() == QDialog::Accepted)
{
tab->setTabText(0, ed.text());
return true;
}
}
}
// Standard event processing
return QObject::eventFilter(obj, event);
}
It might be that Qt's dynamic memory management doesn't like the local class instances, so you'd have to convert all those class instances created to pointers created with new, but then please don't forget to tell the QDialog to delete on close or call dlg->deleteLater() after you queried the new name.
Another way to solve this via a fake inline editor would need a bit more work:
create a QLineEdit
move it right above the tab's, bring it up front and set keyboard focus to it
wire signals and slots
pressing enter should use the contents of the QLineEdit
leaving focus from the line edit should be treated as "abort" and delete the line editor
implement the slots to do what's needed.
You can write the event filter in the fallowing way:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == mTabWidget &&
event->type() == QEvent::MouseButtonDblClick) {
QTabWidget *tab = qobject_cast<QTabWidget *>(obj);
// Set tab(s) names
tab->setTabText(0, "New Name");
}
// Standard event processing
return QObject::eventFilter(obj, event);
}

How to merge KeyReleaseEvent with Button

How to merge KeyReleaseEvent and QPushButton using signal.
I mean whenever user will press enter key button should call some function using SLOT. So what i have to use in the signal?
void mywindow::keyReleaseEvent(QKeyEvent *event)
{
switch(event->key())
{
case Qt::Key_Enter:
connect(button1, SIGNAL(clicked()), this, SLOT(fileNew()));
connect(button2, SIGNAL(clicked()), this, SLOT(file()));
break;
}
}
If I understand your question correctly, you want to click some button when pressing the enter key. You can just call the QAbstractButton::click() function to perform a click.
connect(button1,SIGNAL(clicked()),this,SLOT(fileNew()));
connect(button2,SIGNAL(clicked()),this,SLOT(file())); //do this in your constructor, or somewhere else.. just make sure you only do this once
 
void mywindow::keyReleaseEvent(QKeyEvent *event)
{
switch(event->key())
{
case Qt::Key_Enter:
button1->click();
break;
}
}
There is shortcut property to handle such case.
I recommend to use QAction with shortcut value. There is lost of bonus functionality.

Qt Browser stack when dropping link to QMainWindow

I have simple drag and drop functions implemented in QmainWindow the reference taken from here and here
all i want to do is to accept valid url and open QDialog when the url dropped .
but when the url drooped and the QDialog poped up the browser is stocked in the background until i close the QDialog . this is wrong the browser should be free all the time.
here is my code :
void MainWindow::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat("text/html"))
{
event->acceptProposedAction();
}
else
{
event->ignore();
}
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
// accept just text/uri-list mime format
if (event->mimeData()->hasFormat("text/html"))
{
event->acceptProposedAction();
}
else
{
event->ignore();
}
}
void MainWindow::dragLeaveEvent(QDragLeaveEvent *event)
{
event->accept();
}
void MainWindow::dropEvent(QDropEvent *event)
{
QList<QUrl> urlList;
QString fName;
QStringList pathList;
QFileInfo info;
QString suffix;
if (event->mimeData()->hasFormat("text/html"))
{
urlList = event->mimeData()->urls(); // returns list of QUrls
// if just text was dropped, urlList is empty (size == 0)
if ( urlList.size() > 0) // if at least one QUrl is present in list
{
QString url = urlList.at(0).toString();
event->acceptProposedAction();
openDialog(url); // THIS IS THE FUNCTION THAT I OPEN THE QDIALOG window
}
}
//event->acceptProposedAction();
}
void MainWindow::openDialog(QString& slink)
{
QHash<QString,QVariant> DataMap;
QString link = slink;
DataMap.insert("m_webpage",link);
PublishToDialog* pPublishToDialog = new PublishToDialog(this);
pPublishToDialog->Init(DataMap);
if(pPublishToDialog->exec() != QDialog::Accepted)
{
}
}
when i remove the call to the QDialog , so every thing is working fine .
and the browser doesn't stuck. i even tryed as suggested using signal/slot put again
when i start the QDialog when drop invoked the browser stucked!
What exactly is PublishToDialog? I would assume that it is a custom dialog implementation of yours that inherits QDialog. And given this line:
pPublishToDialog->exec() != QDialog::Accepted
This opens the dialog as a modal dialog. A modal dialog is blocking and will block the execution of the current thread until some action is performed on the dialog. Instead of using a modal dialog, you should use a non-modal dialog. Since I am still not sure if PublishToDialog inherits QDialog or what else, I am just going to assume it is. Here is what you could do:
PublishToDialog* pPublishToDialog = new PublishToDialog(this);
// Make it a non-modal dialog
pPublishDialog->setModal(false);
// Connect it to a slot to handle whenever the user performs some action on it
QObject::connect(pPublishDialog, SIGNAL(finished()), this, SLOT(handleDialogAction());
pPublishDialog.show();
You will have to implement handleDialogAction in your code. At the same time, you may want to make pPublishDialog a class-member as you will need it to access QDialog::reuslt in handleDialogAction.