Trolltech | Documentation | Qt Quarterly | « Implementing a Read/Write Mutex | Not Your Standard Pie Menu »

Mandatory Fields
by Mark Summerfield
There are many situations where we need to present the user with a form that has mandatory fields. In this article, we present a class that simplifies handling such fields.

[Download Source Code]

When a form has mandatory fields, we need to specify which ones they are and arrange for the OK button to be enabled only if all the mandatory fields have values. This can be achieved using the MandatoryFieldGroup class. Here's how it is used in a dialog's constructor:

    MoneyTransferDialog::MoneyTransferDialog(QWidget *parent)
        : QDialog(parent)
    {
        ...
        MandatoryFieldGroup *group =
                new MandatoryFieldGroup(this);
        group->add(toAccountComboBox);
        group->add(amountLineEdit);
        group->setOkButton(acceptButton);
    }
    

As the screenshot below shows, we've chosen to highlight mandatory fields by using a yellow background. (For comboboxes, in some styles, the yellow only appears when the text is editable, or when the list is pulled down.) Checkboxes only need to be made mandatory if they are tri-state, and their initial state is NoChange.

Mandatory1

Now that we've seen the usage, let's look at the definition:

    class MandatoryFieldGroup : public QObject
    {
        Q_OBJECT
    public:
        MandatoryFieldGroup(QObject *parent)
            : QObject(parent), okButton(0) {}
    
        void add(QWidget *widget);
        void remove(QWidget *widget);
        void setOkButton(QPushButton *button);
    
    private slots:
        void changed();
    
    private:
        QValueList<QWidget *> widgets;
        QPushButton *okButton;
    };
    

The widgets value list is used to keep track of all the mandatory widgets, and the okButton is used to refer to the dialog's OK button. The constructor only needs to initialize the okButton.

    void MandatoryFieldGroup::add(QWidget *widget)
    {
        if (!widgets.contains(widget)) {
            if (widget->inherits("QCheckBox")) {
                connect(widget, SIGNAL(clicked()),
                        this, SLOT(changed()));
            } else if (widget->inherits("QComboBox")) {
                connect(widget, SIGNAL(highlighted(int)),
                        this, SLOT(changed()));
            } else if (widget->inherits("QLineEdit")) {
                connect(widget,
                        SIGNAL(textChanged(const QString &)),
                        this, SLOT(changed()));
            } else {
                qWarning("%s unhandled", widget->className());
                return;
            }
            widget->setPaletteBackgroundColor(yellow);
            widgets.append(widget);
            changed();
        }
    }
    

If the added widget isn't already in the mandatory field group, we see what type of widget it is derived from and connect the widget's most appropriate signal to our changed() slot. Then we set the background color, add the widget to the list of mandatory widgets, and call the changed() slot to update the OK button.

    void MandatoryFieldGroup::setOkButton(QPushButton *button)
    {
        if (okButton && okButton != button)
            okButton->setEnabled(true);
        okButton = button;
        changed();
    }
    

If the the OK button has changed, we enable the old one. Then we call changed() to update the button's enabled state.

    void MandatoryFieldGroup::changed()
    {
        if (!okButton)
            return;
        bool enable = true;
        QValueList<QWidget *>::const_iterator i;
        for (i = widgets.begin(); i != widgets.end(); ++i) {
            QWidget *widget = *i;
            if (widget->inherits("QCheckBox")) {
                if (((QCheckBox *)widget)->state() ==
                    QButton::NoChange) {
                    enable = false;
                    break;
                }
            } else if (widget->inherits("QComboBox")) {
                if (currentText().isEmpty()) {
                    enable = false;
                    break;
                }
            } else if (widget->inherits("QLineEdit")) {
                if (((QLineEdit *)widget)->text().isEmpty()) {
                    enable = false;
                    break;
                }
            }
        }
        okButton->setEnabled(enable);
    }
    

We do nothing if no OK button has been set. Otherwise we assume that all the mandatory fields have values and iterate over them to check. If any field is empty (or unchanged in the case of a checkbox), we can leave the loop immediately since we will be disabling the OK button. At the end we enable or disable the OK button as appropriate.

In some situations a field may no longer be mandatory. We can handle this by calling remove() (not shown), which restores the original background color, removes the widget from the list of mandatory widgets, and calls changed() to update the state of the OK button.


This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.

Copyright © 2004 Trolltech Trademarks Not Your Standard Pie Menu »