mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Blacken.
This commit is contained in:
@@ -35,7 +35,9 @@ class QBudgetBox(QGroupBox):
|
||||
:param budget: Current money available
|
||||
:param reward: Planned reward for next turn
|
||||
"""
|
||||
self.money_amount.setText(str(round(budget,2)) + "M (+" + str(round(reward,2)) + "M)")
|
||||
self.money_amount.setText(
|
||||
str(round(budget, 2)) + "M (+" + str(round(reward, 2)) + "M)"
|
||||
)
|
||||
|
||||
def setGame(self, game):
|
||||
if game is None:
|
||||
@@ -47,4 +49,4 @@ class QBudgetBox(QGroupBox):
|
||||
|
||||
def openFinances(self):
|
||||
self.subwindow = QFinancesMenu(self.game)
|
||||
self.subwindow.show()
|
||||
self.subwindow.show()
|
||||
|
||||
@@ -18,10 +18,12 @@ class QTimeTurnWidget(QGroupBox):
|
||||
"""
|
||||
UI Component to display current turn and time info
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super(QTimeTurnWidget, self).__init__("Turn")
|
||||
self.setStyleSheet('padding: 0px; margin-left: 5px; margin-right: 0px; margin-top: 1ex; margin-bottom: 5px; border-right: 0px')
|
||||
self.setStyleSheet(
|
||||
"padding: 0px; margin-left: 5px; margin-right: 0px; margin-top: 1ex; margin-bottom: 5px; border-right: 0px"
|
||||
)
|
||||
|
||||
self.icons = {
|
||||
TimeOfDay.Dawn: CONST.ICONS["Dawn"],
|
||||
@@ -55,20 +57,21 @@ class QTimeTurnWidget(QGroupBox):
|
||||
"""
|
||||
self.daytime_icon.setPixmap(self.icons[conditions.time_of_day])
|
||||
self.date_display.setText(conditions.start_time.strftime("%d %b %Y"))
|
||||
self.time_display.setText(
|
||||
conditions.start_time.strftime("%H:%M:%S Local"))
|
||||
self.time_display.setText(conditions.start_time.strftime("%H:%M:%S Local"))
|
||||
self.setTitle(f"Turn {turn}")
|
||||
|
||||
|
||||
class QWeatherWidget(QGroupBox):
|
||||
"""
|
||||
UI Component to display current weather forecast
|
||||
"""
|
||||
|
||||
turn = None
|
||||
conditions = None
|
||||
|
||||
def __init__(self):
|
||||
super(QWeatherWidget, self).__init__("")
|
||||
self.setProperty('style', 'QWeatherWidget')
|
||||
self.setProperty("style", "QWeatherWidget")
|
||||
|
||||
self.icons = {
|
||||
TimeOfDay.Dawn: CONST.ICONS["Dawn"],
|
||||
@@ -83,17 +86,15 @@ class QWeatherWidget(QGroupBox):
|
||||
self.makeWeatherIcon()
|
||||
self.makeCloudRainFogWidget()
|
||||
self.makeWindsWidget()
|
||||
|
||||
|
||||
def makeWeatherIcon(self):
|
||||
"""Makes the Weather Icon Widget
|
||||
"""
|
||||
"""Makes the Weather Icon Widget"""
|
||||
self.weather_icon = QLabel()
|
||||
self.weather_icon.setPixmap(self.icons[TimeOfDay.Dawn])
|
||||
self.layout.addWidget(self.weather_icon)
|
||||
|
||||
def makeCloudRainFogWidget(self):
|
||||
"""Makes the Cloud, Rain, Fog Widget
|
||||
"""
|
||||
"""Makes the Cloud, Rain, Fog Widget"""
|
||||
self.textLayout = QVBoxLayout()
|
||||
self.layout.addLayout(self.textLayout)
|
||||
|
||||
@@ -107,43 +108,41 @@ class QWeatherWidget(QGroupBox):
|
||||
self.textLayout.addWidget(self.forecastFog)
|
||||
|
||||
def makeWindsWidget(self):
|
||||
"""Factory for the winds widget.
|
||||
"""
|
||||
"""Factory for the winds widget."""
|
||||
windsLayout = QGridLayout()
|
||||
self.layout.addLayout(windsLayout)
|
||||
|
||||
windsLayout.addWidget(self.makeIcon(CONST.ICONS['Weather_winds']), 0, 0, 3, 1)
|
||||
windsLayout.addWidget(self.makeIcon(CONST.ICONS["Weather_winds"]), 0, 0, 3, 1)
|
||||
|
||||
windsLayout.addWidget(self.makeLabel('At GL'), 0, 1)
|
||||
windsLayout.addWidget(self.makeLabel('At FL08'), 1, 1)
|
||||
windsLayout.addWidget(self.makeLabel('At FL26'), 2, 1)
|
||||
windsLayout.addWidget(self.makeLabel("At GL"), 0, 1)
|
||||
windsLayout.addWidget(self.makeLabel("At FL08"), 1, 1)
|
||||
windsLayout.addWidget(self.makeLabel("At FL26"), 2, 1)
|
||||
|
||||
self.windGLSpeedLabel = self.makeLabel('0kts')
|
||||
self.windGLDirLabel = self.makeLabel('0º')
|
||||
self.windGLSpeedLabel = self.makeLabel("0kts")
|
||||
self.windGLDirLabel = self.makeLabel("0º")
|
||||
windsLayout.addWidget(self.windGLSpeedLabel, 0, 2)
|
||||
windsLayout.addWidget(self.windGLDirLabel, 0, 3)
|
||||
|
||||
|
||||
self.windFL08SpeedLabel = self.makeLabel('0kts')
|
||||
self.windFL08DirLabel = self.makeLabel('0º')
|
||||
self.windFL08SpeedLabel = self.makeLabel("0kts")
|
||||
self.windFL08DirLabel = self.makeLabel("0º")
|
||||
windsLayout.addWidget(self.windFL08SpeedLabel, 1, 2)
|
||||
windsLayout.addWidget(self.windFL08DirLabel, 1, 3)
|
||||
|
||||
self.windFL26SpeedLabel = self.makeLabel('0kts')
|
||||
self.windFL26DirLabel = self.makeLabel('0º')
|
||||
self.windFL26SpeedLabel = self.makeLabel("0kts")
|
||||
self.windFL26DirLabel = self.makeLabel("0º")
|
||||
windsLayout.addWidget(self.windFL26SpeedLabel, 2, 2)
|
||||
windsLayout.addWidget(self.windFL26DirLabel, 2, 3)
|
||||
|
||||
def makeLabel(self, text: str = '') -> QLabel:
|
||||
def makeLabel(self, text: str = "") -> QLabel:
|
||||
"""Shorthand to generate a QLabel with widget standard style
|
||||
|
||||
:arg pixmap QPixmap for the icon.
|
||||
"""
|
||||
label = QLabel(text)
|
||||
label.setProperty('style', 'text-sm')
|
||||
label.setProperty("style", "text-sm")
|
||||
|
||||
return label
|
||||
|
||||
|
||||
def makeIcon(self, pixmap: QPixmap) -> QLabel:
|
||||
"""Shorthand to generate a QIcon with pixmap.
|
||||
|
||||
@@ -167,26 +166,28 @@ class QWeatherWidget(QGroupBox):
|
||||
self.updateWinds()
|
||||
|
||||
def updateWinds(self):
|
||||
"""Updates the UI with the current conditions wind info.
|
||||
"""
|
||||
"""Updates the UI with the current conditions wind info."""
|
||||
windGlSpeed = mps(self.conditions.weather.wind.at_0m.speed or 0)
|
||||
windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, '0')
|
||||
self.windGLSpeedLabel.setText(f'{int(windGlSpeed.knots)}kts')
|
||||
self.windGLDirLabel.setText(f'{windGlDir}º')
|
||||
windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, "0")
|
||||
self.windGLSpeedLabel.setText(f"{int(windGlSpeed.knots)}kts")
|
||||
self.windGLDirLabel.setText(f"{windGlDir}º")
|
||||
|
||||
windFL08Speed = mps(self.conditions.weather.wind.at_2000m.speed or 0)
|
||||
windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(3, '0')
|
||||
self.windFL08SpeedLabel.setText(f'{int(windFL08Speed.knots)}kts')
|
||||
self.windFL08DirLabel.setText(f'{windFL08Dir}º')
|
||||
windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(
|
||||
3, "0"
|
||||
)
|
||||
self.windFL08SpeedLabel.setText(f"{int(windFL08Speed.knots)}kts")
|
||||
self.windFL08DirLabel.setText(f"{windFL08Dir}º")
|
||||
|
||||
windFL26Speed = mps(self.conditions.weather.wind.at_8000m.speed or 0)
|
||||
windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(3, '0')
|
||||
self.windFL26SpeedLabel.setText(f'{int(windFL26Speed.knots)}kts')
|
||||
self.windFL26DirLabel.setText(f'{windFL26Dir}º')
|
||||
windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(
|
||||
3, "0"
|
||||
)
|
||||
self.windFL26SpeedLabel.setText(f"{int(windFL26Speed.knots)}kts")
|
||||
self.windFL26DirLabel.setText(f"{windFL26Dir}º")
|
||||
|
||||
def updateForecast(self):
|
||||
"""Updates the Forecast Text and icon with the current conditions wind info.
|
||||
"""
|
||||
"""Updates the Forecast Text and icon with the current conditions wind info."""
|
||||
icon = []
|
||||
if self.conditions.weather.clouds is None:
|
||||
cloudDensity = 0
|
||||
@@ -197,44 +198,44 @@ class QWeatherWidget(QGroupBox):
|
||||
|
||||
fog = self.conditions.weather.fog or None
|
||||
is_night = self.conditions.time_of_day == TimeOfDay.Night
|
||||
time = 'night' if is_night else 'day'
|
||||
time = "night" if is_night else "day"
|
||||
|
||||
if cloudDensity <= 0:
|
||||
self.forecastClouds.setText('Sunny')
|
||||
icon = [time, 'clear']
|
||||
|
||||
self.forecastClouds.setText("Sunny")
|
||||
icon = [time, "clear"]
|
||||
|
||||
if cloudDensity > 0 and cloudDensity < 3:
|
||||
self.forecastClouds.setText('Partly Cloudy')
|
||||
icon = [time, 'partly-cloudy']
|
||||
self.forecastClouds.setText("Partly Cloudy")
|
||||
icon = [time, "partly-cloudy"]
|
||||
|
||||
if cloudDensity >= 3 and cloudDensity < 5:
|
||||
self.forecastClouds.setText('Mostly Cloudy')
|
||||
icon = [time, 'partly-cloudy']
|
||||
self.forecastClouds.setText("Mostly Cloudy")
|
||||
icon = [time, "partly-cloudy"]
|
||||
|
||||
if cloudDensity >= 5:
|
||||
self.forecastClouds.setText('Totally Cloudy')
|
||||
icon = [time, 'partly-cloudy']
|
||||
self.forecastClouds.setText("Totally Cloudy")
|
||||
icon = [time, "partly-cloudy"]
|
||||
|
||||
if precipitation == PydcsWeather.Preceptions.Rain:
|
||||
self.forecastRain.setText('Rain')
|
||||
icon = [time, 'rain']
|
||||
self.forecastRain.setText("Rain")
|
||||
icon = [time, "rain"]
|
||||
|
||||
elif precipitation == PydcsWeather.Preceptions.Thunderstorm:
|
||||
self.forecastRain.setText('Thunderstorm')
|
||||
icon = [time, 'thunderstorm']
|
||||
|
||||
self.forecastRain.setText("Thunderstorm")
|
||||
icon = [time, "thunderstorm"]
|
||||
|
||||
else:
|
||||
self.forecastRain.setText('No Rain')
|
||||
self.forecastRain.setText("No Rain")
|
||||
|
||||
if not fog:
|
||||
self.forecastFog.setText('No fog')
|
||||
else:
|
||||
self.forecastFog.setText("No fog")
|
||||
else:
|
||||
visibility = round(fog.visibility.nautical_miles, 1)
|
||||
self.forecastFog.setText(f'Fog vis: {visibility}nm')
|
||||
icon = [time, ('cloudy' if cloudDensity > 1 else None), 'fog']
|
||||
self.forecastFog.setText(f"Fog vis: {visibility}nm")
|
||||
icon = [time, ("cloudy" if cloudDensity > 1 else None), "fog"]
|
||||
|
||||
icon_key = "Weather_{}".format('-'.join(filter(None.__ne__, icon)))
|
||||
icon = CONST.ICONS.get(icon_key) or CONST.ICONS['Weather_night-partly-cloudy']
|
||||
icon_key = "Weather_{}".format("-".join(filter(None.__ne__, icon)))
|
||||
icon = CONST.ICONS.get(icon_key) or CONST.ICONS["Weather_night-partly-cloudy"]
|
||||
self.weather_icon.setPixmap(icon)
|
||||
|
||||
|
||||
@@ -245,7 +246,7 @@ class QConditionsWidget(QFrame):
|
||||
|
||||
def __init__(self):
|
||||
super(QConditionsWidget, self).__init__()
|
||||
self.setProperty('style', 'QConditionsWidget')
|
||||
self.setProperty("style", "QConditionsWidget")
|
||||
|
||||
self.layout = QGridLayout()
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
@@ -254,11 +255,13 @@ class QConditionsWidget(QFrame):
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.time_turn_widget = QTimeTurnWidget()
|
||||
self.time_turn_widget.setStyleSheet('QGroupBox { margin-right: 0px; }')
|
||||
self.time_turn_widget.setStyleSheet("QGroupBox { margin-right: 0px; }")
|
||||
self.layout.addWidget(self.time_turn_widget, 0, 0)
|
||||
|
||||
self.weather_widget = QWeatherWidget()
|
||||
self.weather_widget.setStyleSheet('QGroupBox { margin-top: 5px; margin-left: 0px; border-left: 0px; }')
|
||||
self.weather_widget.setStyleSheet(
|
||||
"QGroupBox { margin-top: 5px; margin-left: 0px; border-left: 0px; }"
|
||||
)
|
||||
self.weather_widget.hide()
|
||||
self.layout.addWidget(self.weather_widget, 0, 1)
|
||||
|
||||
@@ -271,4 +274,3 @@ class QConditionsWidget(QFrame):
|
||||
self.time_turn_widget.setCurrentTurn(turn, conditions)
|
||||
self.weather_widget.setCurrentTurn(turn, conditions)
|
||||
self.weather_widget.show()
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ class QDebriefingInformation(QFrame):
|
||||
|
||||
def __init__(self):
|
||||
super(QDebriefingInformation, self).__init__()
|
||||
self.init_ui()
|
||||
self.init_ui()
|
||||
|
||||
@@ -16,10 +16,10 @@ class QFactionsInfos(QGroupBox):
|
||||
|
||||
self.layout = QGridLayout()
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.addWidget(QLabel("<b>Player : </b>"),0,0)
|
||||
self.layout.addWidget(self.player_name,0,1)
|
||||
self.layout.addWidget(QLabel("<b>Enemy : </b>"),1,0)
|
||||
self.layout.addWidget(self.enemy_name,1,1)
|
||||
self.layout.addWidget(QLabel("<b>Player : </b>"), 0, 0)
|
||||
self.layout.addWidget(self.player_name, 0, 1)
|
||||
self.layout.addWidget(QLabel("<b>Enemy : </b>"), 1, 0)
|
||||
self.layout.addWidget(self.enemy_name, 1, 1)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def setGame(self, game: Game):
|
||||
@@ -29,4 +29,3 @@ class QFactionsInfos(QGroupBox):
|
||||
else:
|
||||
self.player_name.setText("")
|
||||
self.enemy_name.setText("")
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ from PySide2.QtWidgets import QSpinBox
|
||||
class QFlightSizeSpinner(QSpinBox):
|
||||
"""Spin box for selecting the number of aircraft in a flight."""
|
||||
|
||||
def __init__(self, min_size: int = 1, max_size: int = 4,
|
||||
default_size: int = 2) -> None:
|
||||
def __init__(
|
||||
self, min_size: int = 1, max_size: int = 4, default_size: int = 2
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.setMinimum(min_size)
|
||||
self.setMaximum(max_size)
|
||||
|
||||
@@ -94,12 +94,16 @@ class QIntelBox(QGroupBox):
|
||||
|
||||
data = self.game.game_stats.data_per_turn[-1]
|
||||
|
||||
self.air_strength.setText(self.forces_strength_text(
|
||||
data.allied_units.aircraft_count,
|
||||
data.enemy_units.aircraft_count))
|
||||
self.ground_strength.setText(self.forces_strength_text(
|
||||
data.allied_units.vehicles_count,
|
||||
data.enemy_units.vehicles_count))
|
||||
self.air_strength.setText(
|
||||
self.forces_strength_text(
|
||||
data.allied_units.aircraft_count, data.enemy_units.aircraft_count
|
||||
)
|
||||
)
|
||||
self.ground_strength.setText(
|
||||
self.forces_strength_text(
|
||||
data.allied_units.vehicles_count, data.enemy_units.vehicles_count
|
||||
)
|
||||
)
|
||||
self.economic_strength.setText(self.economic_strength_text())
|
||||
|
||||
def open_details_window(self) -> None:
|
||||
|
||||
@@ -12,8 +12,9 @@ class QLabeledWidget(QHBoxLayout):
|
||||
label is used to name the input.
|
||||
"""
|
||||
|
||||
def __init__(self, text: str, widget: QWidget,
|
||||
tooltip: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self, text: str, widget: QWidget, tooltip: Optional[str] = None
|
||||
) -> None:
|
||||
super().__init__()
|
||||
label = QLabel(text)
|
||||
self.addWidget(label)
|
||||
|
||||
@@ -22,15 +22,13 @@ from qt_ui.widgets.QFactionsInfos import QFactionsInfos
|
||||
from qt_ui.widgets.QIntelBox import QIntelBox
|
||||
from qt_ui.widgets.clientslots import MaxPlayerCount
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
from qt_ui.windows.QWaitingForMissionResultWindow import \
|
||||
QWaitingForMissionResultWindow
|
||||
from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow
|
||||
from qt_ui.windows.settings.QSettingsWindow import QSettingsWindow
|
||||
from qt_ui.windows.stats.QStatsWindow import QStatsWindow
|
||||
from qt_ui.widgets.QConditionsWidget import QConditionsWidget
|
||||
|
||||
|
||||
class QTopPanel(QFrame):
|
||||
|
||||
def __init__(self, game_model: GameModel):
|
||||
super(QTopPanel, self).__init__()
|
||||
self.game_model = game_model
|
||||
@@ -43,7 +41,7 @@ class QTopPanel(QFrame):
|
||||
def game(self) -> Optional[Game]:
|
||||
return self.game_model.game
|
||||
|
||||
def init_ui(self):
|
||||
def init_ui(self):
|
||||
self.conditionsWidget = QConditionsWidget()
|
||||
self.budgetBox = QBudgetBox(self.game)
|
||||
|
||||
@@ -85,14 +83,13 @@ class QTopPanel(QFrame):
|
||||
|
||||
self.proceedBox = QGroupBox("Proceed")
|
||||
self.proceedBoxLayout = QHBoxLayout()
|
||||
self.proceedBoxLayout.addLayout(
|
||||
MaxPlayerCount(self.game_model.ato_model))
|
||||
self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model))
|
||||
self.proceedBoxLayout.addWidget(self.passTurnButton)
|
||||
self.proceedBoxLayout.addWidget(self.proceedButton)
|
||||
self.proceedBox.setLayout(self.proceedBoxLayout)
|
||||
|
||||
self.layout = QHBoxLayout()
|
||||
|
||||
|
||||
self.layout.addWidget(self.factionsInfos)
|
||||
self.layout.addWidget(self.conditionsWidget)
|
||||
self.layout.addWidget(self.budgetBox)
|
||||
@@ -101,8 +98,8 @@ class QTopPanel(QFrame):
|
||||
self.layout.addStretch(1)
|
||||
self.layout.addWidget(self.proceedBox)
|
||||
|
||||
self.layout.setContentsMargins(0,0,0,0)
|
||||
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def setGame(self, game: Optional[Game]):
|
||||
@@ -169,48 +166,50 @@ class QTopPanel(QFrame):
|
||||
result = QMessageBox.question(
|
||||
self,
|
||||
"Continue without client slots?",
|
||||
("No client slots have been created for players. Continuing will "
|
||||
"allow the AI to perform the mission, but players will be unable "
|
||||
"to participate.<br />"
|
||||
"<br />"
|
||||
"To add client slots for players, select a package from the "
|
||||
"Packages panel on the left of the main window, and then a flight "
|
||||
"from the Flights panel below the Packages panel. The edit button "
|
||||
"below the Flights panel will allow you to edit the number of "
|
||||
"client slots in the flight. Each client slot allows one player.<br />"
|
||||
"<br />Click 'Yes' to continue with an AI only mission"
|
||||
"<br />Click 'No' if you'd like to make more changes."),
|
||||
(
|
||||
"No client slots have been created for players. Continuing will "
|
||||
"allow the AI to perform the mission, but players will be unable "
|
||||
"to participate.<br />"
|
||||
"<br />"
|
||||
"To add client slots for players, select a package from the "
|
||||
"Packages panel on the left of the main window, and then a flight "
|
||||
"from the Flights panel below the Packages panel. The edit button "
|
||||
"below the Flights panel will allow you to edit the number of "
|
||||
"client slots in the flight. Each client slot allows one player.<br />"
|
||||
"<br />Click 'Yes' to continue with an AI only mission"
|
||||
"<br />Click 'No' if you'd like to make more changes."
|
||||
),
|
||||
QMessageBox.No,
|
||||
QMessageBox.Yes
|
||||
QMessageBox.Yes,
|
||||
)
|
||||
return result == QMessageBox.Yes
|
||||
|
||||
def confirm_negative_start_time(self,
|
||||
negative_starts: List[Package]) -> bool:
|
||||
formatted = '<br />'.join(
|
||||
def confirm_negative_start_time(self, negative_starts: List[Package]) -> bool:
|
||||
formatted = "<br />".join(
|
||||
[f"{p.primary_task} {p.target.name}" for p in negative_starts]
|
||||
)
|
||||
mbox = QMessageBox(
|
||||
QMessageBox.Question,
|
||||
"Continue with past start times?",
|
||||
("Some flights in the following packages have start times set "
|
||||
"earlier than mission start time:<br />"
|
||||
"<br />"
|
||||
f"{formatted}<br />"
|
||||
"<br />"
|
||||
"Flight start times are estimated based on the package TOT, so it "
|
||||
"is possible that not all flights will be able to reach the "
|
||||
"target area at their assigned times.<br />"
|
||||
"<br />"
|
||||
"You can either continue with the mission as planned, with the "
|
||||
"misplanned flights potentially flying too fast and/or missing "
|
||||
"their rendezvous; automatically fix negative TOTs; or cancel "
|
||||
"mission start and fix the packages manually."),
|
||||
parent=self
|
||||
(
|
||||
"Some flights in the following packages have start times set "
|
||||
"earlier than mission start time:<br />"
|
||||
"<br />"
|
||||
f"{formatted}<br />"
|
||||
"<br />"
|
||||
"Flight start times are estimated based on the package TOT, so it "
|
||||
"is possible that not all flights will be able to reach the "
|
||||
"target area at their assigned times.<br />"
|
||||
"<br />"
|
||||
"You can either continue with the mission as planned, with the "
|
||||
"misplanned flights potentially flying too fast and/or missing "
|
||||
"their rendezvous; automatically fix negative TOTs; or cancel "
|
||||
"mission start and fix the packages manually."
|
||||
),
|
||||
parent=self,
|
||||
)
|
||||
auto = mbox.addButton("Fix TOTs automatically", QMessageBox.ActionRole)
|
||||
ignore = mbox.addButton("Continue without fixing",
|
||||
QMessageBox.DestructiveRole)
|
||||
ignore = mbox.addButton("Continue without fixing", QMessageBox.DestructiveRole)
|
||||
cancel = mbox.addButton(QMessageBox.Cancel)
|
||||
mbox.setEscapeButton(cancel)
|
||||
mbox.exec_()
|
||||
@@ -238,12 +237,12 @@ class QTopPanel(QFrame):
|
||||
closest_cps[1],
|
||||
self.game.theater.controlpoints[0].position,
|
||||
self.game.player_name,
|
||||
self.game.enemy_name)
|
||||
self.game.enemy_name,
|
||||
)
|
||||
|
||||
unit_map = self.game.initiate_event(game_event)
|
||||
waiting = QWaitingForMissionResultWindow(game_event, self.game,
|
||||
unit_map)
|
||||
waiting = QWaitingForMissionResultWindow(game_event, self.game, unit_map)
|
||||
waiting.show()
|
||||
|
||||
def budget_update(self, game:Game):
|
||||
def budget_update(self, game: Game):
|
||||
self.budgetBox.setGame(game)
|
||||
|
||||
@@ -71,34 +71,38 @@ class FlightDelegate(QStyledItemDelegate):
|
||||
return f"From {origin} to {flight.arrival.name}"
|
||||
return f"From {origin}"
|
||||
|
||||
def paint(self, painter: QPainter, option: QStyleOptionViewItem,
|
||||
index: QModelIndex) -> None:
|
||||
def paint(
|
||||
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
|
||||
) -> None:
|
||||
# Draw the list item with all the default selection styling, but with an
|
||||
# invalid index so text formatting is left to us.
|
||||
super().paint(painter, option, QModelIndex())
|
||||
|
||||
rect = option.rect.adjusted(self.HMARGIN, self.VMARGIN, -self.HMARGIN,
|
||||
-self.VMARGIN)
|
||||
rect = option.rect.adjusted(
|
||||
self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN
|
||||
)
|
||||
|
||||
with painter_context(painter):
|
||||
painter.setFont(self.get_font(option))
|
||||
|
||||
icon: Optional[QIcon] = index.data(Qt.DecorationRole)
|
||||
if icon is not None:
|
||||
icon.paint(painter, rect, Qt.AlignLeft | Qt.AlignVCenter,
|
||||
self.icon_mode(option),
|
||||
self.icon_state(option))
|
||||
icon.paint(
|
||||
painter,
|
||||
rect,
|
||||
Qt.AlignLeft | Qt.AlignVCenter,
|
||||
self.icon_mode(option),
|
||||
self.icon_state(option),
|
||||
)
|
||||
|
||||
rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN,
|
||||
0, 0, 0)
|
||||
rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN, 0, 0, 0)
|
||||
painter.drawText(rect, Qt.AlignLeft, self.first_row_text(index))
|
||||
line2 = rect.adjusted(0, rect.height() / 2, 0, rect.height() / 2)
|
||||
painter.drawText(line2, Qt.AlignLeft, self.second_row_text(index))
|
||||
|
||||
clients = self.num_clients(index)
|
||||
if clients:
|
||||
painter.drawText(rect, Qt.AlignRight,
|
||||
f"Player Slots: {clients}")
|
||||
painter.drawText(rect, Qt.AlignRight, f"Player Slots: {clients}")
|
||||
|
||||
def num_clients(self, index: QModelIndex) -> int:
|
||||
flight = self.flight(index)
|
||||
@@ -126,22 +130,24 @@ class FlightDelegate(QStyledItemDelegate):
|
||||
else:
|
||||
return icon_size
|
||||
|
||||
def sizeHint(self, option: QStyleOptionViewItem,
|
||||
index: QModelIndex) -> QSize:
|
||||
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
|
||||
left = self.icon_size(option).width() + self.HMARGIN
|
||||
metrics = QFontMetrics(self.get_font(option))
|
||||
first = metrics.size(0, self.first_row_text(index))
|
||||
second = metrics.size(0, self.second_row_text(index))
|
||||
text_width = max(first.width(), second.width())
|
||||
return QSize(left + text_width + 2 * self.HMARGIN,
|
||||
first.height() + second.height() + 2 * self.VMARGIN)
|
||||
return QSize(
|
||||
left + text_width + 2 * self.HMARGIN,
|
||||
first.height() + second.height() + 2 * self.VMARGIN,
|
||||
)
|
||||
|
||||
|
||||
class QFlightList(QListView):
|
||||
"""List view for displaying the flights of a package."""
|
||||
|
||||
def __init__(self, game_model: GameModel,
|
||||
package_model: Optional[PackageModel]) -> None:
|
||||
def __init__(
|
||||
self, game_model: GameModel, package_model: Optional[PackageModel]
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.game_model = game_model
|
||||
self.package_model = package_model
|
||||
@@ -163,8 +169,7 @@ class QFlightList(QListView):
|
||||
# noinspection PyUnresolvedReferences
|
||||
model.deleted.connect(self.disconnect_model)
|
||||
self.selectionModel().setCurrentIndex(
|
||||
model.index(0, 0, QModelIndex()),
|
||||
QItemSelectionModel.Select
|
||||
model.index(0, 0, QModelIndex()), QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
def disconnect_model(self) -> None:
|
||||
@@ -192,14 +197,15 @@ class QFlightList(QListView):
|
||||
|
||||
def edit_flight(self, index: QModelIndex) -> None:
|
||||
from qt_ui.dialogs import Dialog
|
||||
|
||||
Dialog.open_edit_flight_dialog(
|
||||
self.package_model, self.package_model.flight_at_index(index),
|
||||
parent=self.window()
|
||||
self.package_model,
|
||||
self.package_model.flight_at_index(index),
|
||||
parent=self.window(),
|
||||
)
|
||||
|
||||
def delete_flight(self, index: QModelIndex) -> None:
|
||||
self.game_model.game.aircraft_inventory.return_from_flight(
|
||||
self.selected_item)
|
||||
self.game_model.game.aircraft_inventory.return_from_flight(self.selected_item)
|
||||
self.package_model.delete_flight_at_index(index)
|
||||
GameUpdateSignal.get_instance().redraw_flight_paths()
|
||||
|
||||
@@ -226,8 +232,9 @@ class QFlightPanel(QGroupBox):
|
||||
delete buttons for flight management.
|
||||
"""
|
||||
|
||||
def __init__(self, game_model: GameModel,
|
||||
package_model: Optional[PackageModel] = None) -> None:
|
||||
def __init__(
|
||||
self, game_model: GameModel, package_model: Optional[PackageModel] = None
|
||||
) -> None:
|
||||
super().__init__("Flights")
|
||||
self.game_model = game_model
|
||||
self.package_model = package_model
|
||||
@@ -336,14 +343,16 @@ class PackageDelegate(QStyledItemDelegate):
|
||||
package = self.package(index)
|
||||
return f"TOT T+{package.time_over_target}"
|
||||
|
||||
def paint(self, painter: QPainter, option: QStyleOptionViewItem,
|
||||
index: QModelIndex) -> None:
|
||||
def paint(
|
||||
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
|
||||
) -> None:
|
||||
# Draw the list item with all the default selection styling, but with an
|
||||
# invalid index so text formatting is left to us.
|
||||
super().paint(painter, option, QModelIndex())
|
||||
|
||||
rect = option.rect.adjusted(self.HMARGIN, self.VMARGIN, -self.HMARGIN,
|
||||
-self.VMARGIN)
|
||||
rect = option.rect.adjusted(
|
||||
self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN
|
||||
)
|
||||
|
||||
with painter_context(painter):
|
||||
painter.setFont(self.get_font(option))
|
||||
@@ -354,20 +363,20 @@ class PackageDelegate(QStyledItemDelegate):
|
||||
|
||||
clients = self.num_clients(index)
|
||||
if clients:
|
||||
painter.drawText(rect, Qt.AlignRight,
|
||||
f"Player Slots: {clients}")
|
||||
painter.drawText(rect, Qt.AlignRight, f"Player Slots: {clients}")
|
||||
|
||||
def num_clients(self, index: QModelIndex) -> int:
|
||||
package = self.package(index)
|
||||
return sum(f.client_count for f in package.flights)
|
||||
|
||||
def sizeHint(self, option: QStyleOptionViewItem,
|
||||
index: QModelIndex) -> QSize:
|
||||
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
|
||||
metrics = QFontMetrics(self.get_font(option))
|
||||
left = metrics.size(0, self.left_text(index))
|
||||
right = metrics.size(0, self.right_text(index))
|
||||
return QSize(max(left.width(), right.width()) + 2 * self.HMARGIN,
|
||||
left.height() + right.height() + 2 * self.VMARGIN)
|
||||
return QSize(
|
||||
max(left.width(), right.width()) + 2 * self.HMARGIN,
|
||||
left.height() + right.height() + 2 * self.VMARGIN,
|
||||
)
|
||||
|
||||
|
||||
class QPackageList(QListView):
|
||||
@@ -393,19 +402,20 @@ class QPackageList(QListView):
|
||||
|
||||
def edit_package(self, index: QModelIndex) -> None:
|
||||
from qt_ui.dialogs import Dialog
|
||||
|
||||
Dialog.open_edit_package_dialog(self.ato_model.get_package_model(index))
|
||||
|
||||
def delete_package(self, index: QModelIndex) -> None:
|
||||
self.ato_model.delete_package_at_index(index)
|
||||
GameUpdateSignal.get_instance().redraw_flight_paths()
|
||||
|
||||
def on_new_packages(self, _parent: QModelIndex, first: int,
|
||||
_last: int) -> None:
|
||||
def on_new_packages(self, _parent: QModelIndex, first: int, _last: int) -> None:
|
||||
# Select the newly created pacakges. This should only ever happen due to
|
||||
# the player saving a new package, so selecting it helps them view/edit
|
||||
# it faster.
|
||||
self.selectionModel().setCurrentIndex(self.model().index(first, 0),
|
||||
QItemSelectionModel.Select)
|
||||
self.selectionModel().setCurrentIndex(
|
||||
self.model().index(first, 0), QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
def on_double_click(self, index: QModelIndex) -> None:
|
||||
if not index.isValid():
|
||||
@@ -533,8 +543,6 @@ class QAirTaskingOrderPanel(QSplitter):
|
||||
"""Sets the newly selected flight for display in the bottom panel."""
|
||||
index = self.package_panel.package_list.currentIndex()
|
||||
if index.isValid():
|
||||
self.flight_panel.set_package(
|
||||
self.ato_model.get_package_model(index)
|
||||
)
|
||||
self.flight_panel.set_package(self.ato_model.get_package_model(index))
|
||||
else:
|
||||
self.flight_panel.set_package(None)
|
||||
|
||||
@@ -11,9 +11,12 @@ class MaxPlayerCount(QLabeledWidget):
|
||||
self.slots_label = QLabel(str(self.count_client_slots))
|
||||
self.ato_model.client_slots_changed.connect(self.update_count)
|
||||
super().__init__(
|
||||
"Max Players:", self.slots_label,
|
||||
("Total number of client slots. To add client slots, edit a flight "
|
||||
"using the panel on the left.")
|
||||
"Max Players:",
|
||||
self.slots_label,
|
||||
(
|
||||
"Total number of client slots. To add client slots, edit a flight "
|
||||
"using the panel on the left."
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -11,10 +11,16 @@ import gen.flights.ai_flight_planner_db
|
||||
|
||||
from game import Game, db
|
||||
|
||||
|
||||
class QAircraftTypeSelector(QComboBox):
|
||||
"""Combo box for selecting among the given aircraft types."""
|
||||
|
||||
def __init__(self, aircraft_types: Iterable[Type[FlyingType]], country: str, mission_type: str) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
aircraft_types: Iterable[Type[FlyingType]],
|
||||
country: str,
|
||||
mission_type: str,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.model().sort(0)
|
||||
@@ -26,32 +32,72 @@ class QAircraftTypeSelector(QComboBox):
|
||||
current_aircraft = self.currentData()
|
||||
self.clear()
|
||||
for aircraft in aircraft_types:
|
||||
if mission_type in [FlightType.BARCAP, FlightType.ESCORT, FlightType.INTERCEPTION, FlightType.SWEEP, FlightType.TARCAP]:
|
||||
if mission_type in [
|
||||
FlightType.BARCAP,
|
||||
FlightType.ESCORT,
|
||||
FlightType.INTERCEPTION,
|
||||
FlightType.SWEEP,
|
||||
FlightType.TARCAP,
|
||||
]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.CAP_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
elif mission_type in [FlightType.CAS, FlightType.BAI, FlightType.OCA_AIRCRAFT]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.CAS_CAPABLE or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [
|
||||
FlightType.CAS,
|
||||
FlightType.BAI,
|
||||
FlightType.OCA_AIRCRAFT,
|
||||
]:
|
||||
if (
|
||||
aircraft in gen.flights.ai_flight_planner_db.CAS_CAPABLE
|
||||
or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE
|
||||
):
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [FlightType.SEAD]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.SEAD_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [FlightType.DEAD]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.DEAD_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [FlightType.STRIKE]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
if (
|
||||
aircraft in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE
|
||||
or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE
|
||||
):
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [FlightType.ANTISHIP]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.ANTISHIP_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [FlightType.OCA_RUNWAY]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
elif mission_type in [FlightType.AEWC]:
|
||||
if aircraft in gen.flights.ai_flight_planner_db.AEWC_CAPABLE:
|
||||
self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft)
|
||||
self.addItem(
|
||||
f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}",
|
||||
userData=aircraft,
|
||||
)
|
||||
current_aircraft_index = self.findData(current_aircraft)
|
||||
if current_aircraft_index != -1:
|
||||
self.setCurrentIndex(current_aircraft_index)
|
||||
if self.count() == 0:
|
||||
self.addItem("No capable aircraft available", userData=None)
|
||||
self.addItem("No capable aircraft available", userData=None)
|
||||
|
||||
@@ -14,8 +14,12 @@ class QArrivalAirfieldSelector(QComboBox):
|
||||
aircraft type is able to land at.
|
||||
"""
|
||||
|
||||
def __init__(self, destinations: Iterable[ControlPoint],
|
||||
aircraft: Type[FlyingType], optional_text: str) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
destinations: Iterable[ControlPoint],
|
||||
aircraft: Type[FlyingType],
|
||||
optional_text: str,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.destinations = list(destinations)
|
||||
self.aircraft = aircraft
|
||||
|
||||
@@ -3,9 +3,16 @@ from PySide2.QtWidgets import QComboBox, QCompleter
|
||||
|
||||
|
||||
class QFilteredComboBox(QComboBox):
|
||||
|
||||
def __init__(self, parent=None, include_targets=True, include_airbases=True,
|
||||
include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True):
|
||||
def __init__(
|
||||
self,
|
||||
parent=None,
|
||||
include_targets=True,
|
||||
include_airbases=True,
|
||||
include_frontlines=True,
|
||||
include_units=True,
|
||||
include_enemy=True,
|
||||
include_friendly=True,
|
||||
):
|
||||
super(QFilteredComboBox, self).__init__(parent)
|
||||
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
|
||||
@@ -18,9 +18,12 @@ class QOriginAirfieldSelector(QComboBox):
|
||||
|
||||
availability_changed = Signal(int)
|
||||
|
||||
def __init__(self, global_inventory: GlobalAircraftInventory,
|
||||
origins: Iterable[ControlPoint],
|
||||
aircraft: Type[FlyingType]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
global_inventory: GlobalAircraftInventory,
|
||||
origins: Iterable[ControlPoint],
|
||||
aircraft: Type[FlyingType],
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.global_inventory = global_inventory
|
||||
self.origins = list(origins)
|
||||
|
||||
@@ -9,9 +9,17 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
||||
|
||||
|
||||
class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
|
||||
def __init__(self, game: Game, parent=None, include_targets=True, include_airbases=True,
|
||||
include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True):
|
||||
def __init__(
|
||||
self,
|
||||
game: Game,
|
||||
parent=None,
|
||||
include_targets=True,
|
||||
include_airbases=True,
|
||||
include_frontlines=True,
|
||||
include_units=True,
|
||||
include_enemy=True,
|
||||
include_friendly=True,
|
||||
):
|
||||
super(QPredefinedWaypointSelectionComboBox, self).__init__(parent)
|
||||
self.game = game
|
||||
self.include_targets = include_targets
|
||||
@@ -34,7 +42,11 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
waypoints = [first_waypoint]
|
||||
if include_all_from_same_location:
|
||||
for w in self.wpts:
|
||||
if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name:
|
||||
if (
|
||||
w is not first_waypoint
|
||||
and w.obj_name
|
||||
and w.obj_name == first_waypoint.obj_name
|
||||
):
|
||||
waypoints.append(w)
|
||||
return waypoints
|
||||
|
||||
@@ -53,14 +65,19 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
if self.include_frontlines:
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.captured:
|
||||
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
|
||||
enemy_cp = [
|
||||
ecp
|
||||
for ecp in cp.connected_points
|
||||
if ecp.captured != cp.captured
|
||||
]
|
||||
for ecp in enemy_cp:
|
||||
pos = Conflict.frontline_position(cp, ecp, self.game.theater)[0]
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
pos.x,
|
||||
pos.y,
|
||||
Distance.from_meters(800))
|
||||
Distance.from_meters(800),
|
||||
)
|
||||
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
|
||||
wpt.alt_type = "RADIO"
|
||||
wpt.pretty_name = wpt.name
|
||||
@@ -69,14 +86,18 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
|
||||
if self.include_targets:
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
|
||||
if (self.include_enemy and not cp.captured) or (
|
||||
self.include_friendly and cp.captured
|
||||
):
|
||||
for ground_object in cp.ground_objects:
|
||||
if not ground_object.is_dead and isinstance(ground_object, BuildingGroundObject):
|
||||
if not ground_object.is_dead and isinstance(
|
||||
ground_object, BuildingGroundObject
|
||||
):
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
ground_object.position.x,
|
||||
ground_object.position.y,
|
||||
Distance.from_meters(0)
|
||||
Distance.from_meters(0),
|
||||
)
|
||||
wpt.alt_type = "RADIO"
|
||||
wpt.name = ground_object.waypoint_name
|
||||
@@ -91,19 +112,31 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
|
||||
if self.include_units:
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
|
||||
if (self.include_enemy and not cp.captured) or (
|
||||
self.include_friendly and cp.captured
|
||||
):
|
||||
for ground_object in cp.ground_objects:
|
||||
if not ground_object.is_dead and ground_object.dcs_identifier == "AA":
|
||||
if (
|
||||
not ground_object.is_dead
|
||||
and ground_object.dcs_identifier == "AA"
|
||||
):
|
||||
for g in ground_object.groups:
|
||||
for j, u in enumerate(g.units):
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
u.position.x,
|
||||
u.position.y,
|
||||
Distance.from_meters(0)
|
||||
Distance.from_meters(0),
|
||||
)
|
||||
wpt.alt_type = "RADIO"
|
||||
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
wpt.name = wpt.name = (
|
||||
"["
|
||||
+ str(ground_object.obj_name)
|
||||
+ "] : "
|
||||
+ u.type
|
||||
+ " #"
|
||||
+ str(j)
|
||||
)
|
||||
wpt.pretty_name = wpt.name
|
||||
wpt.targets.append(u)
|
||||
wpt.obj_name = ground_object.obj_name
|
||||
@@ -116,17 +149,21 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
|
||||
if self.include_airbases:
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
|
||||
if (self.include_enemy and not cp.captured) or (
|
||||
self.include_friendly and cp.captured
|
||||
):
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
cp.position.x,
|
||||
cp.position.y,
|
||||
Distance.from_meters(0)
|
||||
Distance.from_meters(0),
|
||||
)
|
||||
wpt.alt_type = "RADIO"
|
||||
wpt.name = cp.name
|
||||
if cp.captured:
|
||||
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
|
||||
wpt.description = (
|
||||
"Position of " + cp.name + " [Friendly Airbase]"
|
||||
)
|
||||
else:
|
||||
wpt.description = "Position of " + cp.name + " [Enemy Airbase]"
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
||||
|
||||
|
||||
class SEADTargetInfo:
|
||||
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.location = None
|
||||
@@ -15,8 +14,8 @@ class SEADTargetInfo:
|
||||
self.threat_range = 0
|
||||
self.detection_range = 0
|
||||
|
||||
class QSEADTargetSelectionComboBox(QFilteredComboBox):
|
||||
|
||||
class QSEADTargetSelectionComboBox(QFilteredComboBox):
|
||||
def __init__(self, game: Game, parent=None):
|
||||
super(QSEADTargetSelectionComboBox, self).__init__(parent)
|
||||
self.game = game
|
||||
@@ -41,7 +40,8 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
|
||||
return i + 1
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.captured: continue
|
||||
if cp.captured:
|
||||
continue
|
||||
for g in cp.ground_objects:
|
||||
|
||||
radars = []
|
||||
@@ -53,7 +53,10 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
|
||||
utype = db.unit_type_from_name(u.type)
|
||||
|
||||
if utype in UNITS_WITH_RADAR:
|
||||
if hasattr(utype, "detection_range") and utype.detection_range > 1000:
|
||||
if (
|
||||
hasattr(utype, "detection_range")
|
||||
and utype.detection_range > 1000
|
||||
):
|
||||
if utype.detection_range > detection_range:
|
||||
detection_range = utype.detection_range
|
||||
radars.append(u)
|
||||
@@ -63,9 +66,18 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
|
||||
threat_range = utype.threat_range
|
||||
if len(radars) > 0:
|
||||
tgt_info = SEADTargetInfo()
|
||||
tgt_info.name = g.obj_name + " [" + ",".join([db.unit_type_from_name(u.type).id for u in radars]) + " ]"
|
||||
tgt_info.name = (
|
||||
g.obj_name
|
||||
+ " ["
|
||||
+ ",".join(
|
||||
[db.unit_type_from_name(u.type).id for u in radars]
|
||||
)
|
||||
+ " ]"
|
||||
)
|
||||
if len(tgt_info.name) > 25:
|
||||
tgt_info.name = g.obj_name + " [" + str(len(radars)) + " units]"
|
||||
tgt_info.name = (
|
||||
g.obj_name + " [" + str(len(radars)) + " units]"
|
||||
)
|
||||
tgt_info.radars = radars
|
||||
tgt_info.location = g
|
||||
tgt_info.threat_range = threat_range
|
||||
@@ -73,5 +85,3 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox):
|
||||
i = add_model_item(i, model, tgt_info)
|
||||
|
||||
self.setModel(model)
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
||||
|
||||
|
||||
class StrikeTargetInfo:
|
||||
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.location = None
|
||||
@@ -14,17 +13,14 @@ class StrikeTargetInfo:
|
||||
|
||||
|
||||
class QStrikeTargetSelectionComboBox(QFilteredComboBox):
|
||||
|
||||
def __init__(self, game: Game, parent=None):
|
||||
super(QStrikeTargetSelectionComboBox, self).__init__(parent)
|
||||
self.game = game
|
||||
self.find_possible_strike_targets()
|
||||
|
||||
|
||||
for t in self.targets:
|
||||
print(t.name + " - " + str(len(t.units)) + " " + str(len(t.buildings)))
|
||||
|
||||
|
||||
def get_selected_target(self) -> StrikeTargetInfo:
|
||||
n = self.currentText()
|
||||
for target in self.targets:
|
||||
@@ -44,12 +40,14 @@ class QStrikeTargetSelectionComboBox(QFilteredComboBox):
|
||||
return i + 1
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.captured: continue
|
||||
if cp.captured:
|
||||
continue
|
||||
|
||||
added_obj_names = []
|
||||
|
||||
for g in cp.ground_objects:
|
||||
if g.obj_name in added_obj_names: continue
|
||||
if g.obj_name in added_obj_names:
|
||||
continue
|
||||
|
||||
target = StrikeTargetInfo()
|
||||
target.location = g
|
||||
@@ -70,5 +68,3 @@ class QStrikeTargetSelectionComboBox(QFilteredComboBox):
|
||||
added_obj_names.append(g.obj_name)
|
||||
|
||||
self.setModel(model)
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,12 @@ from PySide2.QtWidgets import QSpinBox
|
||||
|
||||
|
||||
class TenthsSpinner(QSpinBox):
|
||||
def __init__(self, minimum: Optional[int] = None,
|
||||
maximum: Optional[int] = None,
|
||||
initial: Optional[int] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
minimum: Optional[int] = None,
|
||||
maximum: Optional[int] = None,
|
||||
initial: Optional[int] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
if minimum is not None:
|
||||
|
||||
@@ -27,8 +27,15 @@ class QFrontLine(QGraphicsLineItem):
|
||||
change the mouse cursor on hover.
|
||||
"""
|
||||
|
||||
def __init__(self, x1: float, y1: float, x2: float, y2: float,
|
||||
mission_target: FrontLine, game_model: GameModel) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
x1: float,
|
||||
y1: float,
|
||||
x2: float,
|
||||
y2: float,
|
||||
mission_target: FrontLine,
|
||||
game_model: GameModel,
|
||||
) -> None:
|
||||
super().__init__(x1, y1, x2, y2)
|
||||
self.mission_target = mission_target
|
||||
self.game_model = game_model
|
||||
@@ -98,10 +105,9 @@ class QFrontLine(QGraphicsLineItem):
|
||||
self.mission_target.control_point_b.base.affect_strength(-0.1)
|
||||
self.game_model.game.initialize_turn()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
|
||||
|
||||
def cheat_backward(self) -> None:
|
||||
self.mission_target.control_point_a.base.affect_strength(-0.1)
|
||||
self.mission_target.control_point_b.base.affect_strength(0.1)
|
||||
self.game_model.game.initialize_turn()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
|
||||
@@ -70,10 +70,10 @@ from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
|
||||
MAX_SHIP_DISTANCE = nautical_miles(80)
|
||||
|
||||
|
||||
def binomial(i: int, n: int) -> float:
|
||||
"""Binomial coefficient"""
|
||||
return math.factorial(n) / float(
|
||||
math.factorial(i) * math.factorial(n - i))
|
||||
return math.factorial(n) / float(math.factorial(i) * math.factorial(n - i))
|
||||
|
||||
|
||||
def bernstein(t: float, i: int, n: int) -> float:
|
||||
@@ -92,7 +92,9 @@ def bezier(t: float, points: Iterable[Tuple[float, float]]) -> Tuple[float, floa
|
||||
return x, y
|
||||
|
||||
|
||||
def bezier_curve_range(n: int, points: Iterable[Tuple[float, float]]) -> Iterator[Tuple[float, float]]:
|
||||
def bezier_curve_range(
|
||||
n: int, points: Iterable[Tuple[float, float]]
|
||||
) -> Iterator[Tuple[float, float]]:
|
||||
"""Range of points in a curve bezier"""
|
||||
for i in range(n):
|
||||
t = i / float(n - 1)
|
||||
@@ -133,7 +135,9 @@ class QLiberationMap(QGraphicsView):
|
||||
self.setGame(game_model.game)
|
||||
|
||||
# Object displayed when unit is selected
|
||||
self.movement_line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(QPointF(0,0),QPointF(0,0)))
|
||||
self.movement_line = QtWidgets.QGraphicsLineItem(
|
||||
QtCore.QLineF(QPointF(0, 0), QPointF(0, 0))
|
||||
)
|
||||
self.movement_line.setPen(QPen(CONST.COLORS["orange"], width=10.0))
|
||||
self.selected_cp: QMapControlPoint = None
|
||||
|
||||
@@ -162,8 +166,7 @@ class QLiberationMap(QGraphicsView):
|
||||
# update_flight_selection will be called in when the last
|
||||
# package is removed. If no flight is selected, it's not a
|
||||
# problem to also have no package selected.
|
||||
logging.error(
|
||||
"Flight was selected with no package selected")
|
||||
logging.error("Flight was selected with no package selected")
|
||||
return
|
||||
|
||||
# Optional[int] isn't a valid type for a Qt signal. None will be
|
||||
@@ -183,7 +186,6 @@ class QLiberationMap(QGraphicsView):
|
||||
self.navmesh_highlight: Optional[QPolygonF] = None
|
||||
self.shortest_path_segments: List[QLineF] = []
|
||||
|
||||
|
||||
def init_scene(self):
|
||||
scene = QLiberationScene(self)
|
||||
self.setScene(scene)
|
||||
@@ -203,6 +205,7 @@ class QLiberationMap(QGraphicsView):
|
||||
"""
|
||||
|
||||
Uncomment to set up theather reference points"""
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||
if not self.reference_point_setup_mode:
|
||||
@@ -225,40 +228,39 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
if event.key() == QtCore.Qt.Key_Down:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[0],
|
||||
Point(0, distance))
|
||||
self.game.theater.reference_points[0], Point(0, distance)
|
||||
)
|
||||
if event.key() == QtCore.Qt.Key_Up:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[0],
|
||||
Point(0, -distance))
|
||||
self.game.theater.reference_points[0], Point(0, -distance)
|
||||
)
|
||||
if event.key() == QtCore.Qt.Key_Left:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[0],
|
||||
Point(-distance, 0))
|
||||
self.game.theater.reference_points[0], Point(-distance, 0)
|
||||
)
|
||||
if event.key() == QtCore.Qt.Key_Right:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[0],
|
||||
Point(distance, 0))
|
||||
self.game.theater.reference_points[0], Point(distance, 0)
|
||||
)
|
||||
|
||||
if event.key() == QtCore.Qt.Key_S:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[1],
|
||||
Point(0, distance))
|
||||
self.game.theater.reference_points[1], Point(0, distance)
|
||||
)
|
||||
if event.key() == QtCore.Qt.Key_W:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[1],
|
||||
Point(0, -distance))
|
||||
self.game.theater.reference_points[1], Point(0, -distance)
|
||||
)
|
||||
if event.key() == QtCore.Qt.Key_A:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[1],
|
||||
Point(-distance, 0))
|
||||
self.game.theater.reference_points[1], Point(-distance, 0)
|
||||
)
|
||||
if event.key() == QtCore.Qt.Key_D:
|
||||
self.update_reference_point(
|
||||
self.game.theater.reference_points[1],
|
||||
Point(distance, 0))
|
||||
self.game.theater.reference_points[1], Point(distance, 0)
|
||||
)
|
||||
|
||||
logging.debug(
|
||||
f"Reference points: {self.game.theater.reference_points}")
|
||||
logging.debug(f"Reference points: {self.game.theater.reference_points}")
|
||||
self.reload_scene()
|
||||
|
||||
@staticmethod
|
||||
@@ -275,16 +277,33 @@ class QLiberationMap(QGraphicsView):
|
||||
distance_point = self._transform_point(culling_distance_point)
|
||||
transformed = self._transform_point(point)
|
||||
radius = distance_point[0] - transformed[0]
|
||||
scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"])
|
||||
scene.addEllipse(
|
||||
transformed[0] - radius,
|
||||
transformed[1] - radius,
|
||||
2 * radius,
|
||||
2 * radius,
|
||||
CONST.COLORS["transparent"],
|
||||
CONST.COLORS["light_green_transparent"],
|
||||
)
|
||||
for zone in culling_zones:
|
||||
culling_distance_zone = Point(zone.x + culling_distance*1000, zone.y + culling_distance*1000)
|
||||
culling_distance_zone = Point(
|
||||
zone.x + culling_distance * 1000, zone.y + culling_distance * 1000
|
||||
)
|
||||
distance_zone = self._transform_point(culling_distance_zone)
|
||||
transformed = self._transform_point(zone)
|
||||
radius = distance_zone[0] - transformed[0]
|
||||
scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"])
|
||||
scene.addEllipse(
|
||||
transformed[0] - radius,
|
||||
transformed[1] - radius,
|
||||
2 * radius,
|
||||
2 * radius,
|
||||
CONST.COLORS["transparent"],
|
||||
CONST.COLORS["light_green_transparent"],
|
||||
)
|
||||
|
||||
def draw_shapely_poly(self, scene: QGraphicsScene, poly: Polygon, pen: QPen,
|
||||
brush: QBrush) -> Optional[QPolygonF]:
|
||||
def draw_shapely_poly(
|
||||
self, scene: QGraphicsScene, poly: Polygon, pen: QPen, brush: QBrush
|
||||
) -> Optional[QPolygonF]:
|
||||
if poly.is_empty:
|
||||
return None
|
||||
points = []
|
||||
@@ -293,16 +312,18 @@ class QLiberationMap(QGraphicsView):
|
||||
points.append(QPointF(x, y))
|
||||
return scene.addPolygon(QPolygonF(points), pen, brush)
|
||||
|
||||
def draw_threat_zone(self, scene: QGraphicsScene, poly: Polygon,
|
||||
player: bool) -> None:
|
||||
def draw_threat_zone(
|
||||
self, scene: QGraphicsScene, poly: Polygon, player: bool
|
||||
) -> None:
|
||||
if player:
|
||||
brush = QColor(0, 132, 255, 100)
|
||||
else:
|
||||
brush = QColor(227, 32, 0, 100)
|
||||
self.draw_shapely_poly(scene, poly, CONST.COLORS["transparent"], brush)
|
||||
|
||||
def display_threat_zones(self, scene: QGraphicsScene,
|
||||
options: ThreatZoneOptions, player: bool) -> None:
|
||||
def display_threat_zones(
|
||||
self, scene: QGraphicsScene, options: ThreatZoneOptions, player: bool
|
||||
) -> None:
|
||||
"""Draws the threat zones on the map."""
|
||||
threat_zones = self.game.threat_zone_for(player)
|
||||
if options.all:
|
||||
@@ -321,33 +342,44 @@ class QLiberationMap(QGraphicsView):
|
||||
for poly in polys:
|
||||
self.draw_threat_zone(scene, poly, player)
|
||||
|
||||
def draw_navmesh_neighbor_line(self, scene: QGraphicsScene, poly: Polygon,
|
||||
begin: ShapelyPoint) -> None:
|
||||
def draw_navmesh_neighbor_line(
|
||||
self, scene: QGraphicsScene, poly: Polygon, begin: ShapelyPoint
|
||||
) -> None:
|
||||
vertex = Point(begin.x, begin.y)
|
||||
centroid = poly.centroid
|
||||
direction = Point(centroid.x, centroid.y)
|
||||
end = vertex.point_from_heading(vertex.heading_between_point(direction),
|
||||
nautical_miles(2).meters)
|
||||
end = vertex.point_from_heading(
|
||||
vertex.heading_between_point(direction), nautical_miles(2).meters
|
||||
)
|
||||
|
||||
scene.addLine(QLineF(QPointF(*self._transform_point(vertex)),
|
||||
QPointF(*self._transform_point(end))),
|
||||
CONST.COLORS["yellow"])
|
||||
scene.addLine(
|
||||
QLineF(
|
||||
QPointF(*self._transform_point(vertex)),
|
||||
QPointF(*self._transform_point(end)),
|
||||
),
|
||||
CONST.COLORS["yellow"],
|
||||
)
|
||||
|
||||
@singledispatchmethod
|
||||
def draw_navmesh_border(self, intersection, scene: QGraphicsScene,
|
||||
poly: Polygon) -> None:
|
||||
raise NotImplementedError("draw_navmesh_border not implemented for %s",
|
||||
intersection.__class__.__name__)
|
||||
def draw_navmesh_border(
|
||||
self, intersection, scene: QGraphicsScene, poly: Polygon
|
||||
) -> None:
|
||||
raise NotImplementedError(
|
||||
"draw_navmesh_border not implemented for %s",
|
||||
intersection.__class__.__name__,
|
||||
)
|
||||
|
||||
@draw_navmesh_border.register
|
||||
def draw_navmesh_point_border(self, intersection: ShapelyPoint,
|
||||
scene: QGraphicsScene, poly: Polygon) -> None:
|
||||
def draw_navmesh_point_border(
|
||||
self, intersection: ShapelyPoint, scene: QGraphicsScene, poly: Polygon
|
||||
) -> None:
|
||||
# Draw a line from the vertex toward the center of the polygon.
|
||||
self.draw_navmesh_neighbor_line(scene, poly, intersection)
|
||||
|
||||
@draw_navmesh_border.register
|
||||
def draw_navmesh_edge_border(self, intersection: LineString,
|
||||
scene: QGraphicsScene, poly: Polygon) -> None:
|
||||
def draw_navmesh_edge_border(
|
||||
self, intersection: LineString, scene: QGraphicsScene, poly: Polygon
|
||||
) -> None:
|
||||
# Draw a line from the center of the edge toward the center of the
|
||||
# polygon.
|
||||
edge_center = intersection.interpolate(0.5, normalized=True)
|
||||
@@ -355,13 +387,16 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
def display_navmesh(self, scene: QGraphicsScene, player: bool) -> None:
|
||||
for navpoly in self.game.navmesh_for(player).polys:
|
||||
self.draw_shapely_poly(scene, navpoly.poly, CONST.COLORS["black"],
|
||||
CONST.COLORS["transparent"])
|
||||
self.draw_shapely_poly(
|
||||
scene, navpoly.poly, CONST.COLORS["black"], CONST.COLORS["transparent"]
|
||||
)
|
||||
|
||||
position = self._transform_point(
|
||||
Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y))
|
||||
text = scene.addSimpleText(f"Navmesh {navpoly.ident}",
|
||||
self.waypoint_info_font)
|
||||
Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y)
|
||||
)
|
||||
text = scene.addSimpleText(
|
||||
f"Navmesh {navpoly.ident}", self.waypoint_info_font
|
||||
)
|
||||
text.setBrush(QColor(255, 255, 255))
|
||||
text.setPen(QColor(255, 255, 255))
|
||||
text.moveBy(position[0] + 8, position[1])
|
||||
@@ -370,8 +405,9 @@ class QLiberationMap(QGraphicsView):
|
||||
for border in navpoly.neighbors.values():
|
||||
self.draw_navmesh_border(border, scene, navpoly.poly)
|
||||
|
||||
def highlight_mouse_navmesh(self, scene: QGraphicsScene, navmesh: NavMesh,
|
||||
mouse_position: Point) -> None:
|
||||
def highlight_mouse_navmesh(
|
||||
self, scene: QGraphicsScene, navmesh: NavMesh, mouse_position: Point
|
||||
) -> None:
|
||||
if self.navmesh_highlight is not None:
|
||||
try:
|
||||
scene.removeItem(self.navmesh_highlight)
|
||||
@@ -381,11 +417,15 @@ class QLiberationMap(QGraphicsView):
|
||||
if navpoly is None:
|
||||
return
|
||||
self.navmesh_highlight = self.draw_shapely_poly(
|
||||
scene, navpoly.poly, CONST.COLORS["transparent"],
|
||||
CONST.COLORS["light_green_transparent"])
|
||||
scene,
|
||||
navpoly.poly,
|
||||
CONST.COLORS["transparent"],
|
||||
CONST.COLORS["light_green_transparent"],
|
||||
)
|
||||
|
||||
def draw_shortest_path(self, scene: QGraphicsScene, navmesh: NavMesh,
|
||||
destination: Point, player: bool) -> None:
|
||||
def draw_shortest_path(
|
||||
self, scene: QGraphicsScene, navmesh: NavMesh, destination: Point, player: bool
|
||||
) -> None:
|
||||
for line in self.shortest_path_segments:
|
||||
try:
|
||||
scene.removeItem(line)
|
||||
@@ -407,21 +447,36 @@ class QLiberationMap(QGraphicsView):
|
||||
flight_path_pen = self.flight_path_pen(player, selected=True)
|
||||
# Draw the line to the *middle* of the waypoint.
|
||||
offset = self.WAYPOINT_SIZE // 2
|
||||
self.shortest_path_segments.append(scene.addLine(
|
||||
prev_pos[0] + offset, prev_pos[1] + offset,
|
||||
new_pos[0] + offset, new_pos[1] + offset,
|
||||
flight_path_pen
|
||||
))
|
||||
self.shortest_path_segments.append(
|
||||
scene.addLine(
|
||||
prev_pos[0] + offset,
|
||||
prev_pos[1] + offset,
|
||||
new_pos[0] + offset,
|
||||
new_pos[1] + offset,
|
||||
flight_path_pen,
|
||||
)
|
||||
)
|
||||
|
||||
self.shortest_path_segments.append(scene.addEllipse(
|
||||
new_pos[0], new_pos[1], self.WAYPOINT_SIZE,
|
||||
self.WAYPOINT_SIZE, flight_path_pen, flight_path_pen
|
||||
))
|
||||
self.shortest_path_segments.append(
|
||||
scene.addEllipse(
|
||||
new_pos[0],
|
||||
new_pos[1],
|
||||
self.WAYPOINT_SIZE,
|
||||
self.WAYPOINT_SIZE,
|
||||
flight_path_pen,
|
||||
flight_path_pen,
|
||||
)
|
||||
)
|
||||
|
||||
prev_pos = new_pos
|
||||
|
||||
def draw_test_flight_plan(self, scene: QGraphicsScene, task: FlightType,
|
||||
point_near_target: Point, player: bool) -> None:
|
||||
def draw_test_flight_plan(
|
||||
self,
|
||||
scene: QGraphicsScene,
|
||||
task: FlightType,
|
||||
point_near_target: Point,
|
||||
player: bool,
|
||||
) -> None:
|
||||
for line in self.shortest_path_segments:
|
||||
try:
|
||||
scene.removeItem(line)
|
||||
@@ -438,8 +493,16 @@ class QLiberationMap(QGraphicsView):
|
||||
origin = self.game.theater.enemy_points()[0]
|
||||
|
||||
package = Package(target)
|
||||
flight = Flight(package, F_16C_50, 2, task, start_type="Warm",
|
||||
departure=origin, arrival=origin, divert=None)
|
||||
flight = Flight(
|
||||
package,
|
||||
F_16C_50,
|
||||
2,
|
||||
task,
|
||||
start_type="Warm",
|
||||
departure=origin,
|
||||
arrival=origin,
|
||||
divert=None,
|
||||
)
|
||||
package.add_flight(flight)
|
||||
planner = FlightPlanBuilder(self.game, package, is_player=player)
|
||||
try:
|
||||
@@ -452,31 +515,49 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
@staticmethod
|
||||
def should_display_ground_objects_at(cp: ControlPoint) -> bool:
|
||||
return ((DisplayOptions.sam_ranges and cp.captured) or
|
||||
(DisplayOptions.enemy_sam_ranges and not cp.captured))
|
||||
return (DisplayOptions.sam_ranges and cp.captured) or (
|
||||
DisplayOptions.enemy_sam_ranges and not cp.captured
|
||||
)
|
||||
|
||||
def draw_threat_range(self, scene: QGraphicsScene, group: Group, ground_object: TheaterGroundObject, cp: ControlPoint) -> None:
|
||||
def draw_threat_range(
|
||||
self,
|
||||
scene: QGraphicsScene,
|
||||
group: Group,
|
||||
ground_object: TheaterGroundObject,
|
||||
cp: ControlPoint,
|
||||
) -> None:
|
||||
go_pos = self._transform_point(ground_object.position)
|
||||
detection_range = ground_object.detection_range(group)
|
||||
threat_range = ground_object.threat_range(group)
|
||||
if threat_range:
|
||||
threat_pos = self._transform_point(
|
||||
ground_object.position + Point(threat_range.meters,
|
||||
threat_range.meters))
|
||||
ground_object.position + Point(threat_range.meters, threat_range.meters)
|
||||
)
|
||||
threat_radius = Point(*go_pos).distance_to_point(Point(*threat_pos))
|
||||
|
||||
# Add threat range circle
|
||||
scene.addEllipse(go_pos[0] - threat_radius / 2 + 7, go_pos[1] - threat_radius / 2 + 6,
|
||||
threat_radius, threat_radius, self.threat_pen(cp.captured))
|
||||
scene.addEllipse(
|
||||
go_pos[0] - threat_radius / 2 + 7,
|
||||
go_pos[1] - threat_radius / 2 + 6,
|
||||
threat_radius,
|
||||
threat_radius,
|
||||
self.threat_pen(cp.captured),
|
||||
)
|
||||
|
||||
if detection_range and DisplayOptions.detection_range:
|
||||
# Add detection range circle
|
||||
detection_pos = self._transform_point(
|
||||
ground_object.position + Point(detection_range.meters,
|
||||
detection_range.meters))
|
||||
ground_object.position
|
||||
+ Point(detection_range.meters, detection_range.meters)
|
||||
)
|
||||
detection_radius = Point(*go_pos).distance_to_point(Point(*detection_pos))
|
||||
scene.addEllipse(go_pos[0] - detection_radius/2 + 7, go_pos[1] - detection_radius/2 + 6,
|
||||
detection_radius, detection_radius, self.detection_pen(cp.captured))
|
||||
scene.addEllipse(
|
||||
go_pos[0] - detection_radius / 2 + 7,
|
||||
go_pos[1] - detection_radius / 2 + 6,
|
||||
detection_radius,
|
||||
detection_radius,
|
||||
self.detection_pen(cp.captured),
|
||||
)
|
||||
|
||||
def draw_ground_objects(self, scene: QGraphicsScene, cp: ControlPoint) -> None:
|
||||
added_objects = []
|
||||
@@ -486,8 +567,22 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
go_pos = self._transform_point(ground_object.position)
|
||||
if not ground_object.airbase_group:
|
||||
buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name)
|
||||
scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 14, 12, cp, ground_object, self.game, buildings))
|
||||
buildings = self.game.theater.find_ground_objects_by_obj_name(
|
||||
ground_object.obj_name
|
||||
)
|
||||
scene.addItem(
|
||||
QMapGroundObject(
|
||||
self,
|
||||
go_pos[0],
|
||||
go_pos[1],
|
||||
14,
|
||||
12,
|
||||
cp,
|
||||
ground_object,
|
||||
self.game,
|
||||
buildings,
|
||||
)
|
||||
)
|
||||
|
||||
should_display = self.should_display_ground_objects_at(cp)
|
||||
if ground_object.might_have_aa and should_display:
|
||||
@@ -508,10 +603,8 @@ class QLiberationMap(QGraphicsView):
|
||||
if DisplayOptions.culling and self.game.settings.perf_culling:
|
||||
self.display_culling(scene)
|
||||
|
||||
self.display_threat_zones(scene, DisplayOptions.blue_threat_zones,
|
||||
player=True)
|
||||
self.display_threat_zones(scene, DisplayOptions.red_threat_zones,
|
||||
player=False)
|
||||
self.display_threat_zones(scene, DisplayOptions.blue_threat_zones, player=True)
|
||||
self.display_threat_zones(scene, DisplayOptions.red_threat_zones, player=False)
|
||||
|
||||
if DisplayOptions.navmeshes.blue_navmesh:
|
||||
self.display_navmesh(scene, player=True)
|
||||
@@ -522,24 +615,33 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
pos = self._transform_point(cp.position)
|
||||
|
||||
scene.addItem(QMapControlPoint(self, pos[0] - CONST.CP_SIZE / 2,
|
||||
pos[1] - CONST.CP_SIZE / 2,
|
||||
CONST.CP_SIZE,
|
||||
CONST.CP_SIZE, cp, self.game_model))
|
||||
scene.addItem(
|
||||
QMapControlPoint(
|
||||
self,
|
||||
pos[0] - CONST.CP_SIZE / 2,
|
||||
pos[1] - CONST.CP_SIZE / 2,
|
||||
CONST.CP_SIZE,
|
||||
CONST.CP_SIZE,
|
||||
cp,
|
||||
self.game_model,
|
||||
)
|
||||
)
|
||||
|
||||
if cp.captured:
|
||||
pen = QPen(brush=CONST.COLORS[playerColor])
|
||||
brush = CONST.COLORS[playerColor+"_transparent"]
|
||||
brush = CONST.COLORS[playerColor + "_transparent"]
|
||||
else:
|
||||
pen = QPen(brush=CONST.COLORS[enemyColor])
|
||||
brush = CONST.COLORS[enemyColor+"_transparent"]
|
||||
brush = CONST.COLORS[enemyColor + "_transparent"]
|
||||
|
||||
self.draw_ground_objects(scene, cp)
|
||||
|
||||
if cp.target_position is not None:
|
||||
proj = self._transform_point(cp.target_position)
|
||||
scene.addLine(QLineF(QPointF(pos[0], pos[1]), QPointF(proj[0], proj[1])),
|
||||
QPen(CONST.COLORS["green"], width=10, s=Qt.DashDotLine))
|
||||
scene.addLine(
|
||||
QLineF(QPointF(pos[0], pos[1]), QPointF(proj[0], proj[1])),
|
||||
QPen(CONST.COLORS["green"], width=10, s=Qt.DashDotLine),
|
||||
)
|
||||
|
||||
for cp in self.game.theater.enemy_points():
|
||||
if DisplayOptions.lines:
|
||||
@@ -585,8 +687,9 @@ class QLiberationMap(QGraphicsView):
|
||||
continue
|
||||
self.draw_flight_plan(scene, flight, selected)
|
||||
|
||||
def draw_flight_plan(self, scene: QGraphicsScene, flight: Flight,
|
||||
selected: bool) -> None:
|
||||
def draw_flight_plan(
|
||||
self, scene: QGraphicsScene, flight: Flight, selected: bool
|
||||
) -> None:
|
||||
is_player = flight.from_cp.captured
|
||||
pos = self._transform_point(flight.from_cp.position)
|
||||
|
||||
@@ -604,8 +707,7 @@ class QLiberationMap(QGraphicsView):
|
||||
continue
|
||||
|
||||
new_pos = self._transform_point(Point(point.x, point.y))
|
||||
self.draw_flight_path(scene, prev_pos, new_pos, is_player,
|
||||
selected)
|
||||
self.draw_flight_path(scene, prev_pos, new_pos, is_player, selected)
|
||||
self.draw_waypoint(scene, new_pos, is_player, selected)
|
||||
if selected and DisplayOptions.waypoint_info:
|
||||
if point.waypoint_type in target_types:
|
||||
@@ -613,44 +715,63 @@ class QLiberationMap(QGraphicsView):
|
||||
# Don't draw dozens of targets over each other.
|
||||
continue
|
||||
drew_target = True
|
||||
self.draw_waypoint_info(scene, idx + 1, point, new_pos,
|
||||
flight.flight_plan)
|
||||
self.draw_waypoint_info(
|
||||
scene, idx + 1, point, new_pos, flight.flight_plan
|
||||
)
|
||||
prev_pos = tuple(new_pos)
|
||||
|
||||
if selected and DisplayOptions.barcap_commit_range:
|
||||
self.draw_barcap_commit_range(scene, flight)
|
||||
|
||||
def draw_barcap_commit_range(self, scene: QGraphicsScene,
|
||||
flight: Flight) -> None:
|
||||
def draw_barcap_commit_range(self, scene: QGraphicsScene, flight: Flight) -> None:
|
||||
if flight.flight_type is not FlightType.BARCAP:
|
||||
return
|
||||
if not isinstance(flight.flight_plan, BarCapFlightPlan):
|
||||
return
|
||||
start = flight.flight_plan.patrol_start
|
||||
end = flight.flight_plan.patrol_end
|
||||
line = LineString([
|
||||
ShapelyPoint(start.x, start.y),
|
||||
ShapelyPoint(end.x, end.y),
|
||||
])
|
||||
line = LineString(
|
||||
[
|
||||
ShapelyPoint(start.x, start.y),
|
||||
ShapelyPoint(end.x, end.y),
|
||||
]
|
||||
)
|
||||
doctrine = self.game.faction_for(flight.departure.captured).doctrine
|
||||
bubble = line.buffer(doctrine.cap_engagement_range.meters)
|
||||
self.flight_path_items.append(self.draw_shapely_poly(
|
||||
scene, bubble, CONST.COLORS["yellow"], CONST.COLORS["transparent"]
|
||||
))
|
||||
self.flight_path_items.append(
|
||||
self.draw_shapely_poly(
|
||||
scene, bubble, CONST.COLORS["yellow"], CONST.COLORS["transparent"]
|
||||
)
|
||||
)
|
||||
|
||||
def draw_waypoint(self, scene: QGraphicsScene,
|
||||
position: Tuple[float, float], player: bool,
|
||||
selected: bool) -> None:
|
||||
def draw_waypoint(
|
||||
self,
|
||||
scene: QGraphicsScene,
|
||||
position: Tuple[float, float],
|
||||
player: bool,
|
||||
selected: bool,
|
||||
) -> None:
|
||||
waypoint_pen = self.waypoint_pen(player, selected)
|
||||
waypoint_brush = self.waypoint_brush(player, selected)
|
||||
self.flight_path_items.append(scene.addEllipse(
|
||||
position[0], position[1], self.WAYPOINT_SIZE,
|
||||
self.WAYPOINT_SIZE, waypoint_pen, waypoint_brush
|
||||
))
|
||||
self.flight_path_items.append(
|
||||
scene.addEllipse(
|
||||
position[0],
|
||||
position[1],
|
||||
self.WAYPOINT_SIZE,
|
||||
self.WAYPOINT_SIZE,
|
||||
waypoint_pen,
|
||||
waypoint_brush,
|
||||
)
|
||||
)
|
||||
|
||||
def draw_waypoint_info(self, scene: QGraphicsScene, number: int,
|
||||
waypoint: FlightWaypoint, position: Tuple[int, int],
|
||||
flight_plan: FlightPlan) -> None:
|
||||
def draw_waypoint_info(
|
||||
self,
|
||||
scene: QGraphicsScene,
|
||||
number: int,
|
||||
waypoint: FlightWaypoint,
|
||||
position: Tuple[int, int],
|
||||
flight_plan: FlightPlan,
|
||||
) -> None:
|
||||
|
||||
altitude = int(waypoint.alt.feet)
|
||||
altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL"
|
||||
@@ -669,11 +790,13 @@ class QLiberationMap(QGraphicsView):
|
||||
pen = QPen(QColor("black"), 0.3)
|
||||
brush = QColor("white")
|
||||
|
||||
text = "\n".join([
|
||||
f"{number} {waypoint.name}",
|
||||
f"{altitude} ft {altitude_type}",
|
||||
tot,
|
||||
])
|
||||
text = "\n".join(
|
||||
[
|
||||
f"{number} {waypoint.name}",
|
||||
f"{altitude} ft {altitude_type}",
|
||||
tot,
|
||||
]
|
||||
)
|
||||
|
||||
item = scene.addSimpleText(text, self.waypoint_info_font)
|
||||
item.setFlag(QGraphicsItem.ItemIgnoresTransformations)
|
||||
@@ -683,19 +806,30 @@ class QLiberationMap(QGraphicsView):
|
||||
item.setZValue(2)
|
||||
self.flight_path_items.append(item)
|
||||
|
||||
def draw_flight_path(self, scene: QGraphicsScene, pos0: Tuple[float, float],
|
||||
pos1: Tuple[float, float], player: bool,
|
||||
selected: bool) -> None:
|
||||
def draw_flight_path(
|
||||
self,
|
||||
scene: QGraphicsScene,
|
||||
pos0: Tuple[float, float],
|
||||
pos1: Tuple[float, float],
|
||||
player: bool,
|
||||
selected: bool,
|
||||
) -> None:
|
||||
flight_path_pen = self.flight_path_pen(player, selected)
|
||||
# Draw the line to the *middle* of the waypoint.
|
||||
offset = self.WAYPOINT_SIZE // 2
|
||||
self.flight_path_items.append(scene.addLine(
|
||||
pos0[0] + offset, pos0[1] + offset,
|
||||
pos1[0] + offset, pos1[1] + offset,
|
||||
flight_path_pen
|
||||
))
|
||||
self.flight_path_items.append(
|
||||
scene.addLine(
|
||||
pos0[0] + offset,
|
||||
pos0[1] + offset,
|
||||
pos1[0] + offset,
|
||||
pos1[1] + offset,
|
||||
flight_path_pen,
|
||||
)
|
||||
)
|
||||
|
||||
def draw_bezier_frontline(self, scene: QGraphicsScene, pen:QPen, frontline: FrontLine) -> None:
|
||||
def draw_bezier_frontline(
|
||||
self, scene: QGraphicsScene, pen: QPen, frontline: FrontLine
|
||||
) -> None:
|
||||
"""
|
||||
Thanks to Alquimista for sharing a python implementation of the bezier algorithm this is adapted from.
|
||||
https://gist.github.com/Alquimista/1274149#file-bezdraw-py
|
||||
@@ -706,7 +840,9 @@ class QLiberationMap(QGraphicsView):
|
||||
bezier_fixed_points.append(self._transform_point(segment.point_b))
|
||||
|
||||
old_point = bezier_fixed_points[0]
|
||||
for point in bezier_curve_range(int(len(bezier_fixed_points) * 2), bezier_fixed_points):
|
||||
for point in bezier_curve_range(
|
||||
int(len(bezier_fixed_points) * 2), bezier_fixed_points
|
||||
):
|
||||
scene.addLine(old_point[0], old_point[1], point[0], point[1], pen=pen)
|
||||
old_point = point
|
||||
|
||||
@@ -715,14 +851,18 @@ class QLiberationMap(QGraphicsView):
|
||||
for connected_cp in cp.connected_points:
|
||||
pos2 = self._transform_point(connected_cp.position)
|
||||
if not cp.captured:
|
||||
color = CONST.COLORS["dark_"+enemyColor]
|
||||
color = CONST.COLORS["dark_" + enemyColor]
|
||||
else:
|
||||
color = CONST.COLORS["dark_"+playerColor]
|
||||
color = CONST.COLORS["dark_" + playerColor]
|
||||
pen = QPen(brush=color)
|
||||
pen.setColor(color)
|
||||
pen.setWidth(6)
|
||||
frontline = FrontLine(cp, connected_cp, self.game.theater)
|
||||
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
|
||||
if (
|
||||
cp.captured
|
||||
and not connected_cp.captured
|
||||
and Conflict.has_frontline_between(cp, connected_cp)
|
||||
):
|
||||
if DisplayOptions.actual_frontline_pos:
|
||||
self.draw_actual_frontline(frontline, scene, pen)
|
||||
else:
|
||||
@@ -730,35 +870,30 @@ class QLiberationMap(QGraphicsView):
|
||||
else:
|
||||
self.draw_bezier_frontline(scene, pen, frontline)
|
||||
|
||||
def draw_frontline_approximation(self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen) -> None:
|
||||
def draw_frontline_approximation(
|
||||
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen
|
||||
) -> None:
|
||||
posx = frontline.position
|
||||
h = frontline.attack_heading
|
||||
pos2 = self._transform_point(posx)
|
||||
self.draw_bezier_frontline(scene, pen, frontline)
|
||||
p1 = point_from_heading(pos2[0], pos2[1], h+180, 25)
|
||||
p1 = point_from_heading(pos2[0], pos2[1], h + 180, 25)
|
||||
p2 = point_from_heading(pos2[0], pos2[1], h, 25)
|
||||
scene.addItem(
|
||||
QFrontLine(
|
||||
p1[0],
|
||||
p1[1],
|
||||
p2[0],
|
||||
p2[1],
|
||||
frontline,
|
||||
self.game_model
|
||||
)
|
||||
QFrontLine(p1[0], p1[1], p2[0], p2[1], frontline, self.game_model)
|
||||
)
|
||||
|
||||
def draw_actual_frontline(self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen) -> None:
|
||||
def draw_actual_frontline(
|
||||
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen
|
||||
) -> None:
|
||||
self.draw_bezier_frontline(scene, pen, frontline)
|
||||
vector = Conflict.frontline_vector(
|
||||
frontline.control_point_a,
|
||||
frontline.control_point_b,
|
||||
self.game.theater
|
||||
frontline.control_point_a, frontline.control_point_b, self.game.theater
|
||||
)
|
||||
left_pos = self._transform_point(vector[0])
|
||||
right_pos = self._transform_point(
|
||||
vector[0].point_from_heading(vector[1], vector[2])
|
||||
)
|
||||
vector[0].point_from_heading(vector[1], vector[2])
|
||||
)
|
||||
scene.addItem(
|
||||
QFrontLine(
|
||||
left_pos[0],
|
||||
@@ -766,7 +901,7 @@ class QLiberationMap(QGraphicsView):
|
||||
right_pos[0],
|
||||
right_pos[1],
|
||||
frontline,
|
||||
self.game_model
|
||||
self.game_model,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -779,26 +914,48 @@ class QLiberationMap(QGraphicsView):
|
||||
SMALL_LINE = 2
|
||||
|
||||
dist = self.distance_to_pixels(nautical_miles(scale_distance_nm))
|
||||
self.scene().addRect(POS_X, POS_Y-PADDING, PADDING*2 + dist, BIG_LINE*2+3*PADDING, pen=CONST.COLORS["black"], brush=CONST.COLORS["black"])
|
||||
l = self.scene().addLine(POS_X + PADDING, POS_Y + BIG_LINE*2, POS_X + PADDING + dist, POS_Y + BIG_LINE*2)
|
||||
self.scene().addRect(
|
||||
POS_X,
|
||||
POS_Y - PADDING,
|
||||
PADDING * 2 + dist,
|
||||
BIG_LINE * 2 + 3 * PADDING,
|
||||
pen=CONST.COLORS["black"],
|
||||
brush=CONST.COLORS["black"],
|
||||
)
|
||||
l = self.scene().addLine(
|
||||
POS_X + PADDING,
|
||||
POS_Y + BIG_LINE * 2,
|
||||
POS_X + PADDING + dist,
|
||||
POS_Y + BIG_LINE * 2,
|
||||
)
|
||||
|
||||
text = self.scene().addText("0nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False))
|
||||
text.setPos(POS_X, POS_Y + BIG_LINE*2)
|
||||
text = self.scene().addText(
|
||||
"0nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False)
|
||||
)
|
||||
text.setPos(POS_X, POS_Y + BIG_LINE * 2)
|
||||
text.setDefaultTextColor(Qt.white)
|
||||
|
||||
text2 = self.scene().addText(str(scale_distance_nm) + "nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False))
|
||||
text2 = self.scene().addText(
|
||||
str(scale_distance_nm) + "nm",
|
||||
font=QFont("Trebuchet MS", 6, weight=5, italic=False),
|
||||
)
|
||||
text2.setPos(POS_X + dist, POS_Y + BIG_LINE * 2)
|
||||
text2.setDefaultTextColor(Qt.white)
|
||||
|
||||
l.setPen(CONST.COLORS["white"])
|
||||
for i in range(number_of_points+1):
|
||||
d = float(i)/float(number_of_points)
|
||||
for i in range(number_of_points + 1):
|
||||
d = float(i) / float(number_of_points)
|
||||
if i == 0 or i == number_of_points:
|
||||
h = BIG_LINE
|
||||
else:
|
||||
h = SMALL_LINE
|
||||
|
||||
l = self.scene().addLine(POS_X + PADDING + d * dist, POS_Y + BIG_LINE*2, POS_X + PADDING + d * dist, POS_Y + BIG_LINE - h)
|
||||
l = self.scene().addLine(
|
||||
POS_X + PADDING + d * dist,
|
||||
POS_Y + BIG_LINE * 2,
|
||||
POS_X + PADDING + d * dist,
|
||||
POS_Y + BIG_LINE - h,
|
||||
)
|
||||
l.setPen(CONST.COLORS["white"])
|
||||
|
||||
def wheelEvent(self, event: QWheelEvent):
|
||||
@@ -828,7 +985,8 @@ class QLiberationMap(QGraphicsView):
|
||||
point_b = self.game.theater.reference_points[1]
|
||||
|
||||
world_distance = self._transpose_point(
|
||||
point_b.world_coordinates - point_a.world_coordinates)
|
||||
point_b.world_coordinates - point_a.world_coordinates
|
||||
)
|
||||
image_distance = point_b.image_coordinates - point_a.image_coordinates
|
||||
|
||||
x_scale = image_distance.x / world_distance.x
|
||||
@@ -870,8 +1028,7 @@ class QLiberationMap(QGraphicsView):
|
||||
scale = self._scaling_factor()
|
||||
|
||||
offset = point_a.image_coordinates - scene_point
|
||||
scaled = self._transpose_point(
|
||||
Point(offset.x / scale.x, offset.y / scale.y))
|
||||
scaled = self._transpose_point(Point(offset.x / scale.x, offset.y / scale.y))
|
||||
return point_a.world_coordinates - scaled
|
||||
|
||||
def distance_to_pixels(self, distance: Distance) -> int:
|
||||
@@ -955,7 +1112,12 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
for sea_zone in self.game.theater.landmap.sea_zones:
|
||||
print(sea_zone)
|
||||
poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone.exterior.coords])
|
||||
poly = QPolygonF(
|
||||
[
|
||||
QPointF(*self._transform_point(Point(point[0], point[1])))
|
||||
for point in sea_zone.exterior.coords
|
||||
]
|
||||
)
|
||||
if self.reference_point_setup_mode:
|
||||
color = "sea_blue_transparent"
|
||||
else:
|
||||
@@ -963,18 +1125,40 @@ class QLiberationMap(QGraphicsView):
|
||||
scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color])
|
||||
|
||||
for inclusion_zone in self.game.theater.landmap.inclusion_zones:
|
||||
poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone.exterior.coords])
|
||||
poly = QPolygonF(
|
||||
[
|
||||
QPointF(*self._transform_point(Point(point[0], point[1])))
|
||||
for point in inclusion_zone.exterior.coords
|
||||
]
|
||||
)
|
||||
if self.reference_point_setup_mode:
|
||||
scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_grey_transparent"])
|
||||
scene.addPolygon(
|
||||
poly,
|
||||
CONST.COLORS["grey_transparent"],
|
||||
CONST.COLORS["dark_grey_transparent"],
|
||||
)
|
||||
else:
|
||||
scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"])
|
||||
scene.addPolygon(
|
||||
poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"]
|
||||
)
|
||||
|
||||
for exclusion_zone in self.game.theater.landmap.exclusion_zones:
|
||||
poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone.exterior.coords])
|
||||
poly = QPolygonF(
|
||||
[
|
||||
QPointF(*self._transform_point(Point(point[0], point[1])))
|
||||
for point in exclusion_zone.exterior.coords
|
||||
]
|
||||
)
|
||||
if self.reference_point_setup_mode:
|
||||
scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_dark_grey_transparent"])
|
||||
scene.addPolygon(
|
||||
poly,
|
||||
CONST.COLORS["grey_transparent"],
|
||||
CONST.COLORS["dark_dark_grey_transparent"],
|
||||
)
|
||||
else:
|
||||
scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"])
|
||||
scene.addPolygon(
|
||||
poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"]
|
||||
)
|
||||
|
||||
# Uncomment to display plan projection test
|
||||
# self.projection_test()
|
||||
@@ -983,15 +1167,18 @@ class QLiberationMap(QGraphicsView):
|
||||
if self.reference_point_setup_mode:
|
||||
for i, point in enumerate(self.game.theater.reference_points):
|
||||
self.scene().addRect(
|
||||
QRectF(point.image_coordinates.x, point.image_coordinates.y,
|
||||
25, 25), pen=CONST.COLORS["red"],
|
||||
brush=CONST.COLORS["red"])
|
||||
QRectF(
|
||||
point.image_coordinates.x, point.image_coordinates.y, 25, 25
|
||||
),
|
||||
pen=CONST.COLORS["red"],
|
||||
brush=CONST.COLORS["red"],
|
||||
)
|
||||
text = self.scene().addText(
|
||||
f"P{i} = {point.image_coordinates}",
|
||||
font=QFont("Trebuchet MS", 14, weight=8, italic=False))
|
||||
font=QFont("Trebuchet MS", 14, weight=8, italic=False),
|
||||
)
|
||||
text.setDefaultTextColor(CONST.COLORS["red"])
|
||||
text.setPos(point.image_coordinates.x + 26,
|
||||
point.image_coordinates.y)
|
||||
text.setPos(point.image_coordinates.x + 26, point.image_coordinates.y)
|
||||
|
||||
# Set to True to visually debug _transform_point.
|
||||
draw_transformed = False
|
||||
@@ -1000,10 +1187,12 @@ class QLiberationMap(QGraphicsView):
|
||||
self.scene().addRect(
|
||||
QRectF(x, y, 25, 25),
|
||||
pen=CONST.COLORS["red"],
|
||||
brush=CONST.COLORS["red"])
|
||||
brush=CONST.COLORS["red"],
|
||||
)
|
||||
text = self.scene().addText(
|
||||
f"P{i}' = {x}, {y}",
|
||||
font=QFont("Trebuchet MS", 14, weight=8, italic=False))
|
||||
font=QFont("Trebuchet MS", 14, weight=8, italic=False),
|
||||
)
|
||||
text.setDefaultTextColor(CONST.COLORS["red"])
|
||||
text.setPos(x + 26, y)
|
||||
|
||||
@@ -1023,7 +1212,9 @@ class QLiberationMap(QGraphicsView):
|
||||
self.state = QLiberationMapState.MOVING_UNIT
|
||||
self.selected_cp = selected_cp
|
||||
position = self._transform_point(selected_cp.control_point.position)
|
||||
self.movement_line = QtWidgets.QGraphicsLineItem(QLineF(QPointF(*position), QPointF(*position)))
|
||||
self.movement_line = QtWidgets.QGraphicsLineItem(
|
||||
QLineF(QPointF(*position), QPointF(*position))
|
||||
)
|
||||
self.scene().addItem(self.movement_line)
|
||||
|
||||
def is_valid_ship_pos(self, scene_position: Point) -> bool:
|
||||
@@ -1043,7 +1234,8 @@ class QLiberationMap(QGraphicsView):
|
||||
if self.state == QLiberationMapState.MOVING_UNIT:
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
self.movement_line.setLine(
|
||||
QLineF(self.movement_line.line().p1(), event.scenePos()))
|
||||
QLineF(self.movement_line.line().p1(), event.scenePos())
|
||||
)
|
||||
|
||||
if self.is_valid_ship_pos(mouse_position):
|
||||
self.movement_line.setPen(CONST.COLORS["green"])
|
||||
@@ -1053,21 +1245,28 @@ class QLiberationMap(QGraphicsView):
|
||||
mouse_world_pos = self._scene_to_dcs_coords(mouse_position)
|
||||
if DisplayOptions.navmeshes.blue_navmesh:
|
||||
self.highlight_mouse_navmesh(
|
||||
self.scene(), self.game.blue_navmesh,
|
||||
self._scene_to_dcs_coords(mouse_position))
|
||||
self.scene(),
|
||||
self.game.blue_navmesh,
|
||||
self._scene_to_dcs_coords(mouse_position),
|
||||
)
|
||||
if DisplayOptions.path_debug.shortest_path:
|
||||
self.draw_shortest_path(self.scene(), self.game.blue_navmesh,
|
||||
mouse_world_pos, player=True)
|
||||
self.draw_shortest_path(
|
||||
self.scene(), self.game.blue_navmesh, mouse_world_pos, player=True
|
||||
)
|
||||
|
||||
if DisplayOptions.navmeshes.red_navmesh:
|
||||
self.highlight_mouse_navmesh(
|
||||
self.scene(), self.game.red_navmesh, mouse_world_pos)
|
||||
self.scene(), self.game.red_navmesh, mouse_world_pos
|
||||
)
|
||||
|
||||
debug_blue = DisplayOptions.path_debug_faction.blue
|
||||
if DisplayOptions.path_debug.shortest_path:
|
||||
self.draw_shortest_path(
|
||||
self.scene(), self.game.navmesh_for(player=debug_blue),
|
||||
mouse_world_pos, player=False)
|
||||
self.scene(),
|
||||
self.game.navmesh_for(player=debug_blue),
|
||||
mouse_world_pos,
|
||||
player=False,
|
||||
)
|
||||
elif not DisplayOptions.path_debug.hide:
|
||||
if DisplayOptions.path_debug.barcap:
|
||||
task = FlightType.BARCAP
|
||||
@@ -1080,10 +1279,10 @@ class QLiberationMap(QGraphicsView):
|
||||
elif DisplayOptions.path_debug.tarcap:
|
||||
task = FlightType.TARCAP
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unexpected value for DisplayOptions.path_debug")
|
||||
self.draw_test_flight_plan(self.scene(), task, mouse_world_pos,
|
||||
player=debug_blue)
|
||||
raise ValueError("Unexpected value for DisplayOptions.path_debug")
|
||||
self.draw_test_flight_plan(
|
||||
self.scene(), task, mouse_world_pos, player=debug_blue
|
||||
)
|
||||
|
||||
def sceneMousePressEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
if self.state == QLiberationMapState.MOVING_UNIT:
|
||||
@@ -1109,4 +1308,4 @@ class QLiberationMap(QGraphicsView):
|
||||
self.scene().removeItem(self.movement_line)
|
||||
except:
|
||||
pass
|
||||
self.selected_cp = None
|
||||
self.selected_cp = None
|
||||
|
||||
@@ -4,17 +4,18 @@ import qt_ui.uiconstants as CONST
|
||||
|
||||
|
||||
class QLiberationScene(QGraphicsScene):
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
item = self.addText("Go to \"File/New Game\" to setup a new campaign or go to \"File/Open\" to load an existing save game.",
|
||||
CONST.FONT_PRIMARY)
|
||||
item = self.addText(
|
||||
'Go to "File/New Game" to setup a new campaign or go to "File/Open" to load an existing save game.',
|
||||
CONST.FONT_PRIMARY,
|
||||
)
|
||||
item.setDefaultTextColor(CONST.COLORS["white"])
|
||||
|
||||
def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
super(QLiberationScene, self).mouseMoveEvent(event)
|
||||
self.parent().sceneMouseMovedEvent(event)
|
||||
|
||||
def mousePressEvent(self, event:QGraphicsSceneMouseEvent):
|
||||
def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
|
||||
super(QLiberationScene, self).mousePressEvent(event)
|
||||
self.parent().sceneMousePressEvent(event)
|
||||
|
||||
@@ -13,8 +13,16 @@ from ...windows.GameUpdateSignal import GameUpdateSignal
|
||||
|
||||
|
||||
class QMapControlPoint(QMapObject):
|
||||
def __init__(self, parent, x: float, y: float, w: float, h: float,
|
||||
control_point: ControlPoint, game_model: GameModel) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
x: float,
|
||||
y: float,
|
||||
w: float,
|
||||
h: float,
|
||||
control_point: ControlPoint,
|
||||
game_model: GameModel,
|
||||
) -> None:
|
||||
super().__init__(x, y, w, h, mission_target=control_point)
|
||||
self.game_model = game_model
|
||||
self.control_point = control_point
|
||||
@@ -22,8 +30,7 @@ class QMapControlPoint(QMapObject):
|
||||
self.setZValue(1)
|
||||
self.setToolTip(self.control_point.name)
|
||||
self.base_details_dialog: Optional[QBaseMenu2] = None
|
||||
self.capture_action = QAction(
|
||||
f"CHEAT: Capture {self.control_point.name}")
|
||||
self.capture_action = QAction(f"CHEAT: Capture {self.control_point.name}")
|
||||
self.capture_action.triggered.connect(self.cheat_capture)
|
||||
|
||||
self.move_action = QAction("Move")
|
||||
@@ -69,9 +76,7 @@ class QMapControlPoint(QMapObject):
|
||||
|
||||
def on_click(self) -> None:
|
||||
self.base_details_dialog = QBaseMenu2(
|
||||
self.window(),
|
||||
self.control_point,
|
||||
self.game_model
|
||||
self.window(), self.control_point, self.game_model
|
||||
)
|
||||
self.base_details_dialog.show()
|
||||
|
||||
@@ -89,7 +94,10 @@ class QMapControlPoint(QMapObject):
|
||||
return
|
||||
|
||||
for connected in self.control_point.connected_points:
|
||||
if connected.captured and self.game_model.game.settings.enable_base_capture_cheat:
|
||||
if (
|
||||
connected.captured
|
||||
and self.game_model.game.settings.enable_base_capture_cheat
|
||||
):
|
||||
menu.addAction(self.capture_action)
|
||||
break
|
||||
|
||||
@@ -105,7 +113,7 @@ class QMapControlPoint(QMapObject):
|
||||
def cancel_move(self):
|
||||
self.control_point.target_position = None
|
||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||
|
||||
|
||||
def open_new_package_dialog(self) -> None:
|
||||
"""Extends the default packagedialog to redirect to base menu for red air base."""
|
||||
is_navy = isinstance(self.control_point, NavalControlPoint)
|
||||
|
||||
@@ -9,17 +9,28 @@ from game import Game
|
||||
from game.data.building_data import FORTIFICATION_BUILDINGS
|
||||
from game.db import REWARDS
|
||||
from game.theater import ControlPoint, TheaterGroundObject
|
||||
from game.theater.theatergroundobject import MissileSiteGroundObject, CoastalSiteGroundObject
|
||||
from game.theater.theatergroundobject import (
|
||||
MissileSiteGroundObject,
|
||||
CoastalSiteGroundObject,
|
||||
)
|
||||
from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu
|
||||
from .QMapObject import QMapObject
|
||||
from ...displayoptions import DisplayOptions
|
||||
|
||||
|
||||
class QMapGroundObject(QMapObject):
|
||||
def __init__(self, parent, x: float, y: float, w: float, h: float,
|
||||
control_point: ControlPoint,
|
||||
ground_object: TheaterGroundObject, game: Game,
|
||||
buildings: Optional[List[TheaterGroundObject]] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
x: float,
|
||||
y: float,
|
||||
w: float,
|
||||
h: float,
|
||||
control_point: ControlPoint,
|
||||
ground_object: TheaterGroundObject,
|
||||
game: Game,
|
||||
buildings: Optional[List[TheaterGroundObject]] = None,
|
||||
) -> None:
|
||||
super().__init__(x, y, w, h, mission_target=ground_object)
|
||||
self.ground_object = ground_object
|
||||
self.control_point = control_point
|
||||
@@ -42,7 +53,7 @@ class QMapGroundObject(QMapObject):
|
||||
for g in self.ground_object.groups:
|
||||
for u in g.units:
|
||||
if u.type in units:
|
||||
units[u.type] = units[u.type]+1
|
||||
units[u.type] = units[u.type] + 1
|
||||
else:
|
||||
units[u.type] = 1
|
||||
|
||||
@@ -80,8 +91,12 @@ class QMapGroundObject(QMapObject):
|
||||
if isinstance(self.ground_object, CoastalSiteGroundObject):
|
||||
cat = "coastal"
|
||||
|
||||
rect = QRect(option.rect.x() + 2, option.rect.y(),
|
||||
option.rect.width() - 2, option.rect.height())
|
||||
rect = QRect(
|
||||
option.rect.x() + 2,
|
||||
option.rect.y(),
|
||||
option.rect.width() - 2,
|
||||
option.rect.height(),
|
||||
)
|
||||
|
||||
is_dead = self.ground_object.is_dead
|
||||
for building in self.buildings:
|
||||
@@ -98,7 +113,7 @@ class QMapGroundObject(QMapObject):
|
||||
if not is_dead and not self.control_point.captured:
|
||||
if cat == "aa" and not has_threat:
|
||||
painter.drawPixmap(rect, const.ICONS["nothreat" + enemy_icons])
|
||||
else:
|
||||
else:
|
||||
painter.drawPixmap(rect, const.ICONS[cat + enemy_icons])
|
||||
elif not is_dead:
|
||||
if cat == "aa" and not has_threat:
|
||||
@@ -130,13 +145,22 @@ class QMapGroundObject(QMapObject):
|
||||
units_dead += len(g.units_losts)
|
||||
|
||||
if units_dead + units_alive > 0:
|
||||
ratio = float(units_alive)/(float(units_dead) + float(units_alive))
|
||||
ratio = float(units_alive) / (float(units_dead) + float(units_alive))
|
||||
bar_height = ratio * option.rect.height()
|
||||
painter.fillRect(option.rect.x(), option.rect.y(), 2,
|
||||
option.rect.height(),
|
||||
QBrush(const.COLORS["dark_red"]))
|
||||
painter.fillRect(option.rect.x(), option.rect.y(), 2, bar_height,
|
||||
QBrush(const.COLORS["green"]))
|
||||
painter.fillRect(
|
||||
option.rect.x(),
|
||||
option.rect.y(),
|
||||
2,
|
||||
option.rect.height(),
|
||||
QBrush(const.COLORS["dark_red"]),
|
||||
)
|
||||
painter.fillRect(
|
||||
option.rect.x(),
|
||||
option.rect.y(),
|
||||
2,
|
||||
bar_height,
|
||||
QBrush(const.COLORS["green"]),
|
||||
)
|
||||
|
||||
def on_click(self) -> None:
|
||||
self.ground_object_dialog = QGroundObjectMenu(
|
||||
@@ -144,6 +168,6 @@ class QMapGroundObject(QMapObject):
|
||||
self.ground_object,
|
||||
self.buildings,
|
||||
self.control_point,
|
||||
self.game
|
||||
self.game,
|
||||
)
|
||||
self.ground_object_dialog.show()
|
||||
|
||||
@@ -23,8 +23,9 @@ class QMapObject(QGraphicsRectItem):
|
||||
change the mouse cursor on hover.
|
||||
"""
|
||||
|
||||
def __init__(self, x: float, y: float, w: float, h: float,
|
||||
mission_target: MissionTarget) -> None:
|
||||
def __init__(
|
||||
self, x: float, y: float, w: float, h: float, mission_target: MissionTarget
|
||||
) -> None:
|
||||
super().__init__(x, y, w, h)
|
||||
self.mission_target = mission_target
|
||||
self.new_package_dialog: Optional[QNewPackageDialog] = None
|
||||
|
||||
@@ -5,8 +5,7 @@ from qt_ui.widgets.floatspinners import TenthsSpinner
|
||||
|
||||
|
||||
class TenthsSpinSlider(QGridLayout):
|
||||
def __init__(self, label: str, minimum: int, maximum: int,
|
||||
initial: int) -> None:
|
||||
def __init__(self, label: str, minimum: int, maximum: int, initial: int) -> None:
|
||||
super().__init__()
|
||||
self.addWidget(QLabel(label), 0, 0)
|
||||
|
||||
|
||||
@@ -31,12 +31,3 @@ class QSeadTargetInfoView(QGroupBox):
|
||||
for r in self.sead_target_infos.radars:
|
||||
radar_list_model.appendRow(QStandardItem(r.type))
|
||||
self.radar_list.setModel(radar_list_model)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import random
|
||||
|
||||
from PySide2.QtGui import QStandardItemModel, QStandardItem
|
||||
from PySide2.QtWidgets import QGroupBox, QLabel, QWidget, QVBoxLayout, QListView, QAbstractItemView
|
||||
from PySide2.QtWidgets import (
|
||||
QGroupBox,
|
||||
QLabel,
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QListView,
|
||||
QAbstractItemView,
|
||||
)
|
||||
|
||||
from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import StrikeTargetInfo
|
||||
|
||||
@@ -16,7 +23,9 @@ class QStrikeTargetInfoView(QGroupBox):
|
||||
if strike_target_infos is None:
|
||||
strike_target_infos = StrikeTargetInfo()
|
||||
|
||||
super(QStrikeTargetInfoView, self).__init__("Target : " + strike_target_infos.name)
|
||||
super(QStrikeTargetInfoView, self).__init__(
|
||||
"Target : " + strike_target_infos.name
|
||||
)
|
||||
|
||||
self.strike_target_infos = strike_target_infos
|
||||
|
||||
@@ -24,14 +33,12 @@ class QStrikeTargetInfoView(QGroupBox):
|
||||
|
||||
self.init_ui()
|
||||
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(0)
|
||||
layout.addWidget(self.listView)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
def setTarget(self, target):
|
||||
|
||||
self.setTitle(target.name)
|
||||
@@ -40,36 +47,27 @@ class QStrikeTargetInfoView(QGroupBox):
|
||||
self.listView.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
|
||||
if len(self.strike_target_infos.units) > 0:
|
||||
dic = {}
|
||||
for u in self.strike_target_infos.units:
|
||||
if u.type in dic.keys():
|
||||
dic[u.type] = dic[u.type] + 1
|
||||
else:
|
||||
dic[u.type] = 1
|
||||
for k,v in dic.items():
|
||||
model.appendRow(QStandardItem(k + " x " + str(v)))
|
||||
print(k + " x " + str(v))
|
||||
dic = {}
|
||||
for u in self.strike_target_infos.units:
|
||||
if u.type in dic.keys():
|
||||
dic[u.type] = dic[u.type] + 1
|
||||
else:
|
||||
dic[u.type] = 1
|
||||
for k, v in dic.items():
|
||||
model.appendRow(QStandardItem(k + " x " + str(v)))
|
||||
print(k + " x " + str(v))
|
||||
else:
|
||||
dic = {}
|
||||
for b in self.strike_target_infos.buildings:
|
||||
id = b.dcs_identifier
|
||||
if b.is_dead:
|
||||
id = id + "[Destroyed]"
|
||||
if id in dic.keys():
|
||||
dic[id] = dic[id] + 1
|
||||
else:
|
||||
dic[id] = 1
|
||||
for k, v in dic.items():
|
||||
model.appendRow(QStandardItem(k + " x " + str(v)))
|
||||
print(k + " x " + str(v))
|
||||
dic = {}
|
||||
for b in self.strike_target_infos.buildings:
|
||||
id = b.dcs_identifier
|
||||
if b.is_dead:
|
||||
id = id + "[Destroyed]"
|
||||
if id in dic.keys():
|
||||
dic[id] = dic[id] + 1
|
||||
else:
|
||||
dic[id] = 1
|
||||
for k, v in dic.items():
|
||||
model.appendRow(QStandardItem(k + " x " + str(v)))
|
||||
print(k + " x " + str(v))
|
||||
|
||||
self.listView.setModel(model)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user