Improve UI for flight properties.

Use the new data from pydcs to improve the properties UI:

* Use human readable names
* Use appropriate control types
* Limit min and max values as appropriate for each property
* Show labels

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/3090.
This commit is contained in:
Dan Albert
2023-07-19 21:42:10 -07:00
committed by Raffson
parent a0fdfa11e2
commit bce6a170b8
8 changed files with 143 additions and 7 deletions

View File

@@ -0,0 +1,2 @@
class MissingPropertyDataError(RuntimeError):
...

View File

@@ -0,0 +1,21 @@
from PySide6.QtWidgets import QCheckBox
from dcs.unitpropertydescription import UnitPropertyDescription
from game.ato import Flight
from .missingpropertydataerror import MissingPropertyDataError
class PropertyCheckBox(QCheckBox):
def __init__(self, flight: Flight, prop: UnitPropertyDescription) -> None:
super().__init__()
self.flight = flight
self.prop = prop
if prop.default is None:
raise MissingPropertyDataError("default cannot be None")
self.setChecked(self.flight.props.get(self.prop.identifier, self.prop.default))
self.toggled.connect(self.on_toggle)
def on_toggle(self, checked: bool) -> None:
self.flight.props[self.prop.identifier] = checked

View File

@@ -0,0 +1,29 @@
from PySide6.QtWidgets import QComboBox
from dcs.unitpropertydescription import UnitPropertyDescription
from game.ato import Flight
from .missingpropertydataerror import MissingPropertyDataError
class PropertyComboBox(QComboBox):
def __init__(self, flight: Flight, prop: UnitPropertyDescription) -> None:
super().__init__()
self.flight = flight
self.prop = prop
if prop.values is None:
raise MissingPropertyDataError("values cannot be None")
if prop.default is None:
raise MissingPropertyDataError("default cannot be None")
current_value = self.flight.props.get(self.prop.identifier, self.prop.default)
for ident, text in self.prop.values.items():
self.addItem(text, ident)
if ident == current_value:
self.setCurrentText(text)
self.currentIndexChanged.connect(self.on_selection_changed)
def on_selection_changed(self, _index: int) -> None:
self.flight.props[self.prop.identifier] = self.currentData()

View File

@@ -1,7 +1,18 @@
import logging
from PySide2.QtWidgets import QGridLayout, QLabel
from dcs.unitpropertydescription import UnitPropertyDescription
from game.ato import Flight
from .propertyselector import PropertySelector
from .missingpropertydataerror import MissingPropertyDataError
from .propertycheckbox import PropertyCheckBox
from .propertycombobox import PropertyComboBox
from .propertyspinbox import PropertySpinBox
class UnhandledControlTypeError(RuntimeError):
def __init__(self, control: str) -> None:
super().__init__(f"Unhandled control type {control}")
class PropertyEditor(QGridLayout):
@@ -10,5 +21,47 @@ class PropertyEditor(QGridLayout):
self.flight = flight
for row, prop in enumerate(flight.unit_type.iter_props()):
self.addWidget(QLabel(prop.id), row, 0)
self.addWidget(PropertySelector(self.flight, prop), row, 1)
if prop.label is None:
if prop.control != "label":
logging.error(
"Found non-label aircraft property with no display name."
)
continue
if prop.player_only and not flight.client_count:
continue
try:
widget = self.control_for_property(prop)
except (MissingPropertyDataError, UnhandledControlTypeError):
logging.exception(
f"Cannot create property control for property %s of %s",
prop.identifier,
flight.unit_type,
)
continue
label = prop.label
if widget is None:
label = f"<strong>{label}</label>"
self.addWidget(QLabel(label), row, 0)
# If prop.control is "label", widget will be None. We only need to add the
# label, not the control.
if widget is not None:
self.addWidget(widget, row, 1)
def control_for_property(self, prop: UnitPropertyDescription) -> QWidget | None:
# Valid values are:
# "checkbox", "comboList", "groupbox", "label", "slider", "spinbox"
match prop.control:
case "checkbox":
return PropertyCheckBox(self.flight, prop)
case "comboList":
return PropertyComboBox(self.flight, prop)
case "groupbox" | "label":
return None
case "slider" | "spinbox":
return PropertySpinBox(self.flight, prop)
case _:
raise UnhandledControlTypeError(prop.control)

View File

@@ -0,0 +1,28 @@
from PySide6.QtWidgets import QSpinBox
from dcs.unitpropertydescription import UnitPropertyDescription
from game.ato import Flight
from .missingpropertydataerror import MissingPropertyDataError
class PropertySpinBox(QSpinBox):
def __init__(self, flight: Flight, prop: UnitPropertyDescription) -> None:
super().__init__()
self.flight = flight
self.prop = prop
if prop.minimum is None:
raise MissingPropertyDataError("minimum cannot be None")
if prop.maximum is None:
raise MissingPropertyDataError("maximum cannot be None")
if prop.default is None:
raise MissingPropertyDataError("default cannot be None")
self.setMinimum(prop.minimum)
self.setMaximum(prop.maximum)
self.setValue(self.flight.props.get(self.prop.identifier, self.prop.default))
self.valueChanged.connect(self.on_value_changed)
def on_value_changed(self, value: int) -> None:
self.flight.props[self.prop.identifier] = value