Yes/No message box using QMessageBox - c++

How do I show a message box with Yes/No buttons in Qt, and how do I check which of them was pressed?
I.e. a message box that looks like this:

You would use QMessageBox::question for that.
Example in a hypothetical widget's slot:
#include <QApplication>
#include <QMessageBox>
#include <QDebug>
// ...
void MyWidget::someSlot() {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Test", "Quit?",
QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes) {
qDebug() << "Yes was clicked";
QApplication::quit();
} else {
qDebug() << "Yes was *not* clicked";
}
}
Should work on Qt 4 and 5, requires QT += widgets on Qt 5, and CONFIG += console on Win32 to see qDebug() output.
See the StandardButton enum to get a list of buttons you can use; the function returns the button that was clicked. You can set a default button with an extra argument (Qt "chooses a suitable default automatically" if you don't or specify QMessageBox::NoButton).

You can use the QMessage object to create a Message Box then add buttons :
QMessageBox msgBox;
msgBox.setWindowTitle("title");
msgBox.setText("Question");
msgBox.setStandardButtons(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
if(msgBox.exec() == QMessageBox::Yes){
// do something
}else {
// do something else
}

QT can be as simple as that of Windows. The equivalent code is
if (QMessageBox::Yes == QMessageBox(QMessageBox::Information, "title", "Question", QMessageBox::Yes|QMessageBox::No).exec())
{
}

I'm missing the translation call tr in the answers.
One of the simplest solutions, which allows for later internationalization:
if (QMessageBox::Yes == QMessageBox::question(this,
tr("title"),
tr("Message/Question")))
{
// do stuff
}
It is generally a good Qt habit to put code-level Strings within a tr("Your String") call.
(QMessagebox as above works within any QWidget method)
EDIT:
you can use QMesssageBox outside a QWidget context, see #TobySpeight's answer.
If you're even outside a QObject context, replace tr with qApp->translate("context", "String") - you'll need to #include <QApplication>

QMessageBox includes static methods to quickly ask such questions:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char **argv)
{
QApplication app{argc, argv};
while (QMessageBox::question(nullptr,
qApp->translate("my_app", "Test"),
qApp->translate("my_app", "Are you sure you want to quit?"),
QMessageBox::Yes|QMessageBox::No)
!= QMessageBox::Yes)
// ask again
;
}
If your needs are more complex than provided for by the static methods, you should construct a new QMessageBox object, and call its exec() method to show it in its own event loop and obtain the pressed button identifier. For example, we might want to make "No" be the default answer:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char **argv)
{
QApplication app{argc, argv};
auto question = new QMessageBox(QMessageBox::Question,
qApp->translate("my_app", "Test"),
qApp->translate("my_app", "Are you sure you want to quit?"),
QMessageBox::Yes|QMessageBox::No,
nullptr);
question->setDefaultButton(QMessageBox::No);
while (question->exec() != QMessageBox::Yes)
// ask again
;
}

If you need asynchronous call you should use open and result methods instead of question or exec. Sample code inside a QWidget method:
QMessageBox* const message = new QMessageBox(QMessageBox::Icon::Question, tr("Test"),
tr("Quit?"), QMessageBox::Button::Yes | QMessageBox::Button::No, this);
message->setDefaultButton(QMessageBox::Button::No);
message->open();
connect(message, &QDialog::finished, this, [message] {
message->deleteLater();
if (message->result() == QMessageBox::Button::Yes) {
QApplication::quit();
}
});
It should not be usefull just for a quit dialog but for other confirmation dialogs where parent widget might be destroyed by external events it is the main way to avoid a crash.

Python equivalent code for a QMessageBox which consist of a question in it and Yes and No button. When Yes Button is clicked it will pop up another message box saying yes is clicked and same for No button also. You can push your own code after if block.
button_reply = QMessageBox.question(self,"Test", "Are you sure want to quit??", QMessageBox.Yes,QMessageBox.No,)
if button_reply == QMessageBox.Yes:
QMessageBox.information(self, "Test", "Yes Button Was Clicked")
else :
QMessageBox.information(self, "Test", "No Button Was Clicked")

If you want to make it in python you need check this code in your workbench.
also write like this.
we created a popup box with python.
msgBox = QMessageBox()
msgBox.setText("The document has been modified.")
msgBox.setInformativeText("Do you want to save your changes?")
msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
msgBox.setDefaultButton(QMessageBox.Save)
ret = msgBox.exec_()

Related

Qt state machines: more transition attempts than expected

I have created a simplified example of a state machine transitioning between 2 states on a button click (+ a checkbox showing the active state). I am seeing more calls to the transitions'eventTest method than I expect.
#include <QtStateMachine/QStateMachine>
#include <QtStateMachine/QSignalTransition>
#include <QtWidgets/QApplication>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QPushButton>
class MySignalTransition : public QSignalTransition
{
public:
MySignalTransition(QString name, QPushButton* button, QCheckBox* checkBox) :
QSignalTransition(button, &QAbstractButton::clicked),
cb(checkBox)
{
setObjectName(name);
}
protected:
bool eventTest(QEvent* e) override {
qDebug() << objectName() << "tested on" << e->type() << '|' << cb->isChecked();
return QSignalTransition::eventTest(e);
}
private:
QCheckBox* cb;
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
app.setQuitOnLastWindowClosed(true);
QWidget w;
w.setFixedSize(200, 200);
QPushButton button(QStringLiteral("Click me"), &w);
QCheckBox checkbox(&w);
button.move(10, 80);
checkbox.move(10, 20);
QStateMachine m;
QState s1, s2;
m.addState(&s1);
m.addState(&s2);
m.setInitialState(&s1);
s1.assignProperty(&checkbox, "checked", false);
s2.assignProperty(&checkbox, "checked", true);
//button will be used to make the transition back and forth between states s1 and s2
MySignalTransition t1("Transition 1", &button, &checkbox), t2("Transition 2", &button, &checkbox);
t1.setTargetState(&s2);
t2.setTargetState(&s1);
s1.addTransition(&t1);
s2.addTransition(&t2);
w.show();
m.start();
return app.exec();
}
On top of the checkbox, I placed a qDebug() << ... to watch when Qt attempts to perform transitions.
The first attempt is right when the state machine starts, before I do any click on the button. I guess (but I am not sure) that this is expected.
The real problem happens when I actually do click on the button. I see:
eventTest is called from the starting state (event->type() == QEvent::None)
eventTest is called from the starting state (event->type() == QEvent::StateMachineSignal)
eventTest is called from the target state (event->type() == QEvent::None)
The one call I am most surprised about is point 1.
What causes the state machine to try the transition again when I click on the button with QEvent::None? This is especially surprising to me because it is always a transition the state machine has already tried (either on start or on the previous click).
If I were to create another transition out of s1 and/or s2, should I be worried that transition could have a higher priority than my signal transitions without any way for me to control it?
Edit
To address #Scheff's cat comment below, I changed the transition class to be:
class MySignalTransition : public QSignalTransition
{
public:
MySignalTransition(QString name, QPushButton* button, QCheckBox* checkBox) :
QSignalTransition(button, &QAbstractButton::clicked),
cb(checkBox)
{
setObjectName(name);
}
protected:
bool event(QEvent* e) override {
return false;
}
bool eventTest(QEvent* e) override {
qDebug() << objectName() << "tested on" << e->type() << '|' << cb->isChecked();
return transition;
}
private:
QCheckBox* cb;
public:
inline static bool transition = false;
};
and right after creating the push button, added:
QObject::connect(&button, &QPushButton::clicked, []() { MySignalTransition::transition = true; });
By doing that, the state machine lets me do 1 click before it starts an infinite loop.
When clicking, MySignalTransition::transition is set to true, then eventTest is called.
With a breakpoint, it is easy to see eventTest is never called with event->type() == QEvent::StateMachineSignal.
To me, this is a clear indication there is more to it than just probing.
First, it would need to do the probing with the actual event, not with a dummy event.
Second, it would not do the transition if the result of the probing was correct, I still should see the 2nd call to eventTest.
Last, I thought maybe the state machine tries to execute as many transitions as it can and will then pass the click signal to whatever other state it lands on.However, I am not very satisfied by that explanation because the attempt to execute an automatic transition is already done when entering the state (it is the third call to eventTest) and because I think my click should be applied on the state that is active at the very moment I do it, not on just "whatever other state it lands on".

Is there a method or a way to check whether a user has visited a certain tab page on a QTabWidget?

I have a QTabWidget on my application, so user can navigate through the tab pages by clicking on the title, I want to know when user open a tab page, whether he/she visited this page previously. In QWizard class there is a method hasVisitedPage() which does the exact same thing on a wizard, but I couldn't find a similar method in QTabWidget class. What I want to know is, whether there is a method to do this like in QWizard?
this is the similar method in QWizard class http://doc.qt.io/archives/qt-4.8/qwizard.html#hasVisitedPage
Currently I am using a QList to store the visited page indexes and each time when a user open a tabpage check whether QList contains the index of the opened page, I think it would be more easy if I had a method to check
What I want to know is, whether there is a method to do this like in QWizard?
Unfortunatelly, there is not.
Currently I am using a QList to store the visited page indexes and each time when a user open a tabpage check whether QList contains the index of the opened page
QWizard does the same, i.e. has a QList<int> history; attribute. So, in my opinion you are doing it the right way.
Take a look at the source code for more details. In particular, QWizardPrivate::switchToPage might be interesting to you, in order to give you an idea how it is done in QWizard, so you can check your own implementation against that and adapt it if necessary.
The history property is easy enough to add:
// https://github.com/KubaO/stackoverflown/tree/master/questions/tabwidget-history-52033092
#include <QtWidgets>
#include <array>
static const char kHistory[] = "history";
auto getHistory(const QTabWidget *w) {
return w->property(kHistory).value<QList<int>>();
}
void addHistory(QTabWidget *tabWidget) {
QObject::connect(tabWidget, &QTabWidget::currentChanged, [tabWidget](int index) {
if (index < 0) return;
auto history = getHistory(tabWidget);
history.removeAll(index);
history.append(index);
tabWidget->setProperty(kHistory, QVariant::fromValue(history));
});
if (tabWidget->currentIndex() >= 0)
tabWidget->setProperty(
kHistory, QVariant::fromValue(QList<int>() << tabWidget->currentIndex()));
}
bool hasVisitedPage(const QTabWidget *w, int index) {
return getHistory(w).contains(index);
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget ui;
QVBoxLayout layout{&ui};
QTabWidget tabWidget;
QLabel history;
layout.addWidget(&tabWidget);
layout.addWidget(&history);
std::array<QLabel, 5> tabs;
for (auto &l : tabs) {
auto const n = &l - &tabs[0] + 1;
l.setText(QStringLiteral("Label on Page #%1").arg(n));
tabWidget.addTab(&l, QStringLiteral("Page #%1").arg(n));
}
addHistory(&tabWidget);
auto showHistory = [&] {
auto text = QStringLiteral("History: ");
for (auto i : tabWidget.property("history").value<QList<int>>())
text.append(QString::number(i + 1));
history.setText(text);
};
showHistory();
QObject::connect(&tabWidget, &QTabWidget::currentChanged, showHistory);
tabWidget.currentChanged(tabWidget.currentIndex());
ui.show();
return app.exec();
}

How to set focus to a QWidget, on a different window

I have created two QPushButton on two different QMainWindow. I am assigning focus to them randomly at a specific interval.Here is the code.
int main(int argc, char **argv){
QApplication a(argc, argv);
QMainWindow *win1= new QMainWindow();
win1->resize(567,578);
win1->move(67,30);
win1->show();
QMainWindow *win2= new QMainWindow();
win2->resize(567,578);
win2->move(97,580);
win2->show();
win1->show();
//win2->setModal(true);
QPushButton *but1 = new QPushButton(win1);
but1->resize(80,20);
but1->move(100,100);
but1->setText("1");
but1->show();
QPushButton *but2 = new QPushButton(win2);
but2->resize(80,20);
but2->move(100,300);
but2->setText("2");
but2->show();
while(1){
if((rand()%2) == 1){
//win2->lower();
win1->raise();
win1->activateWindow();
win1->setWindowState(Qt::WindowActive);
win1->setFocus(Qt::ActiveWindowFocusReason);
but1->setFocus(Qt::ActiveWindowFocusReason);
}
else{
//win1->lower();
win2->raise();
win2->activateWindow();
win2->setFocus(Qt::ActiveWindowFocusReason);
but2->setFocus(Qt::ActiveWindowFocusReason);
}
qApp->processEvents(0x00);
sleep(2);
}
But the problem is the title bar of the first window is not changing color(usually putting a window back-n-forth through the visual stack changes the color of the title-bar), even when it has become the top window visually
You will obtain the desired behavious if you change your last loop to something similar:
while (1) {
// Exits if both windows are closed
if (!win1->isVisible() && (!win2->isVisible())) {
return 0;
}
// Eventually changes the focus, if the desired window is still visible
if((rand() % 2) == 1) {
if (win1->isVisible()) {
QApplication::setActiveWindow(win1);
}
}
else {
if (win2->isVisible()) {
QApplication::setActiveWindow(win2);
}
}
QTime now;
now.start();
do {
qApp->processEvents(0x00);
} while (now.elapsed() < 2000);
}
Anyway, if you put your program to sleep, it will not respond to user input during that interval, so be careful.
The implementation is quite ugly, but it checks if the windows to be focused is still visible (i.e. the user has not closed it) and eventually exits if both have been closed.
Of course I suppose that you were only interested in the setActiveWindow() thing, so I've not spent much time in writing something beautiful!

How to change a QMenu text from english to russian by clicking a button

Please consider we have a menu which has text set "MyMenu" and I want to change the menu text by clicking a button in the same widget from "MyMenu" to "МойМеню". Could you bring a code snippet please for that operation?
Have a look at "Dynamic Translation", http://doc.qt.io/qt-5/internationalization.html
void MyWidget::changeEvent(QEvent *event)
{
if (e->type() == QEvent::LanguageChange)
{
titleLabel->setText(tr("Document Title"));
...
okPushButton->setText(tr("&OK"));
// You could also use : retranslateUi(QWidget*);
}
else
{
QWidget::changeEvent(event);
}
}
This will be helpful to you as well : http://doc.qt.io/qt-5/qcoreapplication.html#installTranslator
Basically, when you call : qApp->installTranslator(MyAppTranslator) it will create a QEvent::LanguageChange.
So, provide a simple QComboBox with English/Russian, and when the selected language changes, call qApp->installTranslator(MyAppTranslator);. Then make sure your buttons are properly set up in changeEvent, and that's it !
Hope it helps a bit !
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));//this is the solution
.............
}
And in the code you can dynamically change the strings if you set them from the begining by using tr() function [tr("your text")].

How to hide "Cancel" button in QInputDialog in QT using C++?

#include <QtGui>
int main (int argc, char* argv[])
{
QApplication app(argc, argv);
QTextStream cout(stdout, QIODevice::WriteOnly);
// Declarations of variables
int answer = 0;
do {
// local variables to the loop:
int factArg = 0;
int fact(1);
factArg = QInputDialog::getInteger(0, "Factorial Calculator",
"Factorial of:");
cout << "User entered: " << factArg << endl;
int i=2;
while (i <= factArg) {
fact = fact * i;
++i;
}
QString response = QString("The factorial of %1 is %2.\n%3")
.arg(factArg).arg(fact)
.arg("Do you want to compute another factorial?");
answer = QMessageBox::question(0, "Play again?", response,
QMessageBox::Yes | QMessageBox::No ,QMessageBox::Yes);
} while (answer == QMessageBox::Yes);
return EXIT_SUCCESS;
}
In this program, i dont to have Input window(4th line of do-while loop) to have cancel button. How do i do that?
I have just started learning QT. So, sorry if its a very basic question.
And also how do i make use of cancel button to stop the application.. Bcos, right now CANCEL button does nothing.
QInputDialog is given as a convenience class that provides a quick and easy way to ask for an input and as such, does not allow for much customization. I don't see anything in the documentation to indicate that you can change the layout of the window. I would suggest just designing your own dialog by extending QDialog. This will take more time, but will allow you to customize the form.
If you do want to determine if the cancel button was pressed in the QInputDialog, you must pass a pointer to a bool into the getInteger() function as the 8th argument.
do{
bool ok;
factArg = QInputDialog::getInteger(0, "Factorial Calculator", "Factorial of:",
value, minValue, maxValue, step, &ok);
if(!ok)
{
//cancel was pressed, break out of the loop
break;
}
//
// Do your processing based on input
//
} while (some_condition);
If ok returns as false, the user clicked cancel and you can break out of your loop. You can see what value, minValue, maxValue, and step mean in the documentation:
QInputDialog documentation
Hiding the Help Button in a QInputDialog works by passing the correct windowFlags:
QInputDialog inputDialog;
bool ok;
inputDialog.setWindowFlags(inputDialog.windowFlags() & (~Qt::WindowContextHelpButtonHint));
QString text = inputDialog.getText(this, tr("Factorial Calculator"),
tr("Enter some text:"), QLineEdit::Normal, QString(), &ok,
inputDialog.windowFlags());
// Or for integers
int number = inputDialog.getInt(this, tr("Fractorial Calculator"),
tr("Enter a number:"), 0, -10, 10, 1, &ok,
inputDialog.windowFlags());
In Qt Designer's Property Editor, you can customize the standardButtons property -
This should allow you to control which dialog buttons are being presented.