Show player slots in the overview.

This commit is contained in:
Dan Albert 2020-10-09 18:23:43 -07:00
parent 2d8c8c63c9
commit 1fa18447e1
8 changed files with 177 additions and 23 deletions

View File

@ -58,7 +58,7 @@ class Dialog:
flight: Flight) -> None:
"""Opens the dialog to edit the given flight."""
cls.edit_flight_dialog = QEditFlightDialog(
cls.game_model.game,
cls.game_model,
package_model.package,
flight
)

View File

@ -95,6 +95,8 @@ class NullListModel(QAbstractListModel):
class PackageModel(QAbstractListModel):
"""The model for an ATO package."""
FlightRole = Qt.UserRole
#: Emitted when this package is being deleted from the ATO.
deleted = Signal()
@ -113,6 +115,8 @@ class PackageModel(QAbstractListModel):
return self.text_for_flight(flight)
if role == Qt.DecorationRole:
return self.icon_for_flight(flight)
elif role == PackageModel.FlightRole:
return flight
return None
@staticmethod
@ -185,6 +189,8 @@ class AtoModel(QAbstractListModel):
PackageRole = Qt.UserRole
client_slots_changed = Signal()
def __init__(self, game: Optional[Game], ato: AirTaskingOrder) -> None:
super().__init__()
self.game = game

View File

@ -1,4 +1,6 @@
"""A layout containing a widget with an associated label."""
from typing import Optional
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget
@ -10,8 +12,13 @@ class QLabeledWidget(QHBoxLayout):
label is used to name the input.
"""
def __init__(self, text: str, widget: QWidget) -> None:
def __init__(self, text: str, widget: QWidget,
tooltip: Optional[str]) -> None:
super().__init__()
self.addWidget(QLabel(text))
label = QLabel(text)
self.addWidget(label)
self.addStretch()
self.addWidget(widget, alignment=Qt.AlignRight)
if tooltip is not None:
label.setToolTip(tooltip)
widget.setToolTip(tooltip)

View File

@ -11,9 +11,11 @@ from PySide2.QtWidgets import (
import qt_ui.uiconstants as CONST
from game import Game
from game.event import CAP, CAS, FrontlineAttackEvent
from qt_ui.models import GameModel
from qt_ui.widgets.QBudgetBox import QBudgetBox
from qt_ui.widgets.QFactionsInfos import QFactionsInfos
from qt_ui.widgets.QTurnCounter import QTurnCounter
from qt_ui.widgets.clientslots import MaxPlayerCount
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QWaitingForMissionResultWindow import \
QWaitingForMissionResultWindow
@ -23,14 +25,18 @@ from qt_ui.windows.stats.QStatsWindow import QStatsWindow
class QTopPanel(QFrame):
def __init__(self, game: Game):
def __init__(self, game_model: GameModel):
super(QTopPanel, self).__init__()
self.game = game
self.game_model = game_model
self.setMaximumHeight(70)
self.init_ui()
GameUpdateSignal.get_instance().gameupdated.connect(self.setGame)
GameUpdateSignal.get_instance().budgetupdated.connect(self.budget_update)
@property
def game(self) -> Optional[Game]:
return self.game_model.game
def init_ui(self):
self.turnCounter = QTurnCounter()
@ -68,6 +74,8 @@ class QTopPanel(QFrame):
self.proceedBox = QGroupBox("Proceed")
self.proceedBoxLayout = QHBoxLayout()
self.proceedBoxLayout.addLayout(
MaxPlayerCount(self.game_model.ato_model))
self.proceedBoxLayout.addWidget(self.passTurnButton)
self.proceedBoxLayout.addWidget(self.proceedButton)
self.proceedBox.setLayout(self.proceedBoxLayout)
@ -84,16 +92,17 @@ class QTopPanel(QFrame):
self.setLayout(self.layout)
def setGame(self, game: Optional[Game]):
self.game = game
if game is not None:
self.turnCounter.setCurrentTurn(self.game.turn, self.game.current_day)
self.budgetBox.setGame(self.game)
self.factionsInfos.setGame(self.game)
if game is None:
return
if self.game and self.game.turn == 0:
self.proceedButton.setEnabled(False)
else:
self.proceedButton.setEnabled(True)
self.turnCounter.setCurrentTurn(self.game.turn, self.game.current_day)
self.budgetBox.setGame(self.game)
self.factionsInfos.setGame(self.game)
if self.game and self.game.turn == 0:
self.proceedButton.setEnabled(False)
else:
self.proceedButton.setEnabled(True)
def openSettings(self):
self.subwindow = QSettingsWindow(self.game)

View File

@ -10,7 +10,7 @@ from PySide2.QtCore import (
QSize,
Qt,
)
from PySide2.QtGui import QFont, QFontMetrics, QPainter
from PySide2.QtGui import QFont, QFontMetrics, QIcon, QPainter
from PySide2.QtWidgets import (
QAbstractItemView,
QGroupBox,
@ -18,15 +18,109 @@ from PySide2.QtWidgets import (
QListView,
QPushButton,
QSplitter,
QStyleOptionViewItem, QStyledItemDelegate, QVBoxLayout,
QStyle, QStyleOptionViewItem, QStyledItemDelegate, QVBoxLayout,
)
from game import db
from gen.ato import Package
from gen.flights.flight import Flight
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from ..models import AtoModel, GameModel, NullListModel, PackageModel
class FlightDelegate(QStyledItemDelegate):
FONT_SIZE = 10
HMARGIN = 4
VMARGIN = 4
def get_font(self, option: QStyleOptionViewItem) -> QFont:
font = QFont(option.font)
font.setPointSize(self.FONT_SIZE)
return font
@staticmethod
def flight(index: QModelIndex) -> Flight:
return index.data(PackageModel.FlightRole)
def first_row_text(self, index: QModelIndex) -> str:
flight = self.flight(index)
task = flight.flight_type.name
count = flight.count
name = db.unit_type_name(flight.unit_type)
delay = flight.scheduled_in
return f"[{task}] {count} x {name} in {delay} minutes"
def second_row_text(self, index: QModelIndex) -> str:
flight = self.flight(index)
origin = flight.from_cp.name
return f"From {origin}"
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)
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))
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}")
def num_clients(self, index: QModelIndex) -> int:
flight = self.flight(index)
return flight.client_count
@staticmethod
def icon_mode(option: QStyleOptionViewItem) -> QIcon.Mode:
if not (option.state & QStyle.State_Enabled):
return QIcon.Disabled
elif option.state & QStyle.State_Selected:
return QIcon.Selected
elif option.state & QStyle.State_Active:
return QIcon.Active
return QIcon.Normal
@staticmethod
def icon_state(option: QStyleOptionViewItem) -> QIcon.State:
return QIcon.On if option.state & QStyle.State_Open else QIcon.Off
@staticmethod
def icon_size(option: QStyleOptionViewItem) -> QSize:
icon_size: Optional[QSize] = option.decorationSize
if icon_size is None:
return QSize(0, 0)
else:
return icon_size
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)
class QFlightList(QListView):
"""List view for displaying the flights of a package."""
@ -34,6 +128,7 @@ class QFlightList(QListView):
super().__init__()
self.package_model = model
self.set_package(model)
self.setItemDelegate(FlightDelegate())
self.setIconSize(QSize(91, 24))
self.setSelectionBehavior(QAbstractItemView.SelectItems)
@ -206,6 +301,15 @@ class PackageDelegate(QStyledItemDelegate):
line2 = rect.adjusted(0, rect.height() / 2, 0, rect.height() / 2)
painter.drawText(line2, Qt.AlignLeft, self.right_text(index))
clients = self.num_clients(index)
if 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:
metrics = QFontMetrics(self.get_font(option))

View File

@ -0,0 +1,28 @@
"""Widgets for displaying client slots."""
from PySide2.QtWidgets import QLabel
from qt_ui.models import AtoModel
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
class MaxPlayerCount(QLabeledWidget):
def __init__(self, ato_model: AtoModel) -> None:
self.ato_model = ato_model
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.")
)
@property
def count_client_slots(self) -> int:
slots = 0
for package in self.ato_model.packages:
for flight in package.flights:
slots += flight.client_count
return slots
def update_count(self) -> None:
self.slots_label.setText(str(self.count_client_slots))

View File

@ -77,7 +77,7 @@ class QLiberationWindow(QMainWindow):
vbox = QVBoxLayout()
vbox.setMargin(0)
vbox.addWidget(QTopPanel(self.game))
vbox.addWidget(QTopPanel(self.game_model))
vbox.addWidget(hbox)
central_widget = QWidget()

View File

@ -4,9 +4,9 @@ from PySide2.QtWidgets import (
QVBoxLayout,
)
from game import Game
from gen.ato import Package
from gen.flights.flight import Flight
from qt_ui.models import GameModel
from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.mission.flight.QFlightPlanner import QFlightPlanner
@ -15,22 +15,22 @@ from qt_ui.windows.mission.flight.QFlightPlanner import QFlightPlanner
class QEditFlightDialog(QDialog):
"""Dialog window for editing flight plans and loadouts."""
def __init__(self, game: Game, package: Package, flight: Flight) -> None:
def __init__(self, game_model: GameModel, package: Package, flight: Flight) -> None:
super().__init__()
self.game = game
self.game_model = game_model
self.setWindowTitle("Create flight")
self.setWindowIcon(EVENT_ICONS["strike"])
layout = QVBoxLayout()
self.flight_planner = QFlightPlanner(package, flight, game)
self.flight_planner = QFlightPlanner(package, flight, game_model.game)
layout.addWidget(self.flight_planner)
self.setLayout(layout)
self.finished.connect(self.on_close)
@staticmethod
def on_close(_result) -> None:
def on_close(self, _result) -> None:
GameUpdateSignal.get_instance().redraw_flight_paths()
self.game_model.ato_model.client_slots_changed.emit()