diff --git a/example.json b/example.json index e94efdd..6dba8a4 100644 --- a/example.json +++ b/example.json @@ -30,7 +30,7 @@ "name": "Llael War 2025", "year": 2025, - "entrants": { + "participants": { "e7844fbb-8366-44e4-bb43-89b9eef6ef64": { "war_points": 0, "influence_tokens": 1 diff --git a/src/warchron/constants.py b/src/warchron/constants.py index f0a759f..1d431b6 100644 --- a/src/warchron/constants.py +++ b/src/warchron/constants.py @@ -1,14 +1,24 @@ from enum import StrEnum +from enum import Enum, auto from PyQt6.QtCore import Qt -# --- Tree / Table roles --- ROLE_TYPE = Qt.ItemDataRole.UserRole ROLE_ID = Qt.ItemDataRole.UserRole + 1 -# --- Domain object types --- class ItemType(StrEnum): PLAYER = "player" WAR = "war" CAMPAIGN = "campaign" ROUND = "round" + OBJECTIVE = "objective" + WAR_PARTICIPANT = "war_participant" + +class RefreshScope(Enum): + NONE = auto() + PLAYERS_LIST = auto() + WARS_TREE = auto() + WAR_DETAILS = auto() + CAMPAIGN_DETAILS = auto() + ROUND_DETAILS = auto() + diff --git a/src/warchron/controller/controller.py b/src/warchron/controller/controller.py index 6143a11..af71bbd 100644 --- a/src/warchron/controller/controller.py +++ b/src/warchron/controller/controller.py @@ -5,8 +5,8 @@ from PyQt6.QtWidgets import QMessageBox, QDialog from warchron.model.model import Model from warchron.view.view import View -from warchron.constants import ItemType -from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog +from warchron.constants import ItemType, RefreshScope +from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog, ObjectiveDialog, ParticipantDialog class Controller: def __init__(self, model: Model, view: View): @@ -35,6 +35,8 @@ class Controller: self.view.actionSave_as.triggered.connect(self.save_as) self.view.addPlayerBtn.clicked.connect(self.add_player) self.view.addWarBtn.clicked.connect(self.add_war) + self.view.addObjectiveBtn.clicked.connect(self.add_objective) + self.view.addWarParticipantBtn.clicked.connect(self.add_war_participant) self.view.on_edit_item = self.edit_item self.view.on_delete_item = self.delete_item @@ -51,6 +53,8 @@ class Controller: elif reply == QMessageBox.StandardButton.Cancel: return False return True + + # Menu bar methods def new(self): if self.is_dirty: @@ -106,6 +110,8 @@ class Controller: self.is_dirty = False self.update_window_title() +# Display methods + def update_window_title(self): base = "WarChron" if self.current_file: @@ -124,17 +130,17 @@ class Controller: wars = self.model.get_all_wars() self.view.display_wars_tree(wars) - def refresh_views(self): - current = self.view.get_current_tab() - if current == "players": - self.refresh_players_view() - elif current == "wars": - self.refresh_wars_view() - def _fill_war_details(self, war_id: str): war = self.model.get_war(war_id) - self.view.warName.setText(war.name) - self.view.warYear.setText(str(war.year)) + self.view.show_war_details(name=war.name, year=war.year) + objectives = war.get_all_objectives() + self.view.display_war_objectives(objectives) + participants = war.get_all_war_participants() + participants_for_display = [ + (self.model.get_player_name(p.id), p.faction, p.id) + for p in participants + ] + self.view.display_war_participants(participants_for_display) def _fill_campaign_details(self, campaign_id: str): camp = self.model.get_campaign(campaign_id) @@ -173,6 +179,118 @@ class Controller: self.view.set_add_campaign_enabled(self.selected_war_id is not None) self.view.set_add_round_enabled(self.selected_campaign_id is not None) + def refresh(self, scope: RefreshScope): + match scope: + case RefreshScope.PLAYERS_LIST: + self.refresh_players_view() + case RefreshScope.WARS_TREE: + self.refresh_wars_view() + case RefreshScope.WAR_DETAILS: + if self.selected_war_id: + self.view.show_details(ItemType.WAR) + self._fill_war_details(self.selected_war_id) + case RefreshScope.CAMPAIGN_DETAILS: + if self.selected_campaign_id: + self.view.show_details(ItemType.CAMPAIGN) + self._fill_campaign_details(self.selected_campaign_id) + case RefreshScope.ROUND_DETAILS: + if self.selected_round_id: + self.view.show_details(ItemType.ROUND) + self._fill_round_details(self.selected_round_id) + self.update_window_title() + +# Common command methods + + def refresh_and_select(self, scope: RefreshScope, *, item_type: ItemType, item_id: str): + self.refresh(scope) + self.view.select_tree_item(item_type=item_type, item_id=item_id) + + def edit_item(self, item_type: str, item_id: str): + if item_type == ItemType.PLAYER: + play = self.model.get_player(item_id) + dialog = PlayerDialog(self.view, default_name=play.name) + if dialog.exec() == QDialog.DialogCode.Accepted: + name = dialog.get_player_name() + if not self._validate_player_inputs(name): + return + self.model.update_player(item_id, name=name) + self.refresh(RefreshScope.PLAYERS_LIST) + elif item_type == ItemType.WAR: + war = self.model.get_war(item_id) + dialog = WarDialog(self.view, default_name=war.name, default_year=war.year) + if dialog.exec() == QDialog.DialogCode.Accepted: + name = dialog.get_war_name() + year = dialog.get_war_year() + if not self._validate_war_inputs(name, year): + return + self.model.update_war(item_id, name=name, year=year) + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war.id) + elif item_type == ItemType.CAMPAIGN: + camp = self.model.get_campaign(item_id) + dialog = CampaignDialog(self.view, default_name=camp.name, default_month=camp.month) + if dialog.exec() == QDialog.DialogCode.Accepted: + name = dialog.get_campaign_name() + month = dialog.get_campaign_month() + if not self._validate_campaign_inputs(name, month): + return + self.model.update_campaign(item_id, name=name, month=month) + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp.id) + elif item_type == ItemType.OBJECTIVE: + obj = self.model.get_objective(item_id) + dialog = ObjectiveDialog(self.view, default_name=obj.name, default_description=obj.description) + if dialog.exec() == QDialog.DialogCode.Accepted: + name = dialog.get_objective_name() + description = dialog.get_objective_description() + if not self._validate_objective_inputs(name, description): + return + self.model.update_objective(item_id, name=name, description=description) + self.refresh(RefreshScope.WAR_DETAILS) + elif item_type == ItemType.WAR_PARTICIPANT: + part = self.model.get_war_participant(item_id) + player = self.model.get_player(part.id) + dialog = ParticipantDialog(self.view, players=[player], default_player_id=part.id, default_faction=part.faction) + if dialog.exec() == QDialog.DialogCode.Accepted: + id = dialog.get_player_id() + faction = dialog.get_participant_faction() + self.model.update_war_participant(item_id, faction=faction) + self.refresh(RefreshScope.WAR_DETAILS) + self.is_dirty = True + + def delete_item(self, item_type: str, item_id: str): + reply = QMessageBox.question( + self.view, + "Confirm deletion", + "Are you sure you want to delete this item?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) + if reply != QMessageBox.StandardButton.Yes: + return + if item_type == ItemType.PLAYER: + self.model.remove_player(item_id) + self.refresh(RefreshScope.PLAYERS_LIST) + elif item_type == ItemType.WAR: + self.model.remove_war(item_id) + self.refresh(RefreshScope.WARS_TREE) + elif item_type == ItemType.CAMPAIGN: + war = self.model.get_war_by_campaign(item_id) + war_id = war.id + self.model.remove_campaign(item_id) + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id) + elif item_type == ItemType.OBJECTIVE: + self.model.remove_objective(item_id) + self.refresh(RefreshScope.WAR_DETAILS) + elif item_type == ItemType.WAR_PARTICIPANT: + self.model.remove_war_participant(item_id) + self.refresh(RefreshScope.WAR_DETAILS) + elif item_type == ItemType.ROUND: + camp = self.model.get_campaign_by_round(item_id) + camp_id = camp.id + self.model.remove_round(item_id) + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp_id) + self.is_dirty = True + +# Player methods + def _validate_player_inputs(self, name: str) -> bool: if not name.strip(): QMessageBox.warning( @@ -192,8 +310,9 @@ class Controller: return self.model.add_player(name) self.is_dirty = True - self.refresh_players_view() - self.update_window_title() + self.refresh(RefreshScope.PLAYERS_LIST) + +# War methods def _validate_war_inputs(self, name: str, year: int) -> bool: if not name.strip(): @@ -220,10 +339,54 @@ class Controller: year = dialog.get_war_year() if not self._validate_war_inputs(name, year): return - self.model.add_war(name, year) + war = self.model.add_war(name, year) self.is_dirty = True - self.refresh_wars_view() - self.update_window_title() + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war.id) + +# Objective methods + + def _validate_objective_inputs(self, name: str, description: int) -> bool: + if not name.strip(): + QMessageBox.warning( + self.view, + "Invalid name", + "Campaign name cannot be empty." + ) + return False + return True + + def add_objective(self): + if not self.selected_war_id: + return + dialog = ObjectiveDialog(self.view) + if dialog.exec() != QDialog.DialogCode.Accepted: + return + name = dialog.get_objective_name() + description = dialog.get_objective_description() + if not name: + return + self.model.add_objective(self.selected_war_id, name, description) + self.is_dirty = True + self.refresh(RefreshScope.WAR_DETAILS) + +# War participant methods + + def add_war_participant(self): + if not self.selected_war_id: + return + players = self.model.get_available_players(self.selected_war_id) + dialog = ParticipantDialog(self.view, players=players) + if dialog.exec() != QDialog.DialogCode.Accepted: + return + player_id = dialog.get_player_id() + faction = dialog.get_participant_faction() + if not player_id: + return + self.model.add_war_participant(self.selected_war_id, player_id, faction) + self.is_dirty = True + self.refresh(RefreshScope.WAR_DETAILS) + +# Campaign methods def _validate_campaign_inputs(self, name: str, month: int) -> bool: if not name.strip(): @@ -252,66 +415,15 @@ class Controller: month = dialog.get_campaign_month() if not self._validate_campaign_inputs(name, month): return - self.model.add_campaign(self.selected_war_id, name, month) + camp = self.model.add_campaign(self.selected_war_id, name, month) self.is_dirty = True - self.refresh_wars_view() - self.update_window_title() + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp.id) + +# Round methods def add_round(self): if not self.selected_campaign_id: return - self.model.add_round(self.selected_campaign_id) + rnd = self.model.add_round(self.selected_campaign_id) self.is_dirty = True - self.refresh_wars_view() - self.update_window_title() - - def edit_item(self, item_type: str, item_id: str): - if item_type == "player": - play = self.model.get_player(item_id) - dialog = PlayerDialog(self.view, default_name=play.name) - if dialog.exec() == QDialog.DialogCode.Accepted: - name = dialog.get_player_name() - if not self._validate_player_inputs(name): - return - self.model.update_player(item_id, name=name) - elif item_type == "war": - war = self.model.get_war(item_id) - dialog = WarDialog(self.view, default_name=war.name, default_year=war.year) - if dialog.exec() == QDialog.DialogCode.Accepted: - name = dialog.get_war_name() - year = dialog.get_war_year() - if not self._validate_war_inputs(name, year): - return - self.model.update_war(item_id, name=name, year=year) - elif item_type == "campaign": - camp = self.model.get_campaign(item_id) - dialog = CampaignDialog(self.view, default_name=camp.name, default_month=camp.month) - if dialog.exec() == QDialog.DialogCode.Accepted: - name = dialog.get_campaign_name() - month = dialog.get_campaign_month() - if not self._validate_campaign_inputs(name, month): - return - self.model.update_campaign(item_id, name=name, month=month) - self.is_dirty = True - self.refresh_views() - - def delete_item(self, item_type: str, item_id: str): - reply = QMessageBox.question( - self.view, - "Confirm deletion", - "Are you sure you want to delete this item?", - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No - ) - if reply != QMessageBox.StandardButton.Yes: - return - if item_type == "player": - self.model.remove_player(item_id) - elif item_type == "war": - self.model.remove_war(item_id) - elif item_type == "campaign": - self.model.remove_campaign(item_id) - elif item_type == "round": - self.model.remove_round(item_id) - self.is_dirty = True - self.refresh_views() - + self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=rnd.id) \ No newline at end of file diff --git a/src/warchron/model/campaign.py b/src/warchron/model/campaign.py index c0b2810..76c9d9b 100644 --- a/src/warchron/model/campaign.py +++ b/src/warchron/model/campaign.py @@ -1,27 +1,29 @@ +from __future__ import annotations from uuid import uuid4 from datetime import datetime from warchron.model.round import Round class Campaign: - def __init__(self, name, month): - self.id = str(uuid4()) - self.name = name - self.month = month - self.entrants = {} + def __init__(self, name: str, month: int): + self.id: str = str(uuid4()) + self.name: str = name + self.month: int = month + self.participants = {} + self.sectors = {} self.rounds = [] self.is_over = False - def set_id(self, new_id): + def set_id(self, new_id: str): self.id = new_id - def set_name(self, new_name): + def set_name(self, new_name: str): self.name = new_name - def set_month(self, new_month): + def set_month(self, new_month: int): self.month = new_month - def set_state(self, new_state): + def set_state(self, new_state: bool): self.is_over = new_state def toDict(self): @@ -29,7 +31,7 @@ class Campaign: "id" : self.id, "name" : self.name, "month" : self.month, - # "entrants" : self.entrants, + # "participants" : self.participants, "rounds": [rnd.toDict() for rnd in self.rounds], "is_over": self.is_over } @@ -38,13 +40,16 @@ class Campaign: def fromDict(data: dict): camp = Campaign(name=data["name"], month=data["month"]) camp.set_id(data["id"]) - # camp.entrants = data.get("entrants", {}) + # camp.participants = data.get("participants", {}) for rnd_data in data.get("rounds", []): camp.rounds.append(Round.fromDict(rnd_data)) camp.set_state(data.get("is_over", False)) return camp + + def has_round(self, round_id: str) -> bool: + return any(r.id == round_id for r in self.rounds) - def get_round(self, round_id) -> Round: + def get_round(self, round_id: str) -> Round: return self.rounds[round_id] def get_all_rounds(self) -> list[Round]: @@ -65,3 +70,19 @@ class Campaign: if rnd.id == round_id: return index raise KeyError("Round not found in campaign") + +class CampaignParticipant: + def __init__(self,war_participant_id: str, leader: str): + self.id: str = war_participant_id # ref to War.participants + self.leader: str = leader + self.victory_points = 0 + self.objective_points = {} + +class Sector: + def __init__(self, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str): + self.id: str = str(uuid4()) + self.name: str = name + self.major_objective_id: str = major_id + self.minor_objective_id: str = minor_id + self.influence_objective_id: str = influence_id + self.round_id: str = round_id diff --git a/src/warchron/model/model.py b/src/warchron/model/model.py index 6f70d2d..6f8c222 100644 --- a/src/warchron/model/model.py +++ b/src/warchron/model/model.py @@ -4,14 +4,16 @@ import shutil from datetime import datetime from warchron.model.player import Player -from warchron.model.war import War +from warchron.model.war import War, Objective, WarParticipant from warchron.model.campaign import Campaign from warchron.model.round import Round class Model: def __init__(self): - self.players = {} - self.wars = {} + self.players: dict[str, Player] = {} + self.wars: dict[str, War] = {} + +# File management methods def new(self): self.players.clear() @@ -53,6 +55,8 @@ class Model: with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) +# Player methods + def add_player(self, name): player = Player(name) self.players[player.id] = player @@ -60,17 +64,22 @@ class Model: def get_player(self, id): return self.players[id] - + + def get_player_name(self, player_id: str) -> str: + return self.players[player_id].name + def update_player(self, player_id: str, *, name: str): player = self.get_player(player_id) player.set_name(name) - def delete_player(self, id): - del self.players[id] - def get_all_players(self) -> list[Player]: return list(self.players.values()) + def remove_player(self, player_id: str): + del self.players[player_id] + +# War methods + def get_default_war_values(self) -> dict: return { "year": datetime.now().year @@ -91,6 +100,20 @@ class Model: return war raise KeyError(f"Campaign {campaign_id} not found in any War") + def get_war_by_objective(self, objective_id: str) -> War: + for war in self.wars.values(): + for obj in war.objectives.values(): + if obj.id == objective_id: + return war + raise KeyError(f"Objective {objective_id} not found in any War") + + def get_war_by_war_participant(self, participant_id: str) -> War: + for war in self.wars.values(): + for part in war.participants.values(): + if part.id == participant_id: + return war + raise KeyError(f"Participant {participant_id} not found in any War") + def update_war(self, war_id: str, *, name: str, year: int): war = self.get_war(war_id) war.set_name(name) @@ -99,6 +122,61 @@ class Model: def get_all_wars(self) -> list[War]: return list(self.wars.values()) + def remove_war(self, war_id: str): + del self.wars[war_id] + +# Objective methods + + def add_objective(self, war_id: str, name: str, description: str) -> Objective: + war = self.get_war(war_id) + return war.add_objective(name, description) + + def get_objective(self, objective_id) -> Objective: + for war in self.wars.values(): + for obj in war.objectives.values(): + if obj.id == objective_id: + return obj + raise KeyError("Objective not found") + + def update_objective(self, objective_id: str, *, name: str, description: str): + war = self.get_war_by_objective(objective_id) + war.update_objective(objective_id, name=name, description=description) + + def remove_objective(self, objective_id: str): + war = self.get_war_by_objective(objective_id) + war.remove_objective(objective_id) + +# War participant methods + + def get_available_players(self, war_id: str) -> list[Player]: + war = self.get_war(war_id) + return [ + player + for player in self.players.values() + if not war.has_participant(player.id) + ] + + def add_war_participant(self, war_id: str, player_id: str, faction: str) -> Objective: + war = self.get_war(war_id) + return war.add_war_participant(player_id, faction) + + def get_war_participant(self, participant_id) -> WarParticipant: + for war in self.wars.values(): + for part in war.participants.values(): + if part.id == participant_id: + return part + raise KeyError("Participant not found") + + def update_war_participant(self, participant_id: str, *, faction: str): + war = self.get_war_by_war_participant(participant_id) + war.update_war_participant(participant_id, faction=faction) + + def remove_war_participant(self, participant_id: str): + war = self.get_war_by_war_participant(participant_id) + war.remove_war_participant(participant_id) + +# Campaign methods + def get_default_campaign_values(self, war_id: str) -> dict: war = self.get_war(war_id) return war.get_default_campaign_values() @@ -125,6 +203,12 @@ class Model: war = self.get_war_by_campaign(campaign_id) war.update_campaign(campaign_id, name=name, month=month) + def remove_campaign(self, campaign_id: str): + war = self.get_war_by_campaign(campaign_id) + war.remove_campaign(campaign_id) + +# Round methods + def add_round(self, campaign_id: str) -> Round: campaign = self.get_campaign(campaign_id) return campaign.add_round() @@ -141,16 +225,6 @@ class Model: camp = self.get_campaign_by_round(round_id) return camp.get_round_index(round_id) - def remove_player(self, player_id: str): - del self.players[player_id] - - def remove_war(self, war_id: str): - del self.wars[war_id] - - def remove_campaign(self, campaign_id: str): - war = self.get_war_by_campaign(campaign_id) - war.remove_campaign(campaign_id) - def remove_round(self, round_id: str): camp = self.get_campaign_by_round(round_id) - camp.remove_round(round_id) \ No newline at end of file + camp.remove_round(round_id) diff --git a/src/warchron/model/round.py b/src/warchron/model/round.py index c80012f..a32d0d6 100644 --- a/src/warchron/model/round.py +++ b/src/warchron/model/round.py @@ -2,16 +2,16 @@ from uuid import uuid4 class Round: def __init__(self): - self.id = str(uuid4()) + self.id: str = str(uuid4()) self.sectors = {} self.choices = {} self.battles = {} - self.is_over = False + self.is_over: bool = False - def set_id(self, new_id): + def set_id(self, new_id: str): self.id = new_id - def set_state(self, new_state): + def set_state(self, new_state: bool): self.is_over = new_state def toDict(self): diff --git a/src/warchron/model/war.py b/src/warchron/model/war.py index 0633564..9e3b402 100644 --- a/src/warchron/model/war.py +++ b/src/warchron/model/war.py @@ -1,27 +1,30 @@ +from __future__ import annotations from uuid import uuid4 from datetime import datetime from warchron.model.campaign import Campaign -class War: - def __init__(self, name, year): - self.id = str(uuid4()) - self.name = name - self.year = year - self.entrants = {} - self.campaigns = [] - self.is_over = False - def set_id(self, new_id): +class War: + def __init__(self, name: str, year: int): + self.id: str = str(uuid4()) + self.name: str = name + self.year: int = year + self.participants: dict[str, WarParticipant] = {} + self.objectives: dict[str, Objective] = {} + self.campaigns = [] + self.is_over: bool = False + + def set_id(self, new_id: str): self.id = new_id - def set_name(self, new_name): + def set_name(self, new_name: str): self.name = new_name - def set_year(self, new_year): + def set_year(self, new_year: int): self.year = new_year - def set_state(self, new_state): + def set_state(self, new_state: bool): self.is_over = new_state def toDict(self): @@ -29,7 +32,7 @@ class War: "id" : self.id, "name" : self.name, "year" : self.year, - # "entrants" : self.entrants, + # "participants" : self.participants, "campaigns": [camp.toDict() for camp in self.campaigns], "is_over": self.is_over } @@ -38,12 +41,66 @@ class War: def fromDict(data: dict): war = War(name=data["name"], year=data["year"]) war.set_id(data["id"]) - # war.entrants = data.get("entrants", {}) + # war.participants = data.get("participants", {}) for camp_data in data.get("campaigns", []): war.campaigns.append(Campaign.fromDict(camp_data)) war.set_state(data.get("is_over", False)) return war +# Objective methods + + def add_objective(self, name: str, description: str) -> Objective: + objective = Objective(name, description) + self.objectives[objective.id] = objective + return objective + + def get_objective(self, id: str) -> Objective: + return self.objectives[id] + + def get_all_objectives(self) -> list[Objective]: + return list(self.objectives.values()) + + def update_objective(self, objective_id: str, *, name: str, description: str): + obj = self.get_objective(objective_id) + obj.set_name(name) + obj.set_description(description) + + def remove_objective(self, objective_id: str): + del self.objectives[objective_id] + +# War participant methods + + def get_all_war_participants_ids(self) -> set[str]: + return set(self.participants.keys()) + + def has_participant(self, player_id: str) -> bool: + return player_id in self.participants + + def add_war_participant(self, player_id: str, faction: str) -> WarParticipant: + if player_id in self.participants: + raise ValueError("Player already registered in this war") + participant = WarParticipant(player_id, faction) + self.participants[participant.id] = participant + return participant + + def get_war_participant(self, id: str) -> WarParticipant: + return self.participants[id] + + def get_all_war_participants(self) -> list[WarParticipant]: + return list(self.participants.values()) + + def update_war_participant(self, player_id: str, *, faction: str): + part = self.get_war_participant(player_id) + part.set_faction(faction) + + def remove_war_participant(self, player_id: str): + del self.participants[player_id] + +# Campaign methods + + def has_campaign(self, campaign_id: str) -> bool: + return any(c.id == campaign_id for c in self.campaigns) + def get_default_campaign_values(self) -> dict: return { "month": datetime.now().month @@ -56,7 +113,7 @@ class War: self.campaigns.append(campaign) return campaign - def get_campaign(self, campaign_id) -> Campaign: + def get_campaign(self, campaign_id: str) -> Campaign: for camp in self.campaigns: if camp.id == campaign_id: return camp @@ -79,4 +136,31 @@ class War: def remove_campaign(self, campaign_id: str): camp = self.get_campaign(campaign_id) - self.campaigns.remove(camp) \ No newline at end of file + self.campaigns.remove(camp) + +class Objective: + def __init__(self, name: str, description: str): + self.id: str = str(uuid4()) + self.name: str = name + self.description: str = description + + def set_id(self, new_id: str): + self.id = new_id + + def set_name(self, new_name: str): + self.name = new_name + + def set_description(self, new_description: str): + self.description = new_description + +class WarParticipant: + def __init__(self, player_id: str, faction: str): + self.id: str = player_id # ref to Model.players + self.faction: str = faction + + def set_id(self, new_id: str): + self.id = new_id + + def set_faction(self, new_faction: str): + self.faction = new_faction + diff --git a/src/warchron/view/ui/ui_main_window.py b/src/warchron/view/ui/ui_main_window.py index 7e4829e..5bfbf54 100644 --- a/src/warchron/view/ui/ui_main_window.py +++ b/src/warchron/view/ui/ui_main_window.py @@ -81,44 +81,50 @@ class Ui_MainWindow(object): font.setPointSize(12) self.warYear.setFont(font) self.warYear.setObjectName("warYear") - self.labelEntrants = QtWidgets.QLabel(parent=self.pageWar) - self.labelEntrants.setGeometry(QtCore.QRect(10, 150, 111, 16)) - self.labelEntrants.setObjectName("labelEntrants") - self.addEntrantBtn = QtWidgets.QPushButton(parent=self.pageWar) - self.addEntrantBtn.setGeometry(QtCore.QRect(420, 270, 81, 23)) - self.addEntrantBtn.setObjectName("addEntrantBtn") - self.playersTable_2 = QtWidgets.QTableWidget(parent=self.pageWar) - self.playersTable_2.setGeometry(QtCore.QRect(10, 170, 401, 211)) - self.playersTable_2.setObjectName("playersTable_2") - self.playersTable_2.setColumnCount(5) - self.playersTable_2.setRowCount(0) + self.labelParticipants = QtWidgets.QLabel(parent=self.pageWar) + self.labelParticipants.setGeometry(QtCore.QRect(10, 150, 111, 16)) + self.labelParticipants.setObjectName("labelParticipants") + self.addWarParticipantBtn = QtWidgets.QPushButton(parent=self.pageWar) + self.addWarParticipantBtn.setGeometry(QtCore.QRect(420, 270, 81, 23)) + self.addWarParticipantBtn.setObjectName("addWarParticipantBtn") + self.warParticipantsTable = QtWidgets.QTableWidget(parent=self.pageWar) + self.warParticipantsTable.setGeometry(QtCore.QRect(10, 170, 401, 211)) + self.warParticipantsTable.setObjectName("warParticipantsTable") + self.warParticipantsTable.setColumnCount(5) + self.warParticipantsTable.setRowCount(0) item = QtWidgets.QTableWidgetItem() - self.playersTable_2.setHorizontalHeaderItem(0, item) + self.warParticipantsTable.setHorizontalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_2.setHorizontalHeaderItem(1, item) + self.warParticipantsTable.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_2.setHorizontalHeaderItem(2, item) + self.warParticipantsTable.setHorizontalHeaderItem(2, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_2.setHorizontalHeaderItem(3, item) + self.warParticipantsTable.setHorizontalHeaderItem(3, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_2.setHorizontalHeaderItem(4, item) - self.addRoundBtn_8 = QtWidgets.QPushButton(parent=self.pageWar) - self.addRoundBtn_8.setEnabled(True) - self.addRoundBtn_8.setGeometry(QtCore.QRect(230, 400, 61, 23)) - self.addRoundBtn_8.setObjectName("addRoundBtn_8") - self.listObjectives = QtWidgets.QListWidget(parent=self.pageWar) - self.listObjectives.setGeometry(QtCore.QRect(10, 60, 161, 71)) - self.listObjectives.setObjectName("listObjectives") + self.warParticipantsTable.setHorizontalHeaderItem(4, item) + self.endWarBtn = QtWidgets.QPushButton(parent=self.pageWar) + self.endWarBtn.setEnabled(True) + self.endWarBtn.setGeometry(QtCore.QRect(230, 400, 61, 23)) + self.endWarBtn.setObjectName("endWarBtn") + self.objectivesTable = QtWidgets.QTableWidget(parent=self.pageWar) + self.objectivesTable.setGeometry(QtCore.QRect(10, 60, 401, 71)) + self.objectivesTable.setObjectName("objectivesTable") + self.objectivesTable.setColumnCount(2) + self.objectivesTable.setRowCount(0) + item = QtWidgets.QTableWidgetItem() + self.objectivesTable.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.objectivesTable.setHorizontalHeaderItem(1, item) self.labelObjectives = QtWidgets.QLabel(parent=self.pageWar) self.labelObjectives.setGeometry(QtCore.QRect(10, 40, 111, 20)) self.labelObjectives.setObjectName("labelObjectives") - self.addCampaignBtn_3 = QtWidgets.QPushButton(parent=self.pageWar) - self.addCampaignBtn_3.setEnabled(True) - self.addCampaignBtn_3.setGeometry(QtCore.QRect(180, 80, 91, 23)) + self.addObjectiveBtn = QtWidgets.QPushButton(parent=self.pageWar) + self.addObjectiveBtn.setEnabled(True) + self.addObjectiveBtn.setGeometry(QtCore.QRect(420, 80, 91, 23)) font = QtGui.QFont() font.setPointSize(10) - self.addCampaignBtn_3.setFont(font) - self.addCampaignBtn_3.setObjectName("addCampaignBtn_3") + self.addObjectiveBtn.setFont(font) + self.addObjectiveBtn.setObjectName("addObjectiveBtn") self.selectedDetailsStack.addWidget(self.pageWar) self.pageCampaign = QtWidgets.QWidget() self.pageCampaign.setObjectName("pageCampaign") @@ -128,49 +134,49 @@ class Ui_MainWindow(object): font.setPointSize(12) self.campaignName.setFont(font) self.campaignName.setObjectName("campaignName") - self.labelRound = QtWidgets.QLabel(parent=self.pageCampaign) - self.labelRound.setGeometry(QtCore.QRect(10, 40, 91, 16)) - self.labelRound.setObjectName("labelRound") - self.addEntrantBtn_2 = QtWidgets.QPushButton(parent=self.pageCampaign) - self.addEntrantBtn_2.setGeometry(QtCore.QRect(420, 270, 75, 23)) - self.addEntrantBtn_2.setObjectName("addEntrantBtn_2") + self.labelSectors = QtWidgets.QLabel(parent=self.pageCampaign) + self.labelSectors.setGeometry(QtCore.QRect(10, 40, 91, 16)) + self.labelSectors.setObjectName("labelSectors") + self.addCampaignParticipantBtn = QtWidgets.QPushButton(parent=self.pageCampaign) + self.addCampaignParticipantBtn.setGeometry(QtCore.QRect(420, 270, 75, 23)) + self.addCampaignParticipantBtn.setObjectName("addCampaignParticipantBtn") self.campaignMonth = QtWidgets.QLabel(parent=self.pageCampaign) self.campaignMonth.setGeometry(QtCore.QRect(280, 10, 121, 16)) font = QtGui.QFont() font.setPointSize(12) self.campaignMonth.setFont(font) self.campaignMonth.setObjectName("campaignMonth") - self.labelEntrants_2 = QtWidgets.QLabel(parent=self.pageCampaign) - self.labelEntrants_2.setGeometry(QtCore.QRect(10, 180, 47, 13)) - self.labelEntrants_2.setObjectName("labelEntrants_2") - self.playersTable_3 = QtWidgets.QTableWidget(parent=self.pageCampaign) - self.playersTable_3.setGeometry(QtCore.QRect(10, 200, 401, 181)) - self.playersTable_3.setObjectName("playersTable_3") - self.playersTable_3.setColumnCount(4) - self.playersTable_3.setRowCount(0) + self.labelParticipants_2 = QtWidgets.QLabel(parent=self.pageCampaign) + self.labelParticipants_2.setGeometry(QtCore.QRect(10, 180, 47, 13)) + self.labelParticipants_2.setObjectName("labelParticipants_2") + self.campaignParticipantsTable = QtWidgets.QTableWidget(parent=self.pageCampaign) + self.campaignParticipantsTable.setGeometry(QtCore.QRect(10, 200, 401, 181)) + self.campaignParticipantsTable.setObjectName("campaignParticipantsTable") + self.campaignParticipantsTable.setColumnCount(4) + self.campaignParticipantsTable.setRowCount(0) item = QtWidgets.QTableWidgetItem() - self.playersTable_3.setHorizontalHeaderItem(0, item) + self.campaignParticipantsTable.setHorizontalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_3.setHorizontalHeaderItem(1, item) + self.campaignParticipantsTable.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_3.setHorizontalHeaderItem(2, item) + self.campaignParticipantsTable.setHorizontalHeaderItem(2, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_3.setHorizontalHeaderItem(3, item) - self.playersTable_4 = QtWidgets.QTableWidget(parent=self.pageCampaign) - self.playersTable_4.setGeometry(QtCore.QRect(10, 60, 401, 101)) - self.playersTable_4.setObjectName("playersTable_4") - self.playersTable_4.setColumnCount(5) - self.playersTable_4.setRowCount(0) + self.campaignParticipantsTable.setHorizontalHeaderItem(3, item) + self.sectorsTable = QtWidgets.QTableWidget(parent=self.pageCampaign) + self.sectorsTable.setGeometry(QtCore.QRect(10, 60, 401, 101)) + self.sectorsTable.setObjectName("sectorsTable") + self.sectorsTable.setColumnCount(5) + self.sectorsTable.setRowCount(0) item = QtWidgets.QTableWidgetItem() - self.playersTable_4.setHorizontalHeaderItem(0, item) + self.sectorsTable.setHorizontalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_4.setHorizontalHeaderItem(1, item) + self.sectorsTable.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_4.setHorizontalHeaderItem(2, item) + self.sectorsTable.setHorizontalHeaderItem(2, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_4.setHorizontalHeaderItem(3, item) + self.sectorsTable.setHorizontalHeaderItem(3, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_4.setHorizontalHeaderItem(4, item) + self.sectorsTable.setHorizontalHeaderItem(4, item) self.addSectorBtn = QtWidgets.QPushButton(parent=self.pageCampaign) self.addSectorBtn.setEnabled(True) self.addSectorBtn.setGeometry(QtCore.QRect(420, 110, 71, 23)) @@ -202,32 +208,32 @@ class Ui_MainWindow(object): font.setPointSize(12) self.roundNb.setFont(font) self.roundNb.setObjectName("roundNb") - self.resolveBtn = QtWidgets.QPushButton(parent=self.pageRound) - self.resolveBtn.setEnabled(True) - self.resolveBtn.setGeometry(QtCore.QRect(320, 110, 91, 23)) - self.resolveBtn.setObjectName("resolveBtn") - self.playersTable_6 = QtWidgets.QTableWidget(parent=self.pageRound) - self.playersTable_6.setGeometry(QtCore.QRect(10, 240, 301, 111)) - self.playersTable_6.setObjectName("playersTable_6") - self.playersTable_6.setColumnCount(3) - self.playersTable_6.setRowCount(0) + self.resolvePairingBtn = QtWidgets.QPushButton(parent=self.pageRound) + self.resolvePairingBtn.setEnabled(True) + self.resolvePairingBtn.setGeometry(QtCore.QRect(320, 110, 91, 23)) + self.resolvePairingBtn.setObjectName("resolvePairingBtn") + self.battlesTable = QtWidgets.QTableWidget(parent=self.pageRound) + self.battlesTable.setGeometry(QtCore.QRect(10, 240, 301, 111)) + self.battlesTable.setObjectName("battlesTable") + self.battlesTable.setColumnCount(3) + self.battlesTable.setRowCount(0) item = QtWidgets.QTableWidgetItem() - self.playersTable_6.setHorizontalHeaderItem(0, item) + self.battlesTable.setHorizontalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_6.setHorizontalHeaderItem(1, item) + self.battlesTable.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - self.playersTable_6.setHorizontalHeaderItem(2, item) - self.resultBtn = QtWidgets.QPushButton(parent=self.pageRound) - self.resultBtn.setEnabled(True) - self.resultBtn.setGeometry(QtCore.QRect(320, 290, 91, 23)) - self.resultBtn.setObjectName("resultBtn") + self.battlesTable.setHorizontalHeaderItem(2, item) + self.enterResultBtn = QtWidgets.QPushButton(parent=self.pageRound) + self.enterResultBtn.setEnabled(True) + self.enterResultBtn.setGeometry(QtCore.QRect(320, 290, 91, 23)) + self.enterResultBtn.setObjectName("enterResultBtn") self.endRoundBtn = QtWidgets.QPushButton(parent=self.pageRound) self.endRoundBtn.setEnabled(True) self.endRoundBtn.setGeometry(QtCore.QRect(220, 400, 71, 23)) self.endRoundBtn.setObjectName("endRoundBtn") - self.labelChoices_2 = QtWidgets.QLabel(parent=self.pageRound) - self.labelChoices_2.setGeometry(QtCore.QRect(10, 220, 91, 16)) - self.labelChoices_2.setObjectName("labelChoices_2") + self.labelBattles = QtWidgets.QLabel(parent=self.pageRound) + self.labelBattles.setGeometry(QtCore.QRect(10, 220, 91, 16)) + self.labelBattles.setObjectName("labelBattles") self.selectedDetailsStack.addWidget(self.pageRound) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/swords-small.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) @@ -331,43 +337,47 @@ class Ui_MainWindow(object): self.labelSelect.setText(_translate("MainWindow", "Select an element within the tree to show/edit details.")) self.warName.setText(_translate("MainWindow", "warName")) self.warYear.setText(_translate("MainWindow", "warYear")) - self.labelEntrants.setText(_translate("MainWindow", "Entrants")) - self.addEntrantBtn.setText(_translate("MainWindow", "Add entrant")) - item = self.playersTable_2.horizontalHeaderItem(0) + self.labelParticipants.setText(_translate("MainWindow", "Participants")) + self.addWarParticipantBtn.setText(_translate("MainWindow", "Add participant")) + item = self.warParticipantsTable.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "Name")) - item = self.playersTable_2.horizontalHeaderItem(1) + item = self.warParticipantsTable.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Faction")) - item = self.playersTable_2.horizontalHeaderItem(2) + item = self.warParticipantsTable.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Campaigns")) - item = self.playersTable_2.horizontalHeaderItem(3) + item = self.warParticipantsTable.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Victory pts.")) - item = self.playersTable_2.horizontalHeaderItem(4) + item = self.warParticipantsTable.horizontalHeaderItem(4) item.setText(_translate("MainWindow", "Theme pts")) - self.addRoundBtn_8.setText(_translate("MainWindow", "End war")) + self.endWarBtn.setText(_translate("MainWindow", "End war")) + item = self.objectivesTable.horizontalHeaderItem(0) + item.setText(_translate("MainWindow", "Name")) + item = self.objectivesTable.horizontalHeaderItem(1) + item.setText(_translate("MainWindow", "Description")) self.labelObjectives.setText(_translate("MainWindow", "Objectives")) - self.addCampaignBtn_3.setText(_translate("MainWindow", "Add objective")) + self.addObjectiveBtn.setText(_translate("MainWindow", "Add objective")) self.campaignName.setText(_translate("MainWindow", "campaignName")) - self.labelRound.setText(_translate("MainWindow", "Sectors")) - self.addEntrantBtn_2.setText(_translate("MainWindow", "Add entrant")) + self.labelSectors.setText(_translate("MainWindow", "Sectors")) + self.addCampaignParticipantBtn.setText(_translate("MainWindow", "Add participant")) self.campaignMonth.setText(_translate("MainWindow", "campaignMonth")) - self.labelEntrants_2.setText(_translate("MainWindow", "Entrants")) - item = self.playersTable_3.horizontalHeaderItem(0) + self.labelParticipants_2.setText(_translate("MainWindow", "Participants")) + item = self.campaignParticipantsTable.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "Name")) - item = self.playersTable_3.horizontalHeaderItem(1) + item = self.campaignParticipantsTable.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Leader")) - item = self.playersTable_3.horizontalHeaderItem(2) + item = self.campaignParticipantsTable.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Victory pts.")) - item = self.playersTable_3.horizontalHeaderItem(3) + item = self.campaignParticipantsTable.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Theme pts.")) - item = self.playersTable_4.horizontalHeaderItem(0) + item = self.sectorsTable.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "Name")) - item = self.playersTable_4.horizontalHeaderItem(1) + item = self.sectorsTable.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Round")) - item = self.playersTable_4.horizontalHeaderItem(2) + item = self.sectorsTable.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Major obj.")) - item = self.playersTable_4.horizontalHeaderItem(3) + item = self.sectorsTable.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Minor opp.")) - item = self.playersTable_4.horizontalHeaderItem(4) + item = self.sectorsTable.horizontalHeaderItem(4) item.setText(_translate("MainWindow", "Influence imp.")) self.addSectorBtn.setText(_translate("MainWindow", "Add Sector")) self.endCampaignBtn.setText(_translate("MainWindow", "End campaign")) @@ -379,16 +389,16 @@ class Ui_MainWindow(object): item = self.choicesTable.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Secondary")) self.roundNb.setText(_translate("MainWindow", "Round Nb")) - self.resolveBtn.setText(_translate("MainWindow", "Resolve pairing")) - item = self.playersTable_6.horizontalHeaderItem(0) + self.resolvePairingBtn.setText(_translate("MainWindow", "Resolve pairing")) + item = self.battlesTable.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "Sector")) - item = self.playersTable_6.horizontalHeaderItem(1) + item = self.battlesTable.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Player 1")) - item = self.playersTable_6.horizontalHeaderItem(2) + item = self.battlesTable.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Player 2")) - self.resultBtn.setText(_translate("MainWindow", "Enter results")) + self.enterResultBtn.setText(_translate("MainWindow", "Enter results")) self.endRoundBtn.setText(_translate("MainWindow", "End round")) - self.labelChoices_2.setText(_translate("MainWindow", "Battles")) + self.labelBattles.setText(_translate("MainWindow", "Battles")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.warsTab), _translate("MainWindow", "Wars")) self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuEdit.setTitle(_translate("MainWindow", "Edit")) diff --git a/src/warchron/view/ui/ui_main_window.ui b/src/warchron/view/ui/ui_main_window.ui index 6949658..2cb8313 100644 --- a/src/warchron/view/ui/ui_main_window.ui +++ b/src/warchron/view/ui/ui_main_window.ui @@ -214,7 +214,7 @@ warYear - + 10 @@ -224,10 +224,10 @@ - Entrants + Participants - + 420 @@ -237,10 +237,10 @@ - Add entrant + Add participant - + 10 @@ -275,7 +275,7 @@ - + true @@ -291,15 +291,25 @@ End war - + 10 60 - 161 + 401 71 + + + Name + + + + + Description + + @@ -314,13 +324,13 @@ Objectives - + true - 180 + 420 80 91 23 @@ -355,7 +365,7 @@ campaignName - + 10 @@ -368,7 +378,7 @@ Sectors - + 420 @@ -378,7 +388,7 @@ - Add entrant + Add participant @@ -399,7 +409,7 @@ campaignMonth - + 10 @@ -409,10 +419,10 @@ - Entrants + Participants - + 10 @@ -442,7 +452,7 @@ - + 10 @@ -567,7 +577,7 @@ Round Nb - + true @@ -583,7 +593,7 @@ Resolve pairing - + 10 @@ -608,7 +618,7 @@ - + true @@ -640,7 +650,7 @@ End round - + 10 diff --git a/src/warchron/view/ui/ui_objective_dialog.py b/src/warchron/view/ui/ui_objective_dialog.py new file mode 100644 index 0000000..6892af6 --- /dev/null +++ b/src/warchron/view/ui/ui_objective_dialog.py @@ -0,0 +1,59 @@ +# Form implementation generated from reading ui file '.\src\warchron\view\ui\ui_objective_dialog.ui' +# +# Created by: PyQt6 UI code generator 6.7.1 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_objectiveDialog(object): + def setupUi(self, objectiveDialog): + objectiveDialog.setObjectName("objectiveDialog") + objectiveDialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) + objectiveDialog.resize(394, 148) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/warchron_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + objectiveDialog.setWindowIcon(icon) + self.gridLayout = QtWidgets.QGridLayout(objectiveDialog) + self.gridLayout.setObjectName("gridLayout") + self.label = QtWidgets.QLabel(parent=objectiveDialog) + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.objectiveName = QtWidgets.QLineEdit(parent=objectiveDialog) + self.objectiveName.setObjectName("objectiveName") + self.gridLayout.addWidget(self.objectiveName, 0, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(parent=objectiveDialog) + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) + self.objectiveDescription = QtWidgets.QTextEdit(parent=objectiveDialog) + self.objectiveDescription.setObjectName("objectiveDescription") + self.gridLayout.addWidget(self.objectiveDescription, 1, 1, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=objectiveDialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 2) + + self.retranslateUi(objectiveDialog) + self.buttonBox.accepted.connect(objectiveDialog.accept) # type: ignore + self.buttonBox.rejected.connect(objectiveDialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(objectiveDialog) + + def retranslateUi(self, objectiveDialog): + _translate = QtCore.QCoreApplication.translate + objectiveDialog.setWindowTitle(_translate("objectiveDialog", "Objective")) + self.label.setText(_translate("objectiveDialog", "Name")) + self.label_2.setText(_translate("objectiveDialog", "Description")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + objectiveDialog = QtWidgets.QDialog() + ui = Ui_objectiveDialog() + ui.setupUi(objectiveDialog) + objectiveDialog.show() + sys.exit(app.exec()) diff --git a/src/warchron/view/ui/ui_objective_dialog.ui b/src/warchron/view/ui/ui_objective_dialog.ui new file mode 100644 index 0000000..28b0d52 --- /dev/null +++ b/src/warchron/view/ui/ui_objective_dialog.ui @@ -0,0 +1,91 @@ + + + objectiveDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 394 + 148 + + + + Objective + + + + ../resources/warchron_logo.png../resources/warchron_logo.png + + + + + + Name + + + + + + + + + + Description + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + objectiveDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + objectiveDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/warchron/view/ui/ui_participant_dialog.py b/src/warchron/view/ui/ui_participant_dialog.py new file mode 100644 index 0000000..7e82342 --- /dev/null +++ b/src/warchron/view/ui/ui_participant_dialog.py @@ -0,0 +1,64 @@ +# Form implementation generated from reading ui file '.\src\warchron\view\ui\ui_participant_dialog.ui' +# +# Created by: PyQt6 UI code generator 6.7.1 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_participantDialog(object): + def setupUi(self, participantDialog): + participantDialog.setObjectName("participantDialog") + participantDialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) + participantDialog.resize(394, 148) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/warchron_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + participantDialog.setWindowIcon(icon) + self.gridLayout = QtWidgets.QGridLayout(participantDialog) + self.gridLayout.setObjectName("gridLayout") + self.label = QtWidgets.QLabel(parent=participantDialog) + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.playerComboBox = QtWidgets.QComboBox(parent=participantDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.playerComboBox.sizePolicy().hasHeightForWidth()) + self.playerComboBox.setSizePolicy(sizePolicy) + self.playerComboBox.setObjectName("playerComboBox") + self.gridLayout.addWidget(self.playerComboBox, 0, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(parent=participantDialog) + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) + self.faction = QtWidgets.QLineEdit(parent=participantDialog) + self.faction.setObjectName("faction") + self.gridLayout.addWidget(self.faction, 1, 1, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=participantDialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 2) + + self.retranslateUi(participantDialog) + self.buttonBox.accepted.connect(participantDialog.accept) # type: ignore + self.buttonBox.rejected.connect(participantDialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(participantDialog) + + def retranslateUi(self, participantDialog): + _translate = QtCore.QCoreApplication.translate + participantDialog.setWindowTitle(_translate("participantDialog", "Participant")) + self.label.setText(_translate("participantDialog", "Player")) + self.label_2.setText(_translate("participantDialog", "Faction")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + participantDialog = QtWidgets.QDialog() + ui = Ui_participantDialog() + ui.setupUi(participantDialog) + participantDialog.show() + sys.exit(app.exec()) diff --git a/src/warchron/view/ui/ui_participant_dialog.ui b/src/warchron/view/ui/ui_participant_dialog.ui new file mode 100644 index 0000000..238ea5e --- /dev/null +++ b/src/warchron/view/ui/ui_participant_dialog.ui @@ -0,0 +1,98 @@ + + + participantDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 394 + 148 + + + + Participant + + + + ../resources/warchron_logo.png../resources/warchron_logo.png + + + + + + Player + + + + + + + + 0 + 0 + + + + + + + + Faction + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + participantDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + participantDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/warchron/view/view.py b/src/warchron/view/view.py index a3bbd70..b5932e8 100644 --- a/src/warchron/view/view.py +++ b/src/warchron/view/view.py @@ -11,6 +11,8 @@ 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 +from warchron.view.ui.ui_objective_dialog import Ui_objectiveDialog +from warchron.view.ui.ui_participant_dialog import Ui_participantDialog class View(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): @@ -25,6 +27,10 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): self.show_details(None) self.playersTable.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.playersTable.customContextMenuRequested.connect(self._on_players_table_context_menu) + self.warParticipantsTable.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) + self.warParticipantsTable.customContextMenuRequested.connect(self._on_war_participants_table_context_menu) + self.objectivesTable.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) + self.objectivesTable.customContextMenuRequested.connect(self._on_objectives_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) @@ -41,46 +47,15 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): "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 get_current_tab(self) -> str: + index = self.tabWidget.currentIndex() + if index == 0: + return "players" + elif index == 1: + return "wars" + return "" - 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() +# General popups def closeEvent(self, event: QCloseEvent): if self.on_close_callback: @@ -108,6 +83,26 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): ) return Path(filename) if filename else None +# Players view + + def _on_players_table_context_menu(self, pos): + item = self.playersTable.itemAt(pos) + if not item: + return + row = item.row() + name_item = self.playersTable.item(row, 0) + if not name_item: + return + player_id = name_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 display_players(self, players: list): table = self.playersTable table.setRowCount(len(players)) @@ -117,8 +112,43 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): table.setItem(row, 0, play_item) table.resizeColumnsToContents() +# Wars view + + 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 set_add_campaign_enabled(self, enabled: bool): + self.addCampaignBtn.setEnabled(enabled) + + def set_add_round_enabled(self, enabled: bool): + self.addRoundBtn.setEnabled(enabled) + + 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 _format_war_label(self, war) -> str: - return f"{war.name} ({war.name} ({war.year}))" + return f"{war.name} ({war.year})" def _format_campaign_label(self, camp) -> str: return f"{camp.name} ({calendar.month_name[camp.month]})" @@ -149,26 +179,21 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): 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 select_tree_item(self, *, item_type: ItemType, item_id: str): + def walk(item: QTreeWidgetItem): + if ( + item.data(0, ROLE_TYPE) == item_type + and item.data(0, ROLE_ID) == item_id + ): + self.warsTree.setCurrentItem(item) + return True + for i in range(item.childCount()): + if walk(item.child(i)): + return True + return False + for i in range(self.warsTree.topLevelItemCount()): + if walk(self.warsTree.topLevelItem(i)): + return def get_selected_tree_item(self): item = self.warsTree.currentItem() @@ -179,20 +204,94 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): "id": item.data(0, ROLE_ID) } - def set_add_campaign_enabled(self, enabled: bool): - self.addCampaignBtn.setEnabled(enabled) + 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 set_add_round_enabled(self, enabled: bool): - self.addRoundBtn.setEnabled(enabled) +# War page - def get_current_tab(self) -> str: - index = self.tabWidget.currentIndex() - if index == 0: - return "players" - elif index == 1: - return "wars" - return "" - + def _on_objectives_table_context_menu(self, pos): + item = self.objectivesTable.itemAt(pos) + if not item: + return + row = item.row() + name_item = self.objectivesTable.item(row, 0) + if not name_item: + return + objective_id = name_item.data(Qt.ItemDataRole.UserRole) + menu = QMenu(self) + edit_action = menu.addAction("Edit") + delete_action = menu.addAction("Delete") + action = menu.exec(self.objectivesTable.viewport().mapToGlobal(pos)) + if action == edit_action and self.on_edit_item: + self.on_edit_item(ItemType.OBJECTIVE, objective_id) + elif action == delete_action and self.on_delete_item: + self.on_delete_item(ItemType.OBJECTIVE, objective_id) + + def _on_war_participants_table_context_menu(self, pos): + item = self.warParticipantsTable.itemAt(pos) + if not item: + return + row = item.row() + name_item = self.warParticipantsTable.item(row, 0) + if not name_item: + return + participant_id = name_item.data(Qt.ItemDataRole.UserRole) + menu = QMenu(self) + edit_action = menu.addAction("Edit") + delete_action = menu.addAction("Delete") + action = menu.exec(self.warParticipantsTable.viewport().mapToGlobal(pos)) + if action == edit_action and self.on_edit_item: + self.on_edit_item(ItemType.WAR_PARTICIPANT, participant_id) + elif action == delete_action and self.on_delete_item: + self.on_delete_item(ItemType.WAR_PARTICIPANT, participant_id) + + def show_war_details(self, *, name: str, year: int): + self.warName.setText(name) + self.warYear.setText(str(year)) + + def display_war_objectives(self, objectives: list): + table = self.objectivesTable + table.clearContents() + table.setRowCount(len(objectives)) + for row, obj in enumerate(objectives): + name_item = QtWidgets.QTableWidgetItem(obj.name) + desc_item = QtWidgets.QTableWidgetItem(obj.description) + name_item.setData(Qt.ItemDataRole.UserRole, obj.id) + table.setItem(row, 0, name_item) + table.setItem(row, 1, desc_item) + table.resizeColumnsToContents() + + def display_war_participants(self, participants: list[tuple[str, str, str]]): + table = self.warParticipantsTable + table.clearContents() + table.setRowCount(len(participants)) + for row, (name, faction, pid) in enumerate(participants): + name_item = QtWidgets.QTableWidgetItem(name) + fact_item = QtWidgets.QTableWidgetItem(faction) + name_item.setData(Qt.ItemDataRole.UserRole, pid) + table.setItem(row, 0, name_item) + table.setItem(row, 1, fact_item) + table.resizeColumnsToContents() + +# Campaign page + + def show_campaign_details(self, *, name: str, month: int): + self.campaignName.setText(name) + self.campaignMonth.setText(calendar.month_name[month]) + +# Round page + + def show_round_details(self, *, index: int): + self.roundNb.setText(f"Round {index}") + + class PlayerDialog(QDialog): def __init__(self, parent=None, *, default_name: str = ""): super().__init__(parent) @@ -231,4 +330,38 @@ class CampaignDialog(QDialog): return self.ui.campaignName.text().strip() def get_campaign_month(self) -> int: - return int(self.ui.campaignMonth.value()) \ No newline at end of file + return int(self.ui.campaignMonth.value()) + +class ObjectiveDialog(QDialog): + def __init__(self, parent=None, *, default_name="", default_description=""): + super().__init__(parent) + self.ui = Ui_objectiveDialog() + self.ui.setupUi(self) + self.ui.objectiveName.setText(default_name) + self.ui.objectiveDescription.setPlainText(default_description) + + def get_objective_name(self) -> str: + return self.ui.objectiveName.text().strip() + + def get_objective_description(self) -> str: + return self.ui.objectiveDescription.toPlainText().strip() + +class ParticipantDialog(QDialog): + def __init__(self, parent=None, *, players: list, default_player_id=None, default_faction="", editable_player=True): + super().__init__(parent) + self.ui = Ui_participantDialog() + self.ui.setupUi(self) + for player in players: + self.ui.playerComboBox.addItem(player.name, player.id) + if default_player_id: + index = self.ui.playerComboBox.findData(default_player_id) + if index != -1: + self.ui.playerComboBox.setCurrentIndex(index) + self.ui.playerComboBox.setEnabled(editable_player) + self.ui.faction.setText(default_faction) + + def get_player_id(self) -> str: + return self.ui.playerComboBox.currentData() + + def get_participant_faction(self) -> str: + return self.ui.faction.text().strip() \ No newline at end of file