Configurable RF/TCN/ICLS/LINK4 with UI feedback

Resolves #70

Freq/Channel will turn orange when double booked.
Freq will turn red if GUARD freq was assigned.
This commit is contained in:
Raffson
2023-01-14 15:42:13 +01:00
parent ddb9d6968b
commit 88f984b0a8
37 changed files with 992 additions and 177 deletions

View File

@@ -0,0 +1,54 @@
from typing import Optional
from PySide2.QtCore import Qt
from PySide2.QtWidgets import (
QDialog,
QPushButton,
QLabel,
QHBoxLayout,
QSpinBox,
QLineEdit,
)
from game.radio.ICLSContainer import ICLSContainer
from qt_ui.uiconstants import EVENT_ICONS
class QICLSDialog(QDialog):
def __init__(self, parent=None, container: Optional[ICLSContainer] = None) -> None:
super().__init__(parent=parent)
self.container = container
self.setMinimumWidth(400)
# Make dialog modal to prevent background windows to close unexpectedly.
self.setModal(True)
self.setWindowTitle("Assign ICLS")
self.setWindowIcon(EVENT_ICONS["strike"])
layout = QHBoxLayout()
self.icls_label = QLabel("ICLS:")
self.icls_input = QSpinBox()
self.icls_input.setRange(1, 20)
self.icls_input.setSingleStep(1)
layout.addWidget(self.icls_label)
layout.addStretch()
layout.addWidget(self.icls_input)
self.callsign_input = QLineEdit()
self.callsign_input.setMaxLength(3)
self.callsign_input.setMaximumWidth(50)
layout.addWidget(self.callsign_input)
layout.addStretch()
self.create_button = QPushButton("Save")
self.create_button.clicked.connect(self.accept)
layout.addWidget(self.create_button, alignment=Qt.AlignRight)
self.setLayout(layout)
if container is not None:
if container.icls_name is not None:
self.callsign_input.setText(container.icls_name)
if container.icls_channel is not None:
self.icls_input.setValue(container.icls_channel)

View File

@@ -404,6 +404,7 @@ class QLiberationWindow(QMainWindow):
self.info_panel.setGame(game)
self.sim_controller.set_game(game)
self.game_model.set(self.game)
self.game_model.init_comms_registry()
except AttributeError:
logging.exception("Incompatible save game")
QMessageBox.critical(
@@ -530,6 +531,7 @@ class QLiberationWindow(QMainWindow):
logging.info("On Debriefing")
self.debriefing = QDebriefingWindow(debrief)
self.debriefing.show()
self.game_model.init_comms_registry()
def open_tgo_info_dialog(self, tgo: TheaterGroundObject) -> None:
QGroundObjectMenu(self, tgo, tgo.control_point, self.game).show()

View File

@@ -0,0 +1,60 @@
from typing import Optional
from PySide2.QtCore import Qt, QLocale
from PySide2.QtWidgets import (
QDialog,
QPushButton,
QLabel,
QHBoxLayout,
QDoubleSpinBox,
)
from game.radio.Link4Container import Link4Container
from game.radio.RadioFrequencyContainer import RadioFrequencyContainer
from game.radio.radios import RadioRange, kHz, MHz
from qt_ui.uiconstants import EVENT_ICONS
class QRadioFrequencyDialog(QDialog):
def __init__(
self,
parent=None,
container: Optional[RadioFrequencyContainer] = None,
range: RadioRange = RadioRange(MHz(225), MHz(400), kHz(25)),
link4: bool = False,
) -> None:
super().__init__(parent=parent)
self.container = container
self.setMinimumWidth(400)
# Make dialog modal to prevent background windows to close unexpectedly.
self.setModal(True)
self.setWindowTitle("Assign frequency")
self.setWindowIcon(EVENT_ICONS["strike"])
layout = QHBoxLayout()
self.frequency_label = QLabel("FREQ (MHz):")
self.frequency_input = QDoubleSpinBox()
self.frequency_input.setRange(
range.minimum.mhz, range.maximum.mhz - range.step.mhz
)
self.frequency_input.setSingleStep(range.step.mhz)
self.frequency_input.setDecimals(3)
self.frequency_input.setLocale(QLocale(QLocale.Language.English))
self.frequency_input.setValue(225.0)
layout.addWidget(self.frequency_label)
layout.addWidget(self.frequency_input)
self.create_button = QPushButton("Save")
self.create_button.clicked.connect(self.accept)
layout.addWidget(self.create_button, alignment=Qt.AlignRight)
self.setLayout(layout)
if link4 and isinstance(container, Link4Container):
if container.link4:
self.frequency_input.setValue(container.link4.mhz)
elif container.frequency:
self.frequency_input.setValue(container.frequency.mhz)

View File

@@ -0,0 +1,59 @@
from typing import Optional
from PySide2.QtCore import Qt
from PySide2.QtWidgets import (
QDialog,
QPushButton,
QLabel,
QHBoxLayout,
QSpinBox,
QComboBox,
QLineEdit,
)
from game.radio.TacanContainer import TacanContainer
from qt_ui.uiconstants import EVENT_ICONS
class QTacanDialog(QDialog):
def __init__(self, parent=None, container: Optional[TacanContainer] = None) -> None:
super().__init__(parent=parent)
self.container = container
self.setMinimumWidth(400)
# Make dialog modal to prevent background windows to close unexpectedly.
self.setModal(True)
self.setWindowTitle("Assign TACAN")
self.setWindowIcon(EVENT_ICONS["strike"])
layout = QHBoxLayout()
self.tacan_label = QLabel("TACAN:")
self.tacan_input = QSpinBox()
self.tacan_input.setRange(1, 126)
self.tacan_input.setSingleStep(1)
layout.addWidget(self.tacan_label)
layout.addStretch()
layout.addWidget(self.tacan_input)
self.band_input = QComboBox()
self.band_input.addItems(["X", "Y"])
layout.addWidget(self.band_input)
self.callsign_input = QLineEdit()
self.callsign_input.setMaxLength(3)
self.callsign_input.setMaximumWidth(50)
layout.addWidget(self.callsign_input)
layout.addStretch()
self.create_button = QPushButton("Save")
self.create_button.clicked.connect(self.accept)
layout.addWidget(self.create_button, alignment=Qt.AlignRight)
self.setLayout(layout)
if container is not None:
if container.tcn_name is not None:
self.callsign_input.setText(container.tcn_name)
if container.tacan is not None:
self.tacan_input.setValue(container.tacan.number)
self.band_input.setCurrentText(container.tacan.band.value)

View File

@@ -8,11 +8,15 @@ from PySide2.QtWidgets import (
QPushButton,
QVBoxLayout,
QWidget,
QGridLayout,
)
from game import Game
from game.ato.flighttype import FlightType
from game.config import RUNWAY_REPAIR_COST
from game.radio.ICLSContainer import ICLSContainer
from game.radio.RadioFrequencyContainer import RadioFrequencyContainer
from game.radio.TacanContainer import TacanContainer
from game.server import EventStream
from game.sim import GameUpdateEvents
from game.theater import (
@@ -20,10 +24,15 @@ from game.theater import (
ControlPoint,
ControlPointType,
FREE_FRONTLINE_UNIT_SUPPLY,
NavalControlPoint,
)
from qt_ui.dialogs import Dialog
from qt_ui.models import GameModel
from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.QFrequencyWidget import QFrequencyWidget
from qt_ui.widgets.QICLSWidget import QICLSWidget
from qt_ui.widgets.QLink4Widget import QLink4Widget
from qt_ui.widgets.QTacanWidget import QTacanWidget
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.basemenu.NewUnitTransferDialog import NewUnitTransferDialog
from qt_ui.windows.basemenu.QBaseMenuTabs import QBaseMenuTabs
@@ -60,13 +69,49 @@ class QBaseMenu2(QDialog):
pixmap = QPixmap(self.get_base_image())
header.setPixmap(pixmap)
cp_settings = QGridLayout()
top_layout.addLayout(cp_settings)
title = QLabel("<b>" + self.cp.name + "</b>")
title.setAlignment(Qt.AlignLeft | Qt.AlignTop)
title.setProperty("style", "base-title")
cp_settings.addWidget(title, 0, 0, 1, 2)
cp_settings.setHorizontalSpacing(20)
counter = 2
self.freq_widget = None
self.link4_widget = None
is_friendly = cp.is_friendly(True)
if is_friendly and isinstance(cp, RadioFrequencyContainer):
self.freq_widget = QFrequencyWidget(cp, self.game_model)
cp_settings.addWidget(self.freq_widget, counter // 2, counter % 2)
counter += 1
if is_friendly and isinstance(cp, TacanContainer):
self.tacan_widget = QTacanWidget(cp, self.game_model)
cp_settings.addWidget(self.tacan_widget, counter // 2, counter % 2)
counter += 1
if is_friendly and isinstance(cp, ICLSContainer):
self.icls_widget = QICLSWidget(cp, self.game_model)
cp_settings.addWidget(self.icls_widget, counter // 2, counter % 2)
counter += 1
if is_friendly and isinstance(cp, NavalControlPoint):
self.link4_widget = QLink4Widget(cp, self.game_model)
cp_settings.addWidget(self.link4_widget, counter // 2, counter % 2)
counter += 1
if self.freq_widget and self.link4_widget:
# link them so on change they check freq
self.freq_widget.freq_changed.connect(self.link4_widget.check_freq)
self.link4_widget.freq_changed.connect(self.freq_widget.check_freq)
self.intel_summary = QLabel()
self.intel_summary.setToolTip(self.generate_intel_tooltip())
self.update_intel_summary()
top_layout.addWidget(title)
top_layout.addWidget(self.intel_summary)
top_layout.setAlignment(Qt.AlignTop)
@@ -127,6 +172,7 @@ class QBaseMenu2(QDialog):
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
state = self.game_model.game.check_win_loss()
GameUpdateSignal.get_instance().gameStateChanged(state)
self.close()
@property
def has_transfer_destinations(self) -> bool:

View File

@@ -32,7 +32,7 @@ class QEditFlightDialog(QDialog):
layout = QVBoxLayout()
self.flight_planner = QFlightPlanner(package_model, flight, game_model.game)
self.flight_planner = QFlightPlanner(package_model, flight, game_model)
layout.addWidget(self.flight_planner)
self.setLayout(layout)

View File

@@ -26,9 +26,10 @@ from game.sim import GameUpdateEvents
from game.theater.missiontarget import MissionTarget
from qt_ui.models import AtoModel, GameModel, PackageModel
from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.QFrequencyWidget import QFrequencyWidget
from qt_ui.widgets.ato import QFlightList
from qt_ui.windows.QRadioFrequencyDialog import QRadioFrequencyDialog
from qt_ui.windows.mission.flight.QFlightCreator import QFlightCreator
from qt_ui.windows.mission.package.QPackageFrequency import QPackageFrequency
class QPackageDialog(QDialog):
@@ -70,22 +71,9 @@ class QPackageDialog(QDialog):
package_type_row.addWidget(self.package_type_text)
self.package_type_column.addLayout(package_type_row)
# TODO: make freq red if used by another package
package_freq_row = QHBoxLayout()
self.package_freq_label = QLabel("Package FREQ:")
freq = (
self.package_model.package.frequency.mhz
if self.package_model.package.frequency is not None
else ""
)
self.package_freq_text = QLabel(f"{freq}")
package_freq_row.addWidget(self.package_freq_label)
package_freq_row.addWidget(self.package_freq_text)
self.package_type_column.addLayout(package_freq_row)
self.summary_row.addStretch(1)
self.package_name_column = QVBoxLayout()
self.package_name_column = QHBoxLayout()
self.summary_row.addLayout(self.package_name_column)
self.package_name_label = QLabel("Package Name:")
self.package_name_label.setAlignment(Qt.AlignCenter)
@@ -145,16 +133,17 @@ class QPackageDialog(QDialog):
self.delete_flight_button.setEnabled(model.rowCount() > 0)
self.button_layout.addWidget(self.delete_flight_button)
self.open_radio_button = QPushButton("Set Package FREQ")
self.open_radio_button.clicked.connect(self.on_open_radio)
self.button_layout.addWidget(self.open_radio_button)
self.button_layout.addStretch()
self.package_model.tot_changed.connect(self.update_tot)
self.freq_widget = QFrequencyWidget(self.package_model.package, game_model)
self.button_layout.addWidget(self.freq_widget)
self.button_layout.addStretch()
self.setLayout(self.layout)
self.package_model.tot_changed.connect(self.update_tot)
self.accepted.connect(self.on_save)
self.finished.connect(self.on_close)
self.rejected.connect(self.on_cancel)
@@ -238,8 +227,8 @@ class QPackageDialog(QDialog):
self.package_model.package.custom_name = self.package_name_text.text()
def on_open_radio(self) -> None:
self.package_frequency_dialog = QPackageFrequency(
self.game, self.package_model.package, parent=self.window()
self.package_frequency_dialog = QRadioFrequencyDialog(
parent=self.window(), container=self.package_model.package
)
self.package_frequency_dialog.accepted.connect(self.assign_frequency)
self.package_frequency_dialog.show()
@@ -251,8 +240,11 @@ class QPackageDialog(QDialog):
def on_package_changed(self):
self.package_type_text.setText(self.package_model.description)
if (freq := self.package_model.package.frequency) is not None:
self.package_freq_text.setText(f"{freq.mhz} MHz")
self.freq_widget.check_freq()
def on_reset_radio(self):
self.package_model.package.frequency = None
self.package_freq_text.setText("AUTO")
class QNewPackageDialog(QPackageDialog):

View File

@@ -1,8 +1,7 @@
from PySide2.QtWidgets import QTabWidget
from game import Game
from game.ato.flight import Flight
from qt_ui.models import PackageModel
from qt_ui.models import PackageModel, GameModel
from qt_ui.windows.mission.flight.payload.QFlightPayloadTab import QFlightPayloadTab
from qt_ui.windows.mission.flight.settings.QGeneralFlightSettingsTab import (
QGeneralFlightSettingsTab,
@@ -11,14 +10,12 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWay
class QFlightPlanner(QTabWidget):
def __init__(self, package_model: PackageModel, flight: Flight, game: Game):
def __init__(self, package_model: PackageModel, flight: Flight, gm: GameModel):
super().__init__()
self.general_settings_tab = QGeneralFlightSettingsTab(
game, package_model, flight
)
self.payload_tab = QFlightPayloadTab(flight, game)
self.waypoint_tab = QFlightWaypointTab(game, package_model.package, flight)
self.general_settings_tab = QGeneralFlightSettingsTab(gm, package_model, flight)
self.payload_tab = QFlightPayloadTab(flight, gm.game)
self.waypoint_tab = QFlightWaypointTab(gm.game, package_model.package, flight)
self.waypoint_tab.loadout_changed.connect(self.payload_tab.reload_from_flight)
self.addTab(self.general_settings_tab, "General Flight settings")
self.addTab(self.payload_tab, "Payload")

View File

@@ -4,6 +4,8 @@ from PySide2.QtWidgets import (
QFrame,
QLabel,
QVBoxLayout,
QScrollArea,
QWidget,
)
from game import Game
@@ -42,7 +44,12 @@ class QFlightPayloadTab(QFrame):
docsText.setAlignment(Qt.AlignCenter)
docsText.setOpenExternalLinks(True)
layout.addLayout(PropertyEditor(self.flight))
self.scroll_area = QScrollArea()
self.property_editor = QWidget()
self.property_editor.setLayout(PropertyEditor(self.flight))
self.scroll_area.setWidget(self.property_editor)
layout.addWidget(self.scroll_area)
self.loadout_selector = DcsLoadoutSelector(flight)
self.loadout_selector.currentIndexChanged.connect(self.on_new_loadout)
layout.addWidget(self.loadout_selector)

View File

@@ -30,7 +30,6 @@ class QLoadoutEditor(QGroupBox):
layout.addWidget(QPylonEditor(game, flight, pylon), i, 1)
vbox.addLayout(layout)
vbox.addStretch()
self.setLayout(vbox)
for i in self.findChildren(QPylonEditor):

View File

@@ -0,0 +1,25 @@
from PySide2.QtWidgets import QGroupBox, QVBoxLayout
from game.ato import Flight, FlightType
from qt_ui.models import GameModel
from qt_ui.widgets.QFrequencyWidget import QFrequencyWidget
from qt_ui.widgets.QTacanWidget import QTacanWidget
class QCommsEditor(QGroupBox):
def __init__(self, flight: Flight, game: GameModel):
title = "Intra-Flight Frequency"
layout = QVBoxLayout()
is_refuel = flight.flight_type == FlightType.REFUELING
has_tacan = flight.unit_type.dcs_unit_type.tacan
layout.addWidget(QFrequencyWidget(flight, game))
if is_refuel and has_tacan:
layout.addWidget(QTacanWidget(flight, game))
title = title + " / TACAN"
super(QCommsEditor, self).__init__(title)
self.flight = flight
self.setLayout(layout)

View File

@@ -1,6 +1,6 @@
from typing import Optional
from PySide2.QtWidgets import QGroupBox, QVBoxLayout, QLineEdit, QLabel, QMessageBox
from PySide2.QtWidgets import QGroupBox, QHBoxLayout, QLineEdit, QLabel, QMessageBox
from game.ato.flight import Flight
@@ -10,7 +10,7 @@ class QFlightCustomName(QGroupBox):
self.flight = flight
self.layout = QVBoxLayout()
self.layout = QHBoxLayout()
self.custom_name_label = QLabel(f"Custom Name:")
self.custom_name_input = QLineEdit(flight.custom_name)
self.custom_name_input.textChanged.connect(self.on_change)

View File

@@ -3,10 +3,11 @@ from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout
from game import Game
from game.ato.flight import Flight
from qt_ui.models import PackageModel
from qt_ui.models import PackageModel, GameModel
from qt_ui.windows.mission.flight.settings.FlightAirfieldDisplay import (
FlightAirfieldDisplay,
)
from qt_ui.windows.mission.flight.settings.QCommsEditor import QCommsEditor
from qt_ui.windows.mission.flight.settings.QCustomName import QFlightCustomName
from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import QFlightSlotEditor
from qt_ui.windows.mission.flight.settings.QFlightStartType import QFlightStartType
@@ -18,16 +19,23 @@ from qt_ui.windows.mission.flight.settings.QFlightTypeTaskInfo import (
class QGeneralFlightSettingsTab(QFrame):
on_flight_settings_changed = Signal()
def __init__(self, game: Game, package_model: PackageModel, flight: Flight):
def __init__(self, game: GameModel, package_model: PackageModel, flight: Flight):
super().__init__()
widgets = [
QFlightTypeTaskInfo(flight),
QCommsEditor(flight, game),
FlightAirfieldDisplay(game.game, package_model, flight),
QFlightSlotEditor(package_model, flight, game.game),
QFlightStartType(package_model, flight),
QFlightCustomName(flight),
]
layout = QGridLayout()
layout.addWidget(QFlightTypeTaskInfo(flight), 0, 0)
layout.addWidget(FlightAirfieldDisplay(game, package_model, flight), 1, 0)
layout.addWidget(QFlightSlotEditor(package_model, flight, game), 2, 0)
layout.addWidget(QFlightStartType(package_model, flight), 3, 0)
layout.addWidget(QFlightCustomName(flight), 4, 0)
row = 0
for w in widgets:
layout.addWidget(w, row, 0)
row += 1
vstretch = QVBoxLayout()
vstretch.addStretch()
layout.addLayout(vstretch, 5, 0)
layout.addLayout(vstretch, row, 0)
self.setLayout(layout)

View File

@@ -1,47 +0,0 @@
from typing import Optional, Type
from PySide2.QtCore import Qt
from PySide2.QtWidgets import (
QDialog,
QPushButton,
QVBoxLayout,
QLabel,
QHBoxLayout,
QDoubleSpinBox,
)
from game import Game
from game.ato.package import Package
from game.radio.radios import RadioRegistry
from qt_ui.uiconstants import EVENT_ICONS
class QPackageFrequency(QDialog):
def __init__(self, game: Game, package: Package, parent=None) -> None:
super().__init__(parent=parent)
self.setMinimumWidth(400)
self.game = game
self.package = package
# Make dialog modal to prevent background windows to close unexpectedly.
self.setModal(True)
self.setWindowTitle("Assign frequency")
self.setWindowIcon(EVENT_ICONS["strike"])
layout = QHBoxLayout()
self.frequency_label = QLabel("FREQ (Mhz):")
self.frequency_input = QDoubleSpinBox()
self.frequency_input.setRange(225, 399.975)
self.frequency_input.setSingleStep(0.025)
self.frequency_input.setDecimals(3)
layout.addWidget(self.frequency_label)
layout.addWidget(self.frequency_input)
self.create_button = QPushButton("Save")
self.create_button.clicked.connect(self.accept)
layout.addWidget(self.create_button, alignment=Qt.AlignRight)
self.setLayout(layout)