How to hide "Cancel" button in QInputDialog in QT using C++? - 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.

Related

How to get the QLineEdit text offset when have QAction at leading position

I have a QLineEdit with a QAction at leading position. I want to know the start position of the text but I don't find how to do:
QLineEdit *le = new QLineEdit(parent);
le->addAction(QIcon(":/myicon"), QLineEdit::LeadingPosition);
// Now I want to get the text start position
// but both return "QMargins(0, 0, 0, 0) QMargins(0, 0, 0, 0)"
qDebug() << le->textMargins() << le->contentsMargins();
I searched in the qt's github sources to find if the addAction() method does something on the contents or text margins but without success.
I must admit that (before reading OPs question) I was not aware about QLineEdit::addAction(). Thus, I wrote a little sample testQLineEditAction.cc:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
QLineEdit qEdit;
qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
qEdit.show();
// runtime loop
return app.exec();
}
and this is how it looks (compiled in cygwin64):
Afterwards, I digged a bit through woboq.org to find out how it is implemented.
I started in QLineEdit::paintEvent():
void QLineEdit::paintEvent(QPaintEvent *)
{
...
QStyleOptionFrame panel;
initStyleOption(&panel);
...
QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
r.setX(r.x() + d->effectiveLeftTextMargin());
r.setY(r.y() + d->topTextMargin);
r.setRight(r.right() - d->effectiveRightTextMargin());
r.setBottom(r.bottom() - d->bottomTextMargin);
This is interesting: The rectangle for contents is retrieved and then corrected by inner offsets.
QFontMetrics fm = fontMetrics();
...
QRect lineRect(r.x() + d->horizontalMargin, d->vscroll, r.width() - 2*d->horizontalMargin, fm.height());
About the d->horizontalMargin, I'm not quite sure but I ignored it for now and followed instead d->effectiveLeftTextMargin():
int QLineEditPrivate::effectiveLeftTextMargin() const
{
return effectiveTextMargin(leftTextMargin, leftSideWidgetList(), sideWidgetParameters());
}
...
static int effectiveTextMargin(int defaultMargin, const QLineEditPrivate::SideWidgetEntryList &widgets,
const QLineEditPrivate::SideWidgetParameters &parameters)
{
if (widgets.empty())
return defaultMargin;
return defaultMargin + (parameters.margin + parameters.widgetWidth) *
int(std::count_if(widgets.begin(), widgets.end(),
[](const QLineEditPrivate::SideWidgetEntry &e) {
return e.widget->isVisibleTo(e.widget->parentWidget()); }));
}
So, I came to the conclusion that QLineEditPrivate::effectiveLeftTextMargin() considers the space for action icons when effective size of text rectangle is determined.
It's a pity that all these functions are private and thus not accessable from outside. After thinking a while how to get access to these from outside and looking into doc. whether I haven't overseen something, I got the idea to use the QActions directly for this:
#include <QtWidgets>
void inspect(const QString &cmd, QAction &qCmd)
{
qDebug() << (cmd + "->associatedWidgets().size():")
<< qCmd.associatedWidgets().size();
int i = 0;
for (QWidget *const pQWidget : qCmd.associatedWidgets()) {
qDebug() << '[' << i++ << "]:"
<< typeid(*pQWidget).name()
<< "geometry:" << pQWidget->geometry();
}
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
QLineEdit qEdit;
qEdit.setText("012345678901234567890123456789");
QAction *const pQCmd1
= qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
QAction *const pQCmd2
= qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
qEdit.show();
qDebug() << "qEdit.geometry():" << qEdit.geometry();
inspect("pQCmd1", *pQCmd1);
inspect("pQCmd2", *pQCmd2);
// runtime loop
return app.exec();
}
Console output:
Qt Version: 5.9.4
qEdit.geometry(): QRect(0,0 200x23)
"pQCmd1->associatedWidgets().size():" 2
[ 0 ]: 9QLineEdit geometry: QRect(0,0 200x23)
[ 1 ]: 19QLineEditIconButton geometry: QRect(4,2 22x18)
"pQCmd2->associatedWidgets().size():" 2
[ 0 ]: 9QLineEdit geometry: QRect(0,0 200x23)
[ 1 ]: 19QLineEditIconButton geometry: QRect(174,2 22x18)
To compare the values, another snapshot with modified icons (frame drawn in SVGs to show icon size) which has been magnified (factor 5):
Left QLineEditIconButton reported position (4, 2) but the left frame of icon is 8 pixels away from left border of QLineEdit. There is surely a frame around the QLineEditIconButton which has to be considered as well (and I didn't investigate how to retrieve it). The width of frame might be subject of style engine and thus vary between platforms. To make such attempt robust and portable, the respective values should be retrieved from the widgets or from style. This starts to become a tedious fiddling with more or less chance for success.
I ended up once in a similar situation when trying to answer SO: How to automatically increase/decrease text size in label in Qt.
Concerning QLineEdit::cursorRect():
I believe that using QLineEdit::cursorRect() is (as well) at best fragile.
I modified my above example to check this out:
#include <QtWidgets>
class LineEdit: public QLineEdit {
public:
QRect cursorRect() const { return QLineEdit::cursorRect(); }
};
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
LineEdit qEdit;
qEdit.setText("012345678901234567890123456789");
qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
qEdit.show();
qDebug() << "qEdit.cursorRect():" << qEdit.cursorRect();
// runtime loop
return app.exec();
}
Console output:
Qt Version: 5.9.4
qEdit.geometry(): QRect(0,0 200x23)
qEdit.cursorRect(): QRect(253,0 9x16)
Funny, that the cursor x-position is not only quite high – it's even higher than the width of qEdit. How comes? The initial text "012345678901234567890123456789" I put into qEdit causes the cursor to be close to the right whereby horizontal scrolling happens. The cursor position seems to be related to the virtual text width (including the clipped range on the left side).
After having to deal with this problem myself recently, I found a very simple solution:
QLineEdit.setTextMargins(24, 0, 0, 0)
Where the parameters refer to left, top, right, and bottom text margins.
I am using PyQt5, but the idea is the same for Qt as well.

How to get current row of QTableWidget if I clicked on its child?

I have created a QTableWidget in which I've used setCellWidget(QWidget*). I've set QLineEdit in the cell widget. I've also created a delete button and clicking that button sends a signal to the function deleteRow. I've also used a function currentRow() to get the current row, but it returns -1 because of the QLineEdit. The code snippet is below.
void createTable() {
m_table = new QTableWidget(QDialog); //member variable
for (int i = 0; i < 3; i++)
{
QLineEdit *lineEdit = new QLineEdit(m_table);
m_table->setCellWidget(i, 0, lineEdit);
}
QPushButton *deleteBut = new QPushButton(QDiaolg);
connect(deleteBut, SIGNAL(clicked()), QDialog, SLOT(editRow()));
}
editRow() {
int row = m_table->currentRow(); // This gives -1
m_table->remove(row);
}
In above scenario I click in the QLineEdit and then click on the button delete. Please help me out with a solution.
Just tried it here, it seems that currentRow of the table returns -1 when clicking the button right after program start, and when first selecting a cell, then selecting the QLineEdit and then clicking the button, the correct row is returned.
I would do the following as a workaround: Save the row number in the QLineEdit, e.g. by using QObject::setProperty:
QLineEdit *lineEdit = new QLineEdit(m_table);
lineEdit->setProperty("row", i);
m_table->setCellWidget(i, 0, lineEdit);
Then, in the editRow handler, retrieve the property by asking the QTableWidget for its focused child:
int row = m_table->currentRow();
if (row == -1) {
if (QWidget* focused = m_table->focusWidget()) {
row = focused->property("row").toInt();
}
}
The accepted solution, as is, would not work if rows might get deleted while the program runs. Thus the approach would require to update all the properties. Can be done, if this is a rare operation.
I got away with an iteration approach:
for(unsigned int i = 0; i < table->rowCount(); ++i)
{
if(table->cellWidget(i, relevantColumn) == QObject::sender())
{
return i;
}
}
return -1;
Quick, dirty, but worked, and in my case more suitable, as rows got deleted often or changed their positions, only buttons in the widget were connected to the slot and the slot was never called directly. If these conditions are not met, further checks might get necessary (if(QObject::sender()) { /* */ }, ...).
Karsten's answer will work correctly only if QLineEdit's property is recalculated each time a row is deleted, which might be a lot of work. And Aconcagua's answer works only if the method is invoked via signal/slot mechanism. In my solution, I just calculate the position of the QlineEdit which has focus (assuming all table items were set with setCellWidget):
int getCurrentRow() {
for (int i=0; i<myTable->rowCount(); i++)
for (int j=0; j<myTable->columnCount(); j++) {
if (myTable->cellWidget(i,j) == myTable->focusWidget()) {
return i;
}
}
return -1;
}

How do I handle a user pressing the cancel button in a QInputDialog?

This is my first week doing Qt so forgive me if I don't understand the basics that well. In the commented part of the code below I'd like to write code that handles the cancel button on QInputDialog.
#include <QtWidgets>
int main (int argc, char* argv[]) {
QApplication app(argc, argv);
QTextStream cout(stdout);
int answer;
do {
int celciusArg = 0;
int farenheit;
celciusArg = QInputDialog::getInt(0, "Celcius Calculator",
"Convert this number to Farenheit:", 1);
// I'd like to say here:
// if (user clicked cancel)
// then (close the widget)
cout << "User entered: " << celciusArg
<< endl;
farenheit = celciusArg * 1.8 + 32;
QString response = QString("%1 degrees celcius is %2 degrees farenheit .\n%3")
.arg(celciusArg).arg(farenheit) /* Each %n is replaced with an arg() value. */
.arg("Convert another temperature?"); /* Long statements can continue on multiple lines, as long as they are broken on token boundaries. */
answer = QMessageBox::question(0, "Play again?", response,
QMessageBox::Yes| QMessageBox::No); /* Bitwise or of two values. */
} while (answer == QMessageBox::Yes);
return EXIT_SUCCESS;
}
Reading documentation helps a lot:
If ok is nonnull *ok will be set to true if the user pressed OK and to false if the user pressed Cancel. The dialog's parent is parent. The dialog will be modal and uses the widget flags.
Full prototype is:
int QInputDialog::getInt(QWidget * parent, const QString & title, const QString & label, int value = 0, int min = -2147483647, int max = 2147483647, int step = 1, bool * ok = 0, Qt::WindowFlags flags = 0)
So here you need only to use bool * ok:
bool isOkPressed{};
int celciusArg = 0;
int farenheit;
celciusArg = QInputDialog::getInt(0, "Celcius Calculator",
"Convert this number to Farenheit:", 1, -2147483647, 2147483647, 1, &isOkPressed);
if (isOkPressed) {
// here you go
}
QInputDialog::getInt() documentation
Change to
bool ok;
celciusArg = QInputDialog::getInt(0, "Celcius Calculator",
"Convert this number to Farenheit:", 0, 0, 100, 1, &ok);
if (ok)
//pressed ok
else
//pressed cancel
the first zero is the default value, the second is the minimum value, 100 should be the maximum value and 1 is the increment/decrement, if you want to get temperature from -100 C to 200 C starting from 30 C you must use
celciusArg = QInputDialog::getInt(0, "Celcius Calculator",
"Convert this number to Farenheit:", 30, -100, 200, 1, &ok);

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!

Yes/No message box using QMessageBox

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_()