Qt dialog accept and reject - qt

I'm trying to implement the following thing: when a specific button is clicked, a new dialog appears where the user has to enter some information and to check/uncheck some checkboxes. Then the user can either click "OK" or "Cancel".
If he clicks "OK" the information he entered is checked for correctness. If something is not correct the dialog should appear again with a warning message and the user should correct/re-enter the information. I would like the information the user entered to be stored. So, if the information is not correct, the dialog should not "reset" to the initial state but keep the information the user entered.
If the user clicks "Cancel" some standard values are used further.
I almost got a solution, but it is not working properly. When using my solution: when I enter a wrong information and click "OK" a warning appears and the information is stored and I can edit it. But if I enter wrong information again and click "OK" again, then the wrong information is accepted. Please see my code below.
QDialog dialog(this);
QFormLayout form(&dialog);
form.addRow((new QLabel("Please enter the three questions for the P835 test. \n"
"Questions one and two will be permuted, \n "
"question three will not. Below each question enter \n"
"the rating scale starting with the best rating and \n"
"separate the ratings with a comma.")));
QList<QLineEdit *> fields;
QLineEdit *lineEdit_Q1 = new QLineEdit(&dialog);
lineEdit_Q1->setText("Bitte bewerten Sie die Signalqualität!");
QString label_Q1 = QString("First question:");
form.addRow(label_Q1, lineEdit_Q1);
fields << lineEdit_Q1;
QLineEdit *lineEdit_Q1_answer = new QLineEdit(&dialog);
lineEdit_Q1_answer->setText("nicht verzerrt, leicht verzerrt, etwas verzerrt, ziemlich verzerrt, sehr verzerrt");
QString label_Q1_answer = QString("Rating first question:");
form.addRow(label_Q1_answer, lineEdit_Q1_answer);
fields << lineEdit_Q1_answer;
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
form.addRow(&buttonBox);
QObject::connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
QObject::connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
bool click = true;
int code = dialog.exec();
bool passed = true;
while (click == true){
passed = true;
if (code == QDialog::Accepted) {
// check if empty questions were entered
if (lineEdit_Q1->text() == "" || lineEdit_Q2->text() == "" || lineEdit_Q3->text() == "") {
QMessageBox msgBox;
msgBox.setText("An error occured while entering questions for the P835 test");
msgBox.setInformativeText("You can not enter empty questions! Please try again or click cancel to use the standard questions!");
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
passed = false;
dialog.close();
dialog.exec();
break;
}
if (lineEdit_Q1_answer->text().split(",").size() != 5 || lineEdit_Q2_answer->text().split(",").size() != 5 || lineEdit_Q3_answer->text().split(",").size() != 5) {
QMessageBox msgBox;
msgBox.setText("An error occured while entering question ratings for the P835 test");
msgBox.setInformativeText("You have to enter exactly 5 ratings for each question! Please try again or click cancel to use the standard ratings!");
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
passed = false;
dialog.close();
dialog.exec();
break;
}
if (oneFileCheckBox->isChecked() && multipleFilesCheckBox->isChecked()) {
QMessageBox msgBox;
msgBox.setText("An error occured while setting up the P835 test...");
msgBox.setInformativeText("You cannot check both boxes! Please select only one option for the files!");
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
passed = false;
dialog.close();
dialog.exec();
break;
}
if (oneFileCheckBox->isChecked() == false && multipleFilesCheckBox->isChecked() == false) {
QMessageBox msgBox;
msgBox.setText("An error occured while setting up the P835 test...");
msgBox.setInformativeText("You have to select one file option!");
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
passed = false;
dialog.close();
dialog.exec();
break;
}
if (passed == true) {
this->configMgr->setQuestions(lineEdit_Q1->text(), lineEdit_Q2->text(), lineEdit_Q3->text());
this->configMgr->setAnswers(lineEdit_Q1_answer->text(), lineEdit_Q2_answer->text(), lineEdit_Q3_answer->text());
if(oneFileCheckBox->isChecked() == true) {
this->configMgr->fileOption = 0;
}
if(multipleFilesCheckBox->isChecked() == true) {
this->configMgr->fileOption = 1;
}
QMessageBox msgBox;
msgBox.setText("Success!");
msgBox.setInformativeText("The questions and the question ratings have been set successfully!");
msgBox.setIcon(QMessageBox::Information);
msgBox.exec();
dialog.close();
click = false;
}
if (code == QDialog::Rejected) {
this->configMgr->setQuestions(Q1_std, Q2_std, Q3_std);
this->configMgr->setAnswers(Q1_std_answer, Q2_std_answer, Q3_std_answer);
QMessageBox msgBox;
msgBox.setText("Setting standard values...");
msgBox.setInformativeText("Standard questions and ratings will be set. Click on the P835 button again to set questions and ratings manually!");
msgBox.setIcon(QMessageBox::Information);
msgBox.exec();
dialog.close();
click = false;
}
}
if (code == QDialog::Rejected) {
this->configMgr->setQuestions(Q1_std, Q2_std, Q3_std);
this->configMgr->setAnswers(Q1_std_answer, Q2_std_answer, Q3_std_answer);
QMessageBox msgBox;
msgBox.setText("Setting standard values...");
msgBox.setInformativeText("Standard questions and ratings will be set. Click on the P835 button again to set questions and ratings manually!");
msgBox.setIcon(QMessageBox::Information);
msgBox.exec();
dialog.close();
click = false;
}
}
In this example code I only put one text box where the user has to enter some information. I have six text boxes and two checkboxes.
I hope you can help me! Thank you!

You should use validation with QDialog::done(int r) or a QWizard. Qt makes this task relatively easy if you take the time to study the examples and documentation on it, but it does take time to learn it the first time.
Validation with QDialog::done(int r)
http://www.qtcentre.org/threads/8048-Validate-Data-in-QDialog
void DataSourceDlg::done(int r)
{
if(QDialog::Accepted == r) // ok was pressed
{
if(nodeLineEdit->text().size() > 3) // validate the data somehow
{
QDialog::done(r);
return;
}
else
{
statusBar->setText("Invalid data in text edit...try again...");
return;
}
}
else // cancel, close or exc was pressed
{
QDialog::done(r);
return;
}
}
Note that by subclassing QDialog and managing the done method you can prevent the dialog from closing and display a message.
QWizard and QWizardPage
http://doc.qt.io/qt-5/qwizard.html#details
Using a QWizard is a little more work, but it is built around validation and ensuring the correct information is in the right boxes.
Basically you subclass QWizard and QWizard page, and then you implement validatePage() and some others methods and you follow the examples and it works flawlessly. This used to be included in Qt Solutions and it was opened up a few years ago.
http://doc.qt.io/qt-5/qtwidgets-dialogs-classwizard-example.html
http://doc.qt.io/qt-5/qtwidgets-dialogs-licensewizard-example.html
companyLabel = new QLabel(tr("&Company name:"));
companyLineEdit = new QLineEdit;
companyLabel->setBuddy(companyLineEdit);
emailLabel = new QLabel(tr("&Email address:"));
emailLineEdit = new QLineEdit;
emailLineEdit->setValidator(new QRegExpValidator(QRegExp(".*#.*"), this));
emailLabel->setBuddy(emailLineEdit);
postalLabel = new QLabel(tr("&Postal address:"));
postalLineEdit = new QLineEdit;
postalLabel->setBuddy(postalLineEdit);
registerField("details.company*", companyLineEdit);
registerField("details.email*", emailLineEdit);
registerField("details.postal*", postalLineEdit);
* makes a field mandatory. QRegExpValidator makes sure the email address has an # sign in the middle.
QValidator with QLineEdit
http://doc.qt.io/qt-5/qvalidator.html#details
http://doc.qt.io/qt-5/qtwidgets-widgets-lineedits-example.html
validatorLineEdit->setValidator(new QIntValidator(
validatorLineEdit));
validatorLineEdit->setValidator(new QDoubleValidator(-999.0,
999.0, 2, validatorLineEdit));
QLineEdit::setInputMask(QString)
http://doc.qt.io/qt-5/qlineedit.html#inputMask-prop
inputMaskLineEdit->setInputMask("");
inputMaskLineEdit->setInputMask("+99 99 99 99 99;_");
inputMaskLineEdit->setInputMask("0000-00-00");
inputMaskLineEdit->setText("00000000");
inputMaskLineEdit->setCursorPosition(0);
inputMaskLineEdit->setInputMask(">AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;#");
Regular Expressions
RegEx is awesome. Learning it is extremely useful (IMHO). I really like the tools and cheatsheets provided by gskinner. http://regexr.com/
http://doc.qt.io/qt-5/qregexpvalidator.html#details
// regexp: optional '-' followed by between 1 and 3 digits
QRegExp rx("-?\\d{1,3}");
QValidator *validator = new QRegExpValidator(rx, this);
QLineEdit *edit = new QLineEdit(this);
edit->setValidator(validator);
Hope that helps.

Related

Accessibility notification in Qt

I am trying to push notification to Jaws or NVDA when certain events occur.
These events can occur any time and any application widget can have the focus. They are not linked to user actions but to the controller events.
This is my try:
void announceNewMessageIfNeeded(){
if(QAccessible::isActive()){
QWidget* focusWidget = QApplication::focusWidget();
if(focusWidget != nullptr){
auto* accessibleInterface = QAccessible::queryAccessibleInterface(focusWidget);
accessibleInterface->setText(QAccessible::Name, "New Message");
auto *ev = new QAccessibleEvent(accessibleInterface, QAccessible::Alert);
QAccessible::updateAccessibility(ev);
}
}
}
I tried the above code with various little changes but I either do not have accesibility update or undesired access to nullpointers.
With debug logs, I know for sure that the focusWidget is correct (it points to the item having the currentFocus) and that announceNewMessageIfNeeded is called.
Any idea?
solution that seems to work:
if(QAccessible::isActive()){
QWidget* focusedWidget = QApplication::focusWidget();
if(focusedWidget != nullptr){
auto *ev = new QAccessibleValueChangeEvent(focusedWidget, "New Message");
QAccessible::updateAccessibility(ev);
}
}

QT Creator, how to 'snap to' a position in tab order following a push button event?

I'm making a form where a user submits their own name, username, etc. but the username must be unique, so it throws up a QMessageBox error if the chosen username is already in use.
If that lineEdit is not first in the tab order, how do you snap to that lineEdit after the click event?
{
UserDB userconn;
QString name,username,password,number,userid,userid2;
name=ui->lineEdit_firstlastname->text();
username=ui->lineEdit_username->text();
password=ui->lineEdit_password->text();
number=ui->lineEdit_phonenumber->text();
userid="Admin";
userid2="User";
QByteArray prehash;
prehash.append(password);
QString hashword = QString(QCryptographicHash::hash(prehash,QCryptographicHash::Md5).toHex());
QSqlQuery qry2;
qry2.prepare("select * from user where username='"+username+"'");
if(qry2.exec())
{
int count=0;
while(qry2.next())\
{
count++;
}
if(count==1)
{
QMessageBox::critical(this,tr("Error!"),tr("Choose a different username!"));
ui->lineEdit_username->setText("");
}
else....
{
/*
here, if the user submits a duplicate username, a
QMessageBox pops up telling the user to choose a
different username, and the focus returns to either the
pushButton or to the last lineEdit before the user
pressed Enter, and in this case, I'd like to return the
focus to that lineEdit that needs to be edited.
*/
}
}
}
What you are looking for is the Qt function setFocus().
Another stackoverflow thread: Set QLineEdit focus in Qt provides different answers on how to use it.

How do I wait for a keypress in a Qt GUI application

I am writing a math practice program for my grandson. I first wrote it as a console app as below and everything worked fine.
for(int i = 0; i <= 9; i++)
{
n2Digit = GetNewDigit(iProblemsWorkedArray);
int validAnswer = add(n1Digit, n2Digit);
bool noKey = true;
while(noKey)
{
char KeyPressed = getch();
int KeyAscii = KeyPressed;
if((KeyAscii >= 48) && (KeyAscii <= 57) && (validAnswer < 10))
{
studentAnswer = KeyAscii - 48;
noKey = false;
}
yada yada...
}
}
Now I want to write the same thing using the Qt GUI, but found that the
char KeyPressed = getch();
within the while loop no longer works in GUI mode.
I have searched for days and come to the conclusion that I must be phrasing the search wrong. Would someone please help ?
My guess is that you're creating a list of questions with lettered or numbered answers, and you want the user (grandson) to press a key to answer it, rather than enter the answer into an edit field.
Assuming that's correct, you can do one of two things:
Subclass a widget where you display the questions, and reimplement the keyPressEvent method. Qt calls this method for the widget that gets the keyboard focus. It passes a QKeyEvent objects, which contains the key that was pressed. You can then examine the key to see if it's correct or not, and provide feedback. See the QWidget::keyPressEvent documentation for more information.
Without subclassing, you can create an event filter and install it on a standard widget. The event filter receives the events sent to the target widget, and you can intercept QKeyEvent events, inspect them for a correct or incorrect answer and respond accordingly. See the QObject::installEventFilter documentation for more information.

How to delete selected row from TableView? (JavaFX with FXML)

So, I'm trying to delete the highlighted row in TableView in my program.
I've looked at loads of tutorials online, but really can't get my head around this.
I have followed the example from CodeMakery, which is in Eclipse, but I'm unable to get it to work on IntelliJ (because of some apparent JDK problems?)
This is the code from CodeMakery:
private void handleDeletePerson() {
int selectedIndex = personTable.getSelectionModel().getSelectedIndex();
if (selectedIndex >= 0) {
personTable.getItems().remove(selectedIndex);
} else {
// Nothing selected.
Alert alert = new Alert(AlertType.WARNING);
alert.initOwner(mainApp.getPrimaryStage());
alert.setTitle("No Selection");
alert.setHeaderText("No Person Selected");
alert.setContentText("Please select a person in the table.");
alert.showAndWait();
}
}
Could you please help me understand how to make selected row get deleted?
just add at the beginning of this method personTable.setEditable(true). Should work now.

Trouble getting `QWidget* editor` for a `QModelIndex`

I am having trouble augmenting the default edit behavior of QTableView. I want the following behavior when the Enter key is pressed:
Start editing the current cell if it is not already being edited.
If the cell is being edited,
2a. commit the data and close the editor. Then,
2b. make the cell below, if present, the current cell.
2a is the default behavior, and 2b can likely be achieved by using QAbstractItemView::setCurrentIndex() in a re-implementation of QItemDelegate::eventFilter() (as suggested here in a similar context).
The problem is in achieving 1. I list below the approaches I have tried till now.
Reconfigure the "platform edit key" By default, "Editing starts when the platform edit key has been pressed over an item." (QAbstractItemView::EditKeyPressed) This key is F2 on my platform (Ubuntu 12.04). I could reconfigure the platform edit key to Enter but
Altering platform defaults seems like a bad idea.
I could not find out how to do it.
Capture the Enter key press I use QShortCut to do this as follows:
class CourseTable : public QTableView {
/* ... */
};
/* ... */
CourseTable::CourseTable(/* ... */) {
/* ... */
QShortcut* shortcut = new QShortcut(QKeySequence(Qt::Key_Return), this);
connect(shortcut, SIGNAL(activated()), this, SLOT(handleEnter_()));
/* ... */
}
/* ... */
void CourseTable::handleEnter_() {
QModelIndex idx = this->currentIndex();
if (this->state() != QAbstractItemView::EditingState)
this->edit(idx);
/* else // see below */
}
This does capture the Enter key-press and accomplishes 1 (from above) but now 2 is broken. So, I need to look into the else clause in CourseTable::handleEnter_() above, possibly calling QAbstractItemView::commitData() and QAbstractItemView::closeEditor in it. The problem is that both these functions require a QWidget *editor argument which I just cannot figure out how to get. I could subclass QAbstractItemDelegate, add a getEditor() method to the derived class, and modify existing code to pass instances of the derived delegate class to CourseTable::setItemDelegate*() functions. But that sounds like too much work.
So, any ideas how I can cleanly accomplish both 1 and 2 without having to rewrite my code?
Why cant you just filter the event also for starting the edit?
Just handle the event if state is != QAbstractItemView::EditingState
Returning true in this function makes the event stop propagating to the filtered object.
If state is Editing you can just return falseand allow the table and editor continue processing the event.
Something like this:
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == tableView && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Return && tableView->state() != QAbstractItemView::EditingState) {
// set current cell to edit
return true;
} else
return false;
}
return false;
}

Resources