# Spin Boxes Example

The Spin Boxes example shows how to use the many different types of spin boxes available in Qt, from a simple QSpinBox widget to more complex editors like the QDateTimeEdit widget.

The example consists of a single `Window` class that is used to display the different spin box-based widgets available with Qt.

## Window Class Implementation

The `Window` class inherits QWidget and contains two slots that are used to provide interactive features:

        public class SpinBoxes extends QWidget {
            private QDateTimeEdit meetingEdit;
            private QDoubleSpinBox doubleSpinBox;
            private QDoubleSpinBox priceSpinBox;
            private QDoubleSpinBox scaleSpinBox;
            private QGroupBox spinBoxesGroup;
            private QGroupBox editsGroup;
            private QGroupBox doubleSpinBoxesGroup;
            private QLabel meetingLabel;

Private methods (shown later) are used to set up each type of spin box in the window. We use variables to keep track of various widgets so that they can be reconfigured when required.

The constructor simply calls private methods to set up the different types of spin box used in the example, and places each group in a layout:

            public SpinBoxes() {
                this(null);
            }

We use the layout to manage the arrangement of the window's child widgets, and change the window title.

The `createSpinBoxes()` method constructs a QGroupBox and places three QSpinBox widgets inside it with descriptive labels to indicate the types of input they expect.

                createSpinBoxes();
                createDateTimeEdits();
                createDoubleSpinBoxes();

                QHBoxLayout layout = new QHBoxLayout();
                layout.addWidget(spinBoxesGroup);
                layout.addWidget(editsGroup);
                layout.addWidget(doubleSpinBoxesGroup);
                setLayout(layout);

                setWindowTitle(tr("Spin Boxes"));

                setWindowIcon(new QIcon("classpath:com/trolltech/classpath:com/trolltech/images/qt-logo.png"));
            }

            public void createSpinBoxes() {
                spinBoxesGroup = new QGroupBox(tr("Spinboxes"));

                QLabel integerLabel = new QLabel(String.format(tr("Enter a value between %1$d and %2$d:"), -20, 20));
                QSpinBox integerSpinBox = new QSpinBox();
                integerSpinBox.setRange(-20, 20);
                integerSpinBox.setSingleStep(1);
                integerSpinBox.setValue(0);

The first spin box shows the simplest way to use QSpinBox. It accepts values from -20 to 20, the current value can be increased or decreased by 1 with either the arrow buttons or Up and Down keys, and the default value is 0.

The second spin box uses a larger step size and displays a suffix to provide more information about the type of data the number represents:

                QLabel zoomLabel = new QLabel(String.format(tr("Enter a zoom value between %1$d and %2$d:"), 0, 1000));
                QSpinBox zoomSpinBox = new QSpinBox();
                zoomSpinBox.setRange(0, 1000);
                zoomSpinBox.setSingleStep(10);
                zoomSpinBox.setSuffix("%");
                zoomSpinBox.setSpecialValueText(tr("Automatic"));
                zoomSpinBox.setValue(100);

This spin box also displays a special value instead of the minimum value defined for it. This means that it will never show 0%, but will display Automatic when the minimum value is selected.

The third spin box shows how a prefix can be used:

                QLabel priceLabel = new QLabel(String.format(tr("Enter a price between %1$d and %2$d:"), 0, 999));
                QSpinBox priceSpinBox = new QSpinBox();
                priceSpinBox.setRange(0, 999);
                priceSpinBox.setSingleStep(1);
                priceSpinBox.setPrefix("$");
                priceSpinBox.setValue(99);

For simplicity, we show a spin box with a prefix and no suffix. It is also possible to use both at the same time.

                QVBoxLayout spinBoxLayout = new QVBoxLayout();
                spinBoxLayout.addWidget(integerLabel);
                spinBoxLayout.addWidget(integerSpinBox);
                spinBoxLayout.addWidget(zoomLabel);
                spinBoxLayout.addWidget(zoomSpinBox);
                spinBoxLayout.addWidget(priceLabel);
                spinBoxLayout.addWidget(priceSpinBox);
                spinBoxesGroup.setLayout(spinBoxLayout);
            }

The rest of the method sets up a layout for the group box and places each of the widgets inside it.

The `createDateTimeEdits()` method constructs another group box with a selection of spin boxes used for editing dates and times.

            public void createDateTimeEdits() {
                editsGroup = new QGroupBox(tr("Date and time spin boxes"));

                QLabel dateLabel = new QLabel();
                QDateEdit dateEdit = new QDateEdit(QDate.currentDate());
                dateEdit.setDateRange(new QDate(2005, 1, 1), new QDate(2010, 12, 31));
                dateLabel.setText(String.format(tr("Appointment date (between %1$s and %2$s):"),
                                                dateEdit.minimumDate().toString(Qt.DateFormat.ISODate),
                                                dateEdit.maximumDate().toString(Qt.DateFormat.ISODate)));

The first spin box is a QDateEdit widget that is able to accept dates within a given range specified using QDate values. The arrow buttons and Up and Down keys can be used to increase and decrease the values for year, month, and day when the cursor is in the relevant section.

The second spin box is a QTimeEdit widget:

                QLabel timeLabel = new QLabel();
                QTimeEdit timeEdit = new QTimeEdit(QTime.currentTime());
                timeEdit.setTimeRange(new QTime(9, 0, 0, 0), new QTime(16, 30, 0, 0));
                timeLabel.setText(String.format(tr("Appointment time (between %1$s and %2$s):"),
                                                timeEdit.minimumTime().toString(Qt.DateFormat.ISODate),
                                                timeEdit.maximumTime().toString(Qt.DateFormat.ISODate)));

Acceptable values for the time are defined using QTime values.

The third spin box is a QDateTimeEdit widget that can display both date and time values, and we place a label above it to indicate the range of allowed times for a meeting. These widgets will be updated when the user changes a format string.

                meetingLabel = new QLabel();
                meetingEdit = new QDateTimeEdit(QDateTime.currentDateTime());

The format string used for the date time editor, which is also shown in the string displayed by the label, is chosen from a set of strings in a combobox:

                QLabel formatLabel = new QLabel(tr("Format string for the meeting date and time:"));
                QComboBox formatComboBox = new QComboBox();
                formatComboBox.addItem("yyyy-MM-dd hh:mm:ss (zzz 'ms')");
                formatComboBox.addItem("hh:mm:ss MM/dd/yyyy");
                formatComboBox.addItem("hh:mm:ss dd/MM/yyyy");
                formatComboBox.addItem("hh:mm:ss");
                formatComboBox.addItem("hh:mm ap");
                formatComboBox.activated.connect(this, "setFormatString(String)");

A signal from this combobox is connected to a slot in the `Window` class (shown later).

                QVBoxLayout editsLayout = new QVBoxLayout();
                editsLayout.addWidget(dateLabel);
                editsLayout.addWidget(dateEdit);
                editsLayout.addWidget(timeLabel);
                editsLayout.addWidget(timeEdit);
                editsLayout.addWidget(meetingLabel);
                editsLayout.addWidget(meetingEdit);
                editsLayout.addWidget(formatLabel);
                editsLayout.addWidget(formatComboBox);
                editsGroup.setLayout(editsLayout);
            }

Each child widget of the group box in placed in a layout.

The `setFormatString()` slot is called whenever the user selects a new format string in the combobox. The display format for the QDateTimeEdit widget is set using the raw string passed by the signal:

            public void setFormatString(String formatString) {
                meetingEdit.setDisplayFormat(formatString);

Depending on the visible sections in the widget, we set a new date or time range, and update the associated label to provide relevant information for the user:

                if (meetingEdit.displayedSections().isSet(QDateTimeEdit.Section.DateSections_Mask)) {
                    meetingEdit.setDateRange(new QDate(2004, 11, 1), new QDate(2005, 11, 30));
                    meetingLabel.setText(String.format(tr("Meeting date (between %1$s and %2$s):"),
                                                       meetingEdit.minimumDate().toString(Qt.DateFormat.ISODate), meetingEdit.maximumDate().toString(Qt.DateFormat.ISODate)));
                } else {
                    meetingEdit.setTimeRange(new QTime(0, 7, 20, 0), new QTime(21, 0, 0, 0));
                    meetingLabel.setText(String.format(tr("Meeting time (between %1$s and %2$s):"), meetingEdit.minimumTime().toString(Qt.DateFormat.ISODate), meetingEdit.maximumTime().toString(Qt.DateFormat.ISODate)));
                }
            }

When the format string is changed, there will be an appropriate label and entry widget for dates, times, or both types of input.

The `createDoubleSpinBoxes()` method constructs three spin boxes that are used to input double-precision floating point numbers:

            public void createDoubleSpinBoxes() {
                doubleSpinBoxesGroup = new QGroupBox(tr("Double precision spinboxes"));

                QLabel precisionLabel = new QLabel(tr("Number of decimal places to show:"));
                QSpinBox precisionSpinBox = new QSpinBox();
                precisionSpinBox.setRange(0, 13);
                precisionSpinBox.setValue(2);

Before the QDoubleSpinBox widgets are constructed, we create a spin box to control how many decimal places they show. By default, only two decimal places are shown in the following spin boxes, each of which is the equivalent of a spin box in the group created by the `createSpinBoxes()` method.

The first double spin box shows a basic double-precision spin box with the same range, step size, and default value as the first spin box in the `createSpinBoxes()` method:

                QLabel doubleLabel = new QLabel(String.format(tr("Enter a value between %1$d and %2$d:"), -20, 20));
                doubleSpinBox = new QDoubleSpinBox();
                doubleSpinBox.setRange(-20.0, 20.0);
                doubleSpinBox.setSingleStep(1.0);
                doubleSpinBox.setValue(0.0);

However, this spin box also allows non-integer values to be entered.

The second spin box displays a suffix and shows a special value instead of the minimum value:

                QLabel scaleLabel = new QLabel(String.format(tr("Enter a scale factor between %1$d and %2$d:"), 0, 1000));
                scaleSpinBox = new QDoubleSpinBox();
                scaleSpinBox.setRange(0.0, 1000.0);
                scaleSpinBox.setSingleStep(10.0);
                scaleSpinBox.setSuffix("%");
                scaleSpinBox.setSpecialValueText(tr("No scaling"));
                scaleSpinBox.setValue(100.0);

The third spin box displays a prefix instead of a suffix:

                QLabel priceLabel = new QLabel(String.format(tr("Enter a price between %1$d and %2$d:"), 0, 1000));
                priceSpinBox = new QDoubleSpinBox();
                priceSpinBox.setRange(0.0, 1000.0);
                priceSpinBox.setSingleStep(1.0);
                priceSpinBox.setPrefix("$");
                priceSpinBox.setValue(99.99);

                precisionSpinBox.valueChanged.connect(this, "changePrecision(int)");

We connect the QSpinBox widget that specifies the precision to a slot in the `Window` class.

                QVBoxLayout spinBoxLayout = new QVBoxLayout();
                spinBoxLayout.addWidget(precisionLabel);
                spinBoxLayout.addWidget(precisionSpinBox);
                spinBoxLayout.addWidget(doubleLabel);
                spinBoxLayout.addWidget(doubleSpinBox);
                spinBoxLayout.addWidget(scaleLabel);
                spinBoxLayout.addWidget(scaleSpinBox);
                spinBoxLayout.addWidget(priceLabel);
                spinBoxLayout.addWidget(priceSpinBox);
                doubleSpinBoxesGroup.setLayout(spinBoxLayout);
            }

The rest of the method places each of the widgets into a layout for the group box.

The `changePrecision()` slot is called when the user changes the value in the precision spin box:

            public void changePrecision(int decimals) {
                doubleSpinBox.setDecimals(decimals);
                scaleSpinBox.setDecimals(decimals);
                priceSpinBox.setDecimals(decimals);
            }

This method simply uses the integer supplied by the signal to specify the number of decimal places in each of the QDoubleSpinBox widgets. Each one of these will be updated automatically when their decimals property is changed.

Finally, we provide a `main()` method to create and show the `SpinBoxes` widget when the example is run:

            public static void main(String args[]) {
                QApplication.initialize(args);

                SpinBoxes spinBoxes = new SpinBoxes();
                spinBoxes.show();

                QApplication.exec();
            }
        }