from pathlib import Path import calendar from PyQt6 import QtWidgets from PyQt6.QtCore import Qt from PyQt6.QtWidgets import QDialog, QFileDialog, QTreeWidgetItem, QMenu from PyQt6.QtGui import QCloseEvent from warchron.constants import ROLE_TYPE, ROLE_ID, ItemType from warchron.view.ui.ui_main_window import Ui_MainWindow from warchron.view.ui.ui_player_dialog import Ui_playerDialog from warchron.view.ui.ui_war_dialog import Ui_warDialog from warchron.view.ui.ui_campaign_dialog import Ui_campaignDialog class View(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(View, self).__init__(parent) self.setupUi(self) self.on_close_callback = None self.on_selection_changed = None self.on_add_campaign = None self.on_add_round = None self.on_edit_item = None self.on_delete_item = None self.show_details(None) self.playersTable.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.playersTable.customContextMenuRequested.connect(self._on_players_table_context_menu) self.warsTree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.warsTree.customContextMenuRequested.connect(self._on_wars_tree_context_menu) self.addCampaignBtn.clicked.connect(self._on_add_campaign_clicked) self.addRoundBtn.clicked.connect(self._on_add_round_clicked) def _emit_selection_changed(self, current, previous): if not self.on_tree_selection_changed: return if not current: self.on_tree_selection_changed(None) return self.on_tree_selection_changed({ "type": current.data(0, ROLE_TYPE), "id": current.data(0, ROLE_ID), }) def _on_players_table_context_menu(self, pos): item = self.playersTable.itemAt(pos) if not item: return player_id = item.data(Qt.ItemDataRole.UserRole) menu = QMenu(self) edit_action = menu.addAction("Edit") delete_action = menu.addAction("Delete") action = menu.exec(self.playersTable.viewport().mapToGlobal(pos)) if action == edit_action and self.on_edit_item: self.on_edit_item(ItemType.PLAYER, player_id) elif action == delete_action and self.on_delete_item: self.on_delete_item(ItemType.PLAYER, player_id) def _on_wars_tree_context_menu(self, pos): item = self.warsTree.itemAt(pos) if not item: return item_type = item.data(0, ROLE_TYPE) item_id = item.data(0, ROLE_ID) menu = QMenu(self) edit_action = None if item_type != ItemType.ROUND: edit_action = menu.addAction("Edit") delete_action = menu.addAction("Delete") action = menu.exec(self.warsTree.viewport().mapToGlobal(pos)) if action == edit_action: if self.on_edit_item: self.on_edit_item(item_type, item_id) elif action == delete_action: if self.on_delete_item: self.on_delete_item(item_type, item_id) def _on_add_campaign_clicked(self): if self.on_add_campaign: self.on_add_campaign() def _on_add_round_clicked(self): if self.on_add_round: self.on_add_round() def closeEvent(self, event: QCloseEvent): if self.on_close_callback: proceed = self.on_close_callback() if not proceed: event.ignore() return event.accept() def ask_open_file(self) -> Path | None: filename, _ = QFileDialog.getOpenFileName( self, "Open war history", "", "WarChron files (*.json)" ) return Path(filename) if filename else None def ask_save_file(self) -> Path | None: filename, _ = QFileDialog.getSaveFileName( self, "Save war history", "", "WarChron files (*.json)" ) return Path(filename) if filename else None def display_players(self, players: list): table = self.playersTable table.setRowCount(len(players)) for row, player in enumerate(players): play_item = QtWidgets.QTableWidgetItem(player.name) play_item.setData(Qt.ItemDataRole.UserRole, player.id) table.setItem(row, 0, play_item) table.resizeColumnsToContents() def _format_war_label(self, war) -> str: return f"{war.name} ({war.name} ({war.year}))" def _format_campaign_label(self, camp) -> str: return f"{camp.name} ({calendar.month_name[camp.month]})" def _format_round_label(self, round, index: int) -> str: return f"Round {index}" def display_wars_tree(self, wars: list): tree = self.warsTree tree.clear() tree.setColumnCount(1) tree.setHeaderLabels(["Wars"]) for war in wars: war_item = QTreeWidgetItem([self._format_war_label(war)]) war_item.setData(0, ROLE_TYPE, ItemType.WAR) war_item.setData(0, ROLE_ID, war.id) tree.addTopLevelItem(war_item) for camp in war.get_all_campaigns(): camp_item = QTreeWidgetItem([self._format_campaign_label(camp)]) camp_item.setData(0, ROLE_TYPE, ItemType.CAMPAIGN) camp_item.setData(0, ROLE_ID, camp.id) war_item.addChild(camp_item) for index, rnd in enumerate(camp.get_all_rounds(), start=1): rnd_item = QTreeWidgetItem([self._format_round_label(rnd, index)]) rnd_item.setData(0, ROLE_TYPE, ItemType.ROUND) rnd_item.setData(0, ROLE_ID, rnd.id) camp_item.addChild(rnd_item) tree.currentItemChanged.connect(self._emit_selection_changed) tree.expandAll() def show_details(self, item_type: str | None): if item_type == ItemType.WAR: self.selectedDetailsStack.setCurrentWidget(self.pageWar) elif item_type == ItemType.CAMPAIGN: self.selectedDetailsStack.setCurrentWidget(self.pageCampaign) elif item_type == ItemType.ROUND: self.selectedDetailsStack.setCurrentWidget(self.pageRound) else: self.selectedDetailsStack.setCurrentWidget(self.pageEmpty) def show_war_details(self, *, name: str, year: int): self.warName.setText(name) self.warYear.setText(year) def show_campaign_details(self, *, name: str, month: int): self.campaignName.setText(name) self.campaignMonth.setText(calendar.month_name[month]) def show_round_details(self, *, index: int): self.roundNb.setText(f"Round {index}") def get_selected_tree_item(self): item = self.warsTree.currentItem() if not item: return None return { "type": item.data(0, ROLE_TYPE), "id": item.data(0, ROLE_ID) } def set_add_campaign_enabled(self, enabled: bool): self.addCampaignBtn.setEnabled(enabled) def set_add_round_enabled(self, enabled: bool): self.addRoundBtn.setEnabled(enabled) def get_current_tab(self) -> str: index = self.tabWidget.currentIndex() if index == 0: return "players" elif index == 1: return "wars" return "" class PlayerDialog(QDialog): def __init__(self, parent=None, *, default_name: str = ""): super().__init__(parent) self.ui = Ui_playerDialog() self.ui.setupUi(self) self.ui.playerName.setText(default_name) def get_player_name(self) -> str: return self.ui.playerName.text().strip() class WarDialog(QDialog): def __init__(self, parent=None, default_name: str = "", default_year: int | None = None): super().__init__(parent) self.ui = Ui_warDialog() self.ui.setupUi(self) self.ui.warName.setText(default_name) if default_year is not None: self.ui.warYear.setValue(default_year) def get_war_name(self) -> str: return self.ui.warName.text().strip() def get_war_year(self) -> int: return int(self.ui.warYear.value()) class CampaignDialog(QDialog): def __init__(self, parent=None, default_name: str = "", default_month: int | None = None): super().__init__(parent) self.ui = Ui_campaignDialog() self.ui.setupUi(self) self.ui.campaignName.setText(default_name) if default_month is not None: self.ui.campaignMonth.setValue(default_month) def get_campaign_name(self) -> str: return self.ui.campaignName.text().strip() def get_campaign_month(self) -> int: return int(self.ui.campaignMonth.value())