ScientificSpinBox

ScientificSpinBox is a QAbstractSpinBox subclass for entering and displaying double-precision floating-point values in either fixed or scientific notation. It is used throughout Blackchirp wherever a numeric field must accept values that span many orders of magnitude — RF frequencies, timing delays, voltage amplitudes, and similar quantities.

The widget enforces an 18-character input ceiling (16 significant digits plus a decimal point and a sign), validates input with a QDoubleValidator that accepts scientific notation, and carries an optional unit suffix (e.g. " MHz") that is appended to the displayed text but stripped before parsing.

Display modes

The DisplayMode enum controls how the value is formatted when the widget is not being edited:

  • Auto — uses fixed-point notation when the absolute value falls in [1e-6, 1e6); switches to scientific notation outside that range. This is the default.

  • Fixed — always fixed-point, regardless of magnitude.

  • Scientific — always scientific notation, rendered with superscript exponents in the display text.

The user can change the active mode at runtime via the right-click context menu. The mode can also be set programmatically with setDisplayMode().

When the widget receives focus for editing, the text switches to standard printf-style scientific notation (1.234567e+03) regardless of the display mode, and the full content is selected so the user can type a replacement value immediately. When focus is lost the edited text is parsed, the value is clamped to [minimum(), maximum()], and the display text is regenerated in the current display mode.

Step modes

The StepMode enum controls how large each increment or decrement step is:

  • Adaptive (default) — the step size tracks the place value of the least-significant digit shown in the display. For a fixed-mode value of 1234.56 the step is 0.01; for a scientific-mode value of 1.23456e+03 with four decimal places in the mantissa the step is 0.1 (i.e. 1e+03 × 10^{-4}). The nominal singleStep() value is not used in Adaptive mode.

  • Fixed — every step adds or subtracts the constant returned by fixedStepSize(). Set both with setFixedStepSize() and setStepMode(StepMode::Fixed).

In either mode, holding Ctrl while stepping changes behavior:

  • Adaptive mode with Ctrl — doubles the value on up-step and halves it on down-step (value × 2^steps). If the value is zero, Ctrl-up sets it to 1.0 and Ctrl-down sets it to -1.0.

  • Fixed mode with Ctrl — multiplies fixedStepSize() by 10 for that step.

Suffixes

A unit suffix set with setSuffix() is appended to every display string and stripped before any parse operation. Setting a suffix adjusts the line-edit maxLength to MAX_INPUT_LENGTH + suffix.length() so the suffix never displaces input characters.

Precision

By default (displayPrecision() == -1) the widget infers precision from the text the user typed. Call setDisplayPrecision(n) to fix the number of decimal places; pass -1 to restore automatic inference. The precision spinner in the context menu exposes the same control interactively.

API Reference

class ScientificSpinBox : public QAbstractSpinBox

Spin box accepting and displaying floating-point values in fixed or scientific notation.

Public Types

enum class DisplayMode

Controls how the value is formatted in the display (non-editing) state.

Values:

enumerator Auto

Fixed notation when |value| is in [1e-6, 1e6); scientific otherwise.

enumerator Fixed

Always fixed-point notation.

enumerator Scientific

Always scientific notation.

enum class StepMode

Controls how the step size is calculated when the user increments or decrements.

Values:

enumerator Adaptive

Step size tracks the least-significant digit of the displayed value.

enumerator Fixed

Step size is the constant set by setFixedStepSize().

Public Functions

explicit ScientificSpinBox(QWidget *parent = nullptr)

Construct a ScientificSpinBox with the given parent widget.

~ScientificSpinBox() override = default
double value() const

Return the current value.

void setValue(double value)

Set the value, clamping to [minimum(), maximum()].

double minimum() const

Return the lower bound of the allowed range.

void setMinimum(double min)

Set the lower bound; adjusts the current value if it falls below the new minimum.

double maximum() const

Return the upper bound of the allowed range.

void setMaximum(double max)

Set the upper bound; adjusts the current value if it exceeds the new maximum.

void setRange(double min, double max)

Convenience; equivalent to calling setMinimum() then setMaximum().

double singleStep() const

Return the nominal single-step size used in Adaptive mode.

void setSingleStep(double step)

Set the nominal single-step size used in Adaptive mode.

StepMode stepMode() const

Return the current step mode.

void setStepMode(StepMode mode)

Set the step mode.

double fixedStepSize() const

Return the fixed step size used when stepMode() is StepMode::Fixed.

void setFixedStepSize(double size)

Set the fixed step size; has no effect unless stepMode() is StepMode::Fixed.

int displayPrecision() const

Return the display precision (number of decimal places), or -1 for automatic.

void setDisplayPrecision(int precision)

Set the display precision.

Pass -1 to restore automatic precision detection. Valid range is [-1, 15].

DisplayMode displayMode() const

Return the current display mode.

void setDisplayMode(DisplayMode mode)

Set the display mode and refresh the displayed text.

QString suffix() const

Return the unit suffix appended to the displayed text.

void setSuffix(const QString &suffix)

Set the unit suffix (e.g. " MHz"). Pass an empty string to clear.

void stepBy(int steps) override

Increment or decrement the value by steps logical steps.

In Adaptive mode the step size tracks the least-significant digit of the displayed value. In Fixed mode the step size is fixedStepSize(). When the Ctrl modifier is held, Adaptive mode doubles or halves the value instead of adding a step, and Fixed mode multiplies the fixed step by 10.

QValidator::State validate(QString &input, int &pos) const override

Validate the line-edit contents; delegates to a QDoubleValidator.

void fixup(QString &input) const override

Attempt to interpret input as a valid double and reformat it.

QSize sizeHint() const override

Return the preferred size, wide enough to show typical fixed and scientific text.

QSize minimumSizeHint() const override

Return the minimum acceptable size.

QString textFromValue(double value) const

Format value as display or edit text depending on the current editing state.

double valueFromText(const QString &text) const

Parse text (stripping superscript notation and the suffix) and return its double value.

QLineEdit *lineEdit() const

Return the underlying QLineEdit for external validation or styling.

Public Slots

void selectAll()

Select all text in the line edit.

void clear() override

Reset the value to 0.0.

Signals

void valueChanged(double value)

Emitted whenever the value changes.

void editingFinished()

Emitted when the user finishes editing (equivalent to QLineEdit::editingFinished).

Protected Functions

QAbstractSpinBox::StepEnabled stepEnabled() const override

Return which step directions are enabled based on the current value and range.

void keyPressEvent(QKeyEvent *event) override

Handle Up/Down arrow keys for stepping; delegate everything else to QAbstractSpinBox.

void focusInEvent(QFocusEvent *event) override

Switch to edit notation and select all text when focus is received.

void focusOutEvent(QFocusEvent *event) override

Commit the edited value and switch back to display notation when focus is lost.

void wheelEvent(QWheelEvent *event) override

Step the value on wheel events when the widget has focus; ignore otherwise.

void contextMenuEvent(QContextMenuEvent *event) override

Show a context menu offering display-mode, step-size, and precision controls.

Private Functions

void initializeValidator() const
void updateDisplayText()
void updateEditText()
QString formatForDisplay(double value) const
QString formatForEdit(double value) const
int detectPrecision(const QString &text) const
double calculateStepSize() const
QString applySuperscript(const QString &text) const
QString removeSuperscript(const QString &text) const
QString stripSuffix(const QString &text) const
bool isValidInput(const QString &text) const
void emitValueChanged()
int detectSciPrecision(double value) const
int detectFixedPrecision(double value) const

Private Members

double d_value = {0.0}
double d_minimum = {-std::numeric_limits<double>::max()}
double d_maximum = {std::numeric_limits<double>::max()}
double d_singleStep = {1.0}
int d_displayPrecision = {-1}
bool d_precisionExplicit = {false}
DisplayMode d_displayMode = {DisplayMode::Auto}
StepMode d_stepMode = {StepMode::Adaptive}
double d_fixedStepSize = {0.0}
QString d_suffix
bool d_isEditing = {false}
QString d_cachedDisplayText
QString d_cachedEditText
Qt::KeyboardModifiers d_currentWheelModifiers = {Qt::NoModifier}
mutable std::unique_ptr<QDoubleValidator> d_validator

Private Slots

void onEditingFinished()
void onTextChanged()

Private Static Attributes

static constexpr int MAX_INPUT_LENGTH = 18
static constexpr int MAX_PRECISION = 15