Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/widgets/qquantity.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ w.show()
app.exec()
```

!!! note

`QQuantity` currently supports simple units with exponents, e.g., `meters^2` or
`1/second`. However, compound units, e.g. `meter/second`, `Newton`, etc.,
are not currently supported.

{{ show_widget(150) }}

{{ show_members('superqt.QQuantity') }}
25 changes: 16 additions & 9 deletions src/superqt/spinbox/_quantity.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ def unitRegistry(self) -> UnitRegistry:
"""Return the pint UnitRegistry used by this widget."""
return self._ureg

def _get_unit_options(self, units: Unit) -> list[Unit]:
if len(units.dimensionality) > 1:
raise NotImplementedError(
"QQuantity does not currently support quantities with non-simple units,"
" e.g. `meter/second` or `Newton`."
)
dims, exp = next(iter(units.dimensionality.items()))

options = DEFAULT_OPTIONS.get(dims, [])
if exp != 1:
options = [f"({u})^{exp}" for u in options]
return [Unit(u) for u in options]

def _update_units_combo_choices(self):
if self._value.dimensionless:
with signals_blocked(self._units_combo):
Expand All @@ -122,13 +135,7 @@ def _update_units_combo_choices(self):
return

units = self._value.units
dims, exp = next(iter(units.dimensionality.items()))
if exp != 1:
raise NotImplementedError("Inverse units not yet implemented")
options = [
self._format_units(self._ureg.Unit(u))
for u in DEFAULT_OPTIONS.get(dims, [])
]
options = [self._format_units(u) for u in self._get_unit_options(units)]
current = self._format_units(units)
with signals_blocked(self._units_combo):
self._units_combo.clear()
Expand Down Expand Up @@ -202,7 +209,7 @@ def setMagnitude(self, magnitude: Number) -> None:
"""Set the magnitude of the current value."""
self.setValue(self._ureg.Quantity(magnitude, self._value.units))

def setUnits(self, units: str | Unit | Quantity) -> None:
def setUnits(self, units: str | Unit | Quantity | None) -> None:
"""Set the units of the current value.

If `units` is `None`, will convert to a dimensionless quantity.
Expand Down Expand Up @@ -231,4 +238,4 @@ def unitsComboBox(self) -> QComboBox:
def _format_units(self, u: Unit | str) -> str:
if isinstance(u, str):
return u
return f"{u:~}" if self._abbreviate_units else f"{u:}"
return f"{u:~P}" if self._abbreviate_units else f"{u:}"
24 changes: 24 additions & 0 deletions tests/test_quantity.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from pint import Quantity

from superqt import QQuantity
Expand Down Expand Up @@ -33,6 +34,29 @@ def test_qquantity(qtbot):
assert w.magnitude() == 1


def test_qquantity_exponents(qtbot):
w = QQuantity(1, "m^2")
qtbot.addWidget(w)

assert w.value() == 1 * w.unitRegistry().meter ** 2
assert w.magnitude() == 1
assert w.units() == w.unitRegistry().meter ** 2
assert w.text() == "1 meter ** 2"
w.setUnits("cm^2")
assert w.value() == 10000 * w.unitRegistry().centimeter ** 2
assert w.magnitude() == 10000
assert w.units() == w.unitRegistry().centimeter ** 2
assert w.text() == "10000.0 centimeter ** 2"


def test_qquantity_non_simple_units(qtbot):
with pytest.raises(NotImplementedError):
qtbot.addWidget(QQuantity(1, "m/s"))

with pytest.raises(NotImplementedError):
qtbot.addWidget(QQuantity(1, "N"))


def test_change_qquantity_value(qtbot):
w = QQuantity()
qtbot.addWidget(w)
Expand Down
Loading