Rt Rtt
Rt Rtt

Reputation: 617

Qt: How to set QSpinBox/QDoubleSpinBox to angular values?

QSpinBox/QDoubleSpinBox has default of decimal value. Is it possible to change decimal to angular?

UPDATE> Please see the picture. This is the effect i want to get in QSpinBox/QDoubleSpinBox.

enter image description here

Upvotes: 0

Views: 1828

Answers (2)

dtech
dtech

Reputation: 49279

First - using a single spin box to manipulate such a value is not applicable, because it represents a significant range. A dedicated spin box for every component would be much easier from a user experience standpoint.

Second - if you still insist on a single spinbox, Qt doesn't have a widget that can do that out of the box. You can however easily extend on QDoubleSpinBox and overload the textFromValue() method, which is the one that determines what text the spin box is showing, and then you can compose a string value from whatever raw data you have to represent the location.

Upvotes: 1

Marek R
Marek R

Reputation: 37512

Yes it is possible. Just override valueFromText, textFromValue and validate (optionally fixup).

  • textFromValue should be easy
  • valueFromText and validate requires a parsing functionality, so extract common method for it.
  • fixup use only if you know how to correct user mistakes and when validate returns QValidator::Intermediate in some cases.

See documentation.

Here is my solution C++11:

#ifndef DEGREESPINBOX_H
#define DEGREESPINBOX_H

#include <QDoubleSpinBox>

class DegreeSpinBox : public QDoubleSpinBox
{
    Q_OBJECT
public:
    using QDoubleSpinBox::QDoubleSpinBox;

    QString textFromValue(double value) const override;
    double valueFromText(const QString &text) const override;
    QValidator::State validate(QString &text, int &pos) const override;

private:
    static void extractDegMinSecFrom(double value,
                                     int *degrees,
                                     int *minutes,
                                     double *seconds);

    static bool parse(QString str, double *result);
    static bool areSeparatorsValid(QChar deg, QChar min, QChar sec);
};

#endif // DEGREESPINBOX_H

cpp file

#include "degreespinbox.h"
#include <QTextStream>
#include <cmath>

QString DegreeSpinBox::textFromValue(double value) const {
    int degrees, minutes;
    double seconds;
    extractDegMinSecFrom(value, &degrees, &minutes, &seconds);
    return QString(tr("%1° %2′ %3″"))
            .arg(degrees).arg(minutes).arg(seconds);
}

double DegreeSpinBox::valueFromText(const QString &text) const {
    double result = 0;
    auto ok = parse(text, &result);
    Q_ASSERT(ok);
    return result;
}

QValidator::State DegreeSpinBox::validate(QString &text, int &pos) const {
    Q_UNUSED(pos)
    return parse(text, nullptr) ? QValidator::Acceptable : QValidator::Invalid;
}

void DegreeSpinBox::extractDegMinSecFrom(double value,
                                         int *degrees,
                                         int *minutes,
                                         double *seconds) {
    Q_ASSERT(degrees);
    Q_ASSERT(minutes);
    Q_ASSERT(seconds);
    double degreesDouble;
    double minutesDouble = std::modf(value, &degreesDouble) * 60;
    *degrees = static_cast<int>(degreesDouble);
    *seconds = std::modf(minutesDouble, &minutesDouble) * 60;
    *minutes = static_cast<int>(minutesDouble);
}

bool DegreeSpinBox::parse(QString str, double *result) {
    QTextStream stream(&str);

    int degrees(0), minutes(0);
    double seconds(0);
    // %1° %2′ %3″"
    QChar symbolDegree(0), symbolMinutes(0), symbolSeconds(0);
    stream >> degrees >> symbolDegree
            >> ws >> minutes >> symbolMinutes
            >> ws >> seconds >> symbolSeconds;

    bool success = stream.status() == QTextStream::Ok
            && areSeparatorsValid(symbolDegree, symbolMinutes, symbolSeconds)
            && minutes >=0 && minutes < 60
            && seconds >=0 && seconds < 60;

    if (success && result) {
        *result = (seconds / 60.0 + minutes) / 60.0 + degrees;
    }
    return success;
}


bool DegreeSpinBox::areSeparatorsValid(QChar deg, QChar min, QChar sec) {
    return deg == QChar(L'°')
            && min == QChar(L'′')
            && sec == QChar(L'″');
}

Upvotes: 0

Related Questions