Compare commits

...

4 commits

Author SHA1 Message Date
Maxime Réaux
60fc88af75 catch settings exception 2026-02-13 16:12:43 +01:00
Maxime Réaux
a2b6c7c684 exceptions adding in closed elements 2026-02-13 15:44:28 +01:00
Maxime Réaux
88bd28e949 add forbidden exceptions on closed elements 2026-02-13 11:38:59 +01:00
Maxime Réaux
42eb625ef6 fix select round after closing 2026-02-13 08:01:26 +01:00
13 changed files with 379 additions and 165 deletions

View file

@ -3,11 +3,7 @@ from pathlib import Path
from PyQt6.QtWidgets import QMessageBox from PyQt6.QtWidgets import QMessageBox
from warchron.model.model import Model from warchron.model.model import Model
from warchron.model.exception import ( from warchron.model.exception import DomainError, RequiresConfirmation
DeletionForbidden,
DeletionRequiresConfirmation,
UpdateRequiresConfirmation,
)
from warchron.view.view import View from warchron.view.view import View
from warchron.constants import ItemType, RefreshScope from warchron.constants import ItemType, RefreshScope
from warchron.controller.navigation_controller import NavigationController from warchron.controller.navigation_controller import NavigationController
@ -35,8 +31,7 @@ class AppController:
self.navigation.refresh_wars_view() self.navigation.refresh_wars_view()
self.update_window_title() self.update_window_title()
self.view.on_tree_selection_changed = self.navigation.on_tree_selection_changed self.view.on_tree_selection_changed = self.navigation.on_tree_selection_changed
self.view.on_add_campaign = self.campaigns.add_campaign self.view.on_add_item = self.add_item
self.view.on_add_round = self.rounds.add_round
def __connect(self) -> None: def __connect(self) -> None:
self.view.actionExit.triggered.connect(self.view.close) self.view.actionExit.triggered.connect(self.view.close)
@ -45,20 +40,25 @@ class AppController:
self.view.actionSave.triggered.connect(self.save) self.view.actionSave.triggered.connect(self.save)
self.view.actionSave_as.triggered.connect(self.save_as) self.view.actionSave_as.triggered.connect(self.save_as)
self.view.actionAbout.triggered.connect(self.show_about) self.view.actionAbout.triggered.connect(self.show_about)
self.view.addPlayerBtn.clicked.connect(self.players.add_player) self.view.addPlayerBtn.clicked.connect(lambda: self.add_item(ItemType.PLAYER))
self.view.addWarBtn.clicked.connect(self.wars.add_war) self.view.addWarBtn.clicked.connect(lambda: self.add_item(ItemType.WAR))
self.view.majorValue.valueChanged.connect(self.wars.set_major_value) self.view.majorValue.valueChanged.connect(self.wars.set_major_value)
self.view.minorValue.valueChanged.connect(self.wars.set_minor_value) self.view.minorValue.valueChanged.connect(self.wars.set_minor_value)
self.view.influenceToken.toggled.connect(self.wars.set_influence_token) self.view.influenceToken.toggled.connect(self.wars.set_influence_token)
self.view.addObjectiveBtn.clicked.connect(self.wars.add_objective) self.view.addObjectiveBtn.clicked.connect(
self.view.addWarParticipantBtn.clicked.connect(self.wars.add_war_participant) lambda: self.add_item(ItemType.OBJECTIVE)
)
self.view.addWarParticipantBtn.clicked.connect(
lambda: self.add_item(ItemType.WAR_PARTICIPANT)
)
self.view.endWarBtn.clicked.connect(self.wars.close_war) self.view.endWarBtn.clicked.connect(self.wars.close_war)
self.view.addSectorBtn.clicked.connect(self.campaigns.add_sector) self.view.addSectorBtn.clicked.connect(lambda: self.add_item(ItemType.SECTOR))
self.view.addCampaignParticipantBtn.clicked.connect( self.view.addCampaignParticipantBtn.clicked.connect(
self.campaigns.add_campaign_participant lambda: self.add_item(ItemType.CAMPAIGN_PARTICIPANT)
) )
self.view.endCampaignBtn.clicked.connect(self.campaigns.close_campaign) self.view.endCampaignBtn.clicked.connect(self.campaigns.close_campaign)
self.view.endRoundBtn.clicked.connect(self.rounds.close_round) self.view.endRoundBtn.clicked.connect(self.rounds.close_round)
self.view.on_add_item = self.add_item
self.view.on_edit_item = self.edit_item self.view.on_edit_item = self.edit_item
self.view.on_delete_item = self.delete_item self.view.on_delete_item = self.delete_item
@ -165,6 +165,72 @@ class AppController:
# Command methods # Command methods
def add_item(self, item_type: str) -> None:
try:
if item_type == ItemType.PLAYER:
play = self.players.create_player()
if not play:
return
self.navigation.refresh(RefreshScope.PLAYERS_LIST)
elif item_type == ItemType.WAR:
war = self.wars.create_war()
if not war:
return
self.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war.id
)
elif item_type == ItemType.CAMPAIGN:
camp = self.campaigns.create_campaign()
if not camp:
return
self.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp.id
)
elif item_type == ItemType.OBJECTIVE:
obj = self.wars.create_objective()
if not obj:
return
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
elif item_type == ItemType.WAR_PARTICIPANT:
war_part = self.wars.create_war_participant()
if not war_part:
return
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
elif item_type == ItemType.SECTOR:
sect = self.campaigns.create_sector()
if not sect:
return
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
elif item_type == ItemType.CAMPAIGN_PARTICIPANT:
camp_part = self.campaigns.create_campaign_participant()
if not camp_part:
return
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
elif item_type == ItemType.ROUND:
rnd = self.rounds.create_round()
if not rnd:
return
self.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=rnd.id
)
self.is_dirty = True
except DomainError as e:
QMessageBox.warning(
self.view,
"Deletion forbidden",
str(e),
)
except RequiresConfirmation as e:
reply = QMessageBox.question(
self.view,
"Confirm update",
str(e),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
)
if reply == QMessageBox.StandardButton.Yes:
e.action()
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def edit_item(self, item_type: str, item_id: str) -> None: def edit_item(self, item_type: str, item_id: str) -> None:
try: try:
if item_type == ItemType.PLAYER: if item_type == ItemType.PLAYER:
@ -199,15 +265,21 @@ class AppController:
self.rounds.edit_round_battle(item_id) self.rounds.edit_round_battle(item_id)
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.is_dirty = True self.is_dirty = True
except UpdateRequiresConfirmation as e: except DomainError as e:
QMessageBox.warning(
self.view,
"Deletion forbidden",
str(e),
)
except RequiresConfirmation as e:
reply = QMessageBox.question( reply = QMessageBox.question(
self.view, self.view,
"Confirm update", "Confirm update",
e.message, str(e),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
e.apply_update() e.action()
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def delete_item(self, item_type: str, item_id: str) -> None: def delete_item(self, item_type: str, item_id: str) -> None:
@ -253,19 +325,19 @@ class AppController:
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp_id RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp_id
) )
self.is_dirty = True self.is_dirty = True
except DeletionForbidden as e: except DomainError as e:
QMessageBox.warning( QMessageBox.warning(
self.view, self.view,
"Deletion forbidden", "Deletion forbidden",
e.reason, str(e),
) )
except DeletionRequiresConfirmation as e: except RequiresConfirmation as e:
reply = QMessageBox.question( reply = QMessageBox.question(
self.view, self.view,
"Confirm deletion", "Confirm deletion",
e.message, str(e),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
) )
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
e.cleanup_action() e.action()
self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)

View file

@ -2,7 +2,7 @@ from typing import List, TYPE_CHECKING
from PyQt6.QtWidgets import QMessageBox, QDialog from PyQt6.QtWidgets import QMessageBox, QDialog
from warchron.constants import ItemType, RefreshScope from warchron.constants import RefreshScope
if TYPE_CHECKING: if TYPE_CHECKING:
from warchron.controller.app_controller import AppController from warchron.controller.app_controller import AppController
@ -13,6 +13,9 @@ from warchron.controller.dtos import (
SectorDTO, SectorDTO,
RoundDTO, RoundDTO,
) )
from warchron.model.campaign import Campaign
from warchron.model.campaign_participant import CampaignParticipant
from warchron.model.sector import Sector
from warchron.model.closure_service import ClosureService from warchron.model.closure_service import ClosureService
from warchron.view.campaign_dialog import CampaignDialog from warchron.view.campaign_dialog import CampaignDialog
from warchron.view.campaign_participant_dialog import CampaignParticipantDialog from warchron.view.campaign_participant_dialog import CampaignParticipantDialog
@ -68,9 +71,9 @@ class CampaignController:
return False return False
return True return True
def add_campaign(self) -> None: def create_campaign(self) -> Campaign | None:
if not self.app.navigation.selected_war_id: if not self.app.navigation.selected_war_id:
return return None
dialog = CampaignDialog( dialog = CampaignDialog(
self.app.view, self.app.view,
default_month=self.app.model.get_default_campaign_values( default_month=self.app.model.get_default_campaign_values(
@ -78,18 +81,18 @@ class CampaignController:
)["month"], )["month"],
) )
if dialog.exec() != QDialog.DialogCode.Accepted: if dialog.exec() != QDialog.DialogCode.Accepted:
return return None
name = dialog.get_campaign_name() name = dialog.get_campaign_name()
month = dialog.get_campaign_month() month = dialog.get_campaign_month()
if not self._validate_campaign_inputs(name, month): if not self._validate_campaign_inputs(name, month):
return return None
camp = self.app.model.add_campaign( return self.app.model.add_campaign(
self.app.navigation.selected_war_id, name, month self.app.navigation.selected_war_id, name, month
) )
self.app.is_dirty = True # self.app.is_dirty = True
self.app.navigation.refresh_and_select( # self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp.id # RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp.id
) # )
def edit_campaign(self, campaign_id: str) -> None: def edit_campaign(self, campaign_id: str) -> None:
camp = self.app.model.get_campaign(campaign_id) camp = self.app.model.get_campaign(campaign_id)
@ -128,9 +131,9 @@ class CampaignController:
# Campaign participant methods # Campaign participant methods
def add_campaign_participant(self) -> None: def create_campaign_participant(self) -> CampaignParticipant | None:
if not self.app.navigation.selected_campaign_id: if not self.app.navigation.selected_campaign_id:
return return None
participants = self.app.model.get_available_war_participants( participants = self.app.model.get_available_war_participants(
self.app.navigation.selected_campaign_id self.app.navigation.selected_campaign_id
) )
@ -140,17 +143,15 @@ class CampaignController:
] ]
dialog = CampaignParticipantDialog(self.app.view, participants=part_opts) dialog = CampaignParticipantDialog(self.app.view, participants=part_opts)
if dialog.exec() != QDialog.DialogCode.Accepted: if dialog.exec() != QDialog.DialogCode.Accepted:
return return None
player_id = dialog.get_player_id() player_id = dialog.get_player_id()
leader = dialog.get_participant_leader() leader = dialog.get_participant_leader()
theme = dialog.get_participant_theme() theme = dialog.get_participant_theme()
if not player_id: if not player_id:
return return None
self.app.model.add_campaign_participant( return self.app.model.add_campaign_participant(
self.app.navigation.selected_campaign_id, player_id, leader, theme self.app.navigation.selected_campaign_id, player_id, leader, theme
) )
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def edit_campaign_participant(self, participant_id: str) -> None: def edit_campaign_participant(self, participant_id: str) -> None:
camp_part = self.app.model.get_campaign_participant(participant_id) camp_part = self.app.model.get_campaign_participant(participant_id)
@ -191,9 +192,9 @@ class CampaignController:
# allow same objectives in different fields? # allow same objectives in different fields?
return True return True
def add_sector(self) -> None: def create_sector(self) -> Sector | None:
if not self.app.navigation.selected_campaign_id: if not self.app.navigation.selected_campaign_id:
return return None
war = self.app.model.get_war_by_campaign( war = self.app.model.get_war_by_campaign(
self.app.navigation.selected_campaign_id self.app.navigation.selected_campaign_id
) )
@ -212,7 +213,7 @@ class CampaignController:
self.app.view, default_name="", rounds=rnd_objs, objectives=obj_dtos self.app.view, default_name="", rounds=rnd_objs, objectives=obj_dtos
) )
if dialog.exec() != QDialog.DialogCode.Accepted: if dialog.exec() != QDialog.DialogCode.Accepted:
return return None
name = dialog.get_sector_name() name = dialog.get_sector_name()
round_id = dialog.get_round_id() round_id = dialog.get_round_id()
major_id = dialog.get_major_id() major_id = dialog.get_major_id()
@ -223,8 +224,8 @@ class CampaignController:
if not self._validate_sector_inputs( if not self._validate_sector_inputs(
name, round_id, major_id, minor_id, influence_id name, round_id, major_id, minor_id, influence_id
): ):
return return None
self.app.model.add_sector( return self.app.model.add_sector(
self.app.navigation.selected_campaign_id, self.app.navigation.selected_campaign_id,
name, name,
round_id, round_id,
@ -234,8 +235,6 @@ class CampaignController:
mission, mission,
description, description,
) )
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def edit_sector(self, sector_id: str) -> None: def edit_sector(self, sector_id: str) -> None:
sect = self.app.model.get_sector(sector_id) sect = self.app.model.get_sector(sector_id)

View file

@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
from PyQt6.QtWidgets import QMessageBox, QDialog from PyQt6.QtWidgets import QMessageBox, QDialog
from warchron.constants import RefreshScope from warchron.constants import RefreshScope
from warchron.model.player import Player
if TYPE_CHECKING: if TYPE_CHECKING:
from warchron.controller.app_controller import AppController from warchron.controller.app_controller import AppController
@ -21,16 +22,15 @@ class PlayerController:
return False return False
return True return True
def add_player(self) -> None: def create_player(self) -> Player | None:
dialog = PlayerDialog(self.app.view) dialog = PlayerDialog(self.app.view)
result = dialog.exec() # modal blocking dialog result = dialog.exec()
if result == QDialog.DialogCode.Accepted: if result != QDialog.DialogCode.Accepted:
name = dialog.get_player_name() return None
if not self._validate_player_inputs(name): name = dialog.get_player_name()
return if not self._validate_player_inputs(name):
self.app.model.add_player(name) return None
self.app.is_dirty = True return self.app.model.add_player(name)
self.app.navigation.refresh(RefreshScope.PLAYERS_LIST)
def edit_player(self, player_id: str) -> None: def edit_player(self, player_id: str) -> None:
play = self.app.model.get_player(player_id) play = self.app.model.get_player(player_id)

View file

@ -3,6 +3,7 @@ from typing import List, TYPE_CHECKING
from PyQt6.QtWidgets import QDialog, QMessageBox from PyQt6.QtWidgets import QDialog, QMessageBox
from warchron.constants import ItemType, RefreshScope, Icons, IconName from warchron.constants import ItemType, RefreshScope, Icons, IconName
from warchron.model.round import Round
if TYPE_CHECKING: if TYPE_CHECKING:
from warchron.controller.app_controller import AppController from warchron.controller.app_controller import AppController
@ -111,14 +112,11 @@ class RoundController:
self.app.view.display_round_battles(battles_for_display) self.app.view.display_round_battles(battles_for_display)
self.app.view.endRoundBtn.setEnabled(not rnd.is_over) self.app.view.endRoundBtn.setEnabled(not rnd.is_over)
def add_round(self) -> None: def create_round(self) -> Round | None:
if not self.app.navigation.selected_campaign_id: campaign_id = self.app.navigation.selected_campaign_id
return if not campaign_id:
rnd = self.app.model.add_round(self.app.navigation.selected_campaign_id) return None
self.app.is_dirty = True return self.app.model.add_round(campaign_id)
self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=rnd.id
)
def close_round(self) -> None: def close_round(self) -> None:
round_id = self.app.navigation.selected_round_id round_id = self.app.navigation.selected_round_id
@ -141,7 +139,9 @@ class RoundController:
return return
self.app.is_dirty = True self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS) self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
self.app.navigation.refresh(RefreshScope.WARS_TREE) self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=round_id
)
# Choice methods # Choice methods

View file

@ -2,7 +2,8 @@ from typing import List, TYPE_CHECKING
from PyQt6.QtWidgets import QMessageBox, QDialog from PyQt6.QtWidgets import QMessageBox, QDialog
from warchron.constants import ItemType, RefreshScope from warchron.constants import RefreshScope
from warchron.model.exception import DomainError
if TYPE_CHECKING: if TYPE_CHECKING:
from warchron.controller.app_controller import AppController from warchron.controller.app_controller import AppController
@ -11,6 +12,9 @@ from warchron.controller.dtos import (
WarParticipantDTO, WarParticipantDTO,
ObjectiveDTO, ObjectiveDTO,
) )
from warchron.model.war import War
from warchron.model.war_participant import WarParticipant
from warchron.model.objective import Objective
from warchron.model.closure_service import ClosureService from warchron.model.closure_service import ClosureService
from warchron.view.war_dialog import WarDialog from warchron.view.war_dialog import WarDialog
from warchron.view.objective_dialog import ObjectiveDialog from warchron.view.objective_dialog import ObjectiveDialog
@ -60,21 +64,18 @@ class WarController:
return False return False
return True return True
def add_war(self) -> None: def create_war(self) -> War | None:
dialog = WarDialog( dialog = WarDialog(
self.app.view, default_year=self.app.model.get_default_war_values()["year"] self.app.view, default_year=self.app.model.get_default_war_values()["year"]
) )
result = dialog.exec() # modal blocking dialog result = dialog.exec()
if result == QDialog.DialogCode.Accepted: if result != QDialog.DialogCode.Accepted:
name = dialog.get_war_name() return None
year = dialog.get_war_year() name = dialog.get_war_name()
if not self._validate_war_inputs(name, year): year = dialog.get_war_year()
return if not self._validate_war_inputs(name, year):
war = self.app.model.add_war(name, year) return None
self.app.is_dirty = True return self.app.model.add_war(name, year)
self.app.navigation.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war.id
)
def edit_war(self, war_id: str) -> None: def edit_war(self, war_id: str) -> None:
war = self.app.model.get_war(war_id) war = self.app.model.get_war(war_id)
@ -115,22 +116,46 @@ class WarController:
war_id = self.app.navigation.selected_war_id war_id = self.app.navigation.selected_war_id
if not war_id: if not war_id:
return return
self.app.model.set_major_value(war_id, value) try:
self.app.model.set_major_value(war_id, value)
except DomainError as e:
QMessageBox.warning(
self.app.view,
"Setting forbidden",
str(e),
)
self.app.is_dirty = True self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def set_minor_value(self, value: int) -> None: def set_minor_value(self, value: int) -> None:
war_id = self.app.navigation.selected_war_id war_id = self.app.navigation.selected_war_id
if not war_id: if not war_id:
return return
self.app.model.set_minor_value(war_id, value) try:
self.app.model.set_minor_value(war_id, value)
except DomainError as e:
QMessageBox.warning(
self.app.view,
"Setting forbidden",
str(e),
)
self.app.is_dirty = True self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def set_influence_token(self, checked: bool) -> None: def set_influence_token(self, checked: bool) -> None:
war_id = self.app.navigation.selected_war_id war_id = self.app.navigation.selected_war_id
if not war_id: if not war_id:
return return
self.app.model.set_influence_token(war_id, checked) try:
self.app.model.set_influence_token(war_id, checked)
except DomainError as e:
QMessageBox.warning(
self.app.view,
"Setting forbidden",
str(e),
)
self.app.is_dirty = True self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
# Objective methods # Objective methods
@ -142,21 +167,19 @@ class WarController:
return False return False
return True return True
def add_objective(self) -> None: def create_objective(self) -> Objective | None:
if not self.app.navigation.selected_war_id: if not self.app.navigation.selected_war_id:
return return None
dialog = ObjectiveDialog(self.app.view) dialog = ObjectiveDialog(self.app.view)
if dialog.exec() != QDialog.DialogCode.Accepted: if dialog.exec() != QDialog.DialogCode.Accepted:
return return None
name = dialog.get_objective_name() name = dialog.get_objective_name()
description = dialog.get_objective_description() description = dialog.get_objective_description()
if not self._validate_objective_inputs(name, description): if not self._validate_objective_inputs(name, description):
return return None
self.app.model.add_objective( return self.app.model.add_objective(
self.app.navigation.selected_war_id, name, description self.app.navigation.selected_war_id, name, description
) )
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def edit_objective(self, objective_id: str) -> None: def edit_objective(self, objective_id: str) -> None:
obj = self.app.model.get_objective(objective_id) obj = self.app.model.get_objective(objective_id)
@ -174,9 +197,9 @@ class WarController:
# War participant methods # War participant methods
def add_war_participant(self) -> None: def create_war_participant(self) -> WarParticipant | None:
if not self.app.navigation.selected_war_id: if not self.app.navigation.selected_war_id:
return return None
players = self.app.model.get_available_players( players = self.app.model.get_available_players(
self.app.navigation.selected_war_id self.app.navigation.selected_war_id
) )
@ -185,16 +208,14 @@ class WarController:
] ]
dialog = WarParticipantDialog(self.app.view, players=play_opts) dialog = WarParticipantDialog(self.app.view, players=play_opts)
if dialog.exec() != QDialog.DialogCode.Accepted: if dialog.exec() != QDialog.DialogCode.Accepted:
return return None
player_id = dialog.get_player_id() player_id = dialog.get_player_id()
faction = dialog.get_participant_faction() faction = dialog.get_participant_faction()
if not player_id: if not player_id:
return return None
self.app.model.add_war_participant( return self.app.model.add_war_participant(
self.app.navigation.selected_war_id, player_id, faction self.app.navigation.selected_war_id, player_id, faction
) )
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def edit_war_participant(self, participant_id: str) -> None: def edit_war_participant(self, participant_id: str) -> None:
war_part = self.app.model.get_war_participant(participant_id) war_part = self.app.model.get_war_participant(participant_id)

View file

@ -2,10 +2,7 @@ from __future__ import annotations
from uuid import uuid4 from uuid import uuid4
from typing import Any, Dict, List from typing import Any, Dict, List
from warchron.model.exception import ( from warchron.model.exception import ForbiddenOperation, RequiresConfirmation
DeletionRequiresConfirmation,
UpdateRequiresConfirmation,
)
from warchron.model.campaign_participant import CampaignParticipant from warchron.model.campaign_participant import CampaignParticipant
from warchron.model.sector import Sector from warchron.model.sector import Sector
from warchron.model.round import Round from warchron.model.round import Round
@ -78,6 +75,8 @@ class Campaign:
def add_campaign_participant( def add_campaign_participant(
self, war_participant_id: str, leader: str, theme: str self, war_participant_id: str, leader: str, theme: str
) -> CampaignParticipant: ) -> CampaignParticipant:
if self.is_over:
raise ForbiddenOperation("Can't add participant in a closed campaign.")
if self.has_war_participant(war_participant_id): if self.has_war_participant(war_participant_id):
raise ValueError("Player already registered in this campaign") raise ValueError("Player already registered in this campaign")
participant = CampaignParticipant( participant = CampaignParticipant(
@ -98,12 +97,16 @@ class Campaign:
def update_campaign_participant( def update_campaign_participant(
self, participant_id: str, *, leader: str, theme: str self, participant_id: str, *, leader: str, theme: str
) -> None: ) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update participant in a closed campaign.")
part = self.get_campaign_participant(participant_id) part = self.get_campaign_participant(participant_id)
# Can't change referred War.participant # Can't change referred War.participant
part.set_leader(leader) part.set_leader(leader)
part.set_theme(theme) part.set_theme(theme)
def remove_campaign_participant(self, participant_id: str) -> None: def remove_campaign_participant(self, participant_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove participant in a closed campaign.")
rounds_blocking: list[Round] = [] rounds_blocking: list[Round] = []
for rnd in self.rounds: for rnd in self.rounds:
if rnd.has_choice_with_participant( if rnd.has_choice_with_participant(
@ -123,13 +126,11 @@ class Campaign:
rounds_str = ", ".join( rounds_str = ", ".join(
str(self.get_round_index(rnd.id)) for rnd in rounds_blocking str(self.get_round_index(rnd.id)) for rnd in rounds_blocking
) )
raise DeletionRequiresConfirmation( raise RequiresConfirmation(
message=( f"This participant is used in round(s): {rounds_str}.\n"
f"This participant is used in round(s): {rounds_str}.\n" "Related choices and battles will be cleared.\n"
"Related choices and battles will be cleared.\n" "Do you want to continue?",
"Do you want to continue?" action=cleanup,
),
cleanup_action=cleanup,
) )
# Sector methods # Sector methods
@ -155,6 +156,8 @@ class Campaign:
mission: str | None, mission: str | None,
description: str | None, description: str | None,
) -> Sector: ) -> Sector:
if self.is_over:
raise ForbiddenOperation("Can't add sector in a closed campaign.")
sect = Sector( sect = Sector(
name, round_id, major_id, minor_id, influence_id, mission, description name, round_id, major_id, minor_id, influence_id, mission, description
) )
@ -184,6 +187,8 @@ class Campaign:
mission: str | None, mission: str | None,
description: str | None, description: str | None,
) -> None: ) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update sector in a closed campaign.")
sect = self.get_sector(sector_id) sect = self.get_sector(sector_id)
old_round_id = sect.round_id old_round_id = sect.round_id
@ -219,16 +224,16 @@ class Campaign:
rounds_str = ", ".join( rounds_str = ", ".join(
str(self.get_round_index(rnd.id)) for rnd in affected_rounds str(self.get_round_index(rnd.id)) for rnd in affected_rounds
) )
raise UpdateRequiresConfirmation( raise RequiresConfirmation(
message=( f"Changing the round of this sector will affect round(s): {rounds_str}."
f"Changing the round of this sector will affect round(s): {rounds_str}." "\nRelated battles and choices will be cleared.\n"
"\nRelated battles and choices will be cleared.\n" "Do you want to continue?",
"Do you want to continue?" action=cleanup_and_update,
),
apply_update=cleanup_and_update,
) )
def remove_sector(self, sector_id: str) -> None: def remove_sector(self, sector_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove sector in a closed campaign.")
rounds_blocking: list[Round] = [] rounds_blocking: list[Round] = []
for rnd in self.rounds: for rnd in self.rounds:
if rnd.has_battle_with_sector(sector_id) or rnd.has_choice_with_sector( if rnd.has_battle_with_sector(sector_id) or rnd.has_choice_with_sector(
@ -248,13 +253,11 @@ class Campaign:
rounds_str = ", ".join( rounds_str = ", ".join(
str(self.get_round_index(rnd.id)) for rnd in rounds_blocking str(self.get_round_index(rnd.id)) for rnd in rounds_blocking
) )
raise DeletionRequiresConfirmation( raise RequiresConfirmation(
message=( f"This sector is used in round(s): {rounds_str}.\n"
f"This sector is used in round(s): {rounds_str}.\n" "Battles and choices using this sector will be cleared.\n"
"Battles and choices using this sector will be cleared.\n" "Do you want to continue?",
"Do you want to continue?" action=cleanup,
),
cleanup_action=cleanup,
) )
def get_sectors_in_round(self, round_id: str) -> List[Sector]: def get_sectors_in_round(self, round_id: str) -> List[Sector]:
@ -266,6 +269,15 @@ class Campaign:
def has_round(self, round_id: str) -> bool: def has_round(self, round_id: str) -> bool:
return any(r.id == round_id for r in self.rounds) return any(r.id == round_id for r in self.rounds)
def has_finished_round(self) -> bool:
return any(r.is_over for r in self.rounds)
def has_finished_battle(self) -> bool:
return any(r.has_finished_battle() for r in self.rounds)
def all_rounds_finished(self) -> bool:
return all(r.is_over for r in self.rounds)
def get_round(self, round_id: str) -> Round: def get_round(self, round_id: str) -> Round:
for rnd in self.rounds: for rnd in self.rounds:
if rnd.id == round_id: if rnd.id == round_id:
@ -276,12 +288,21 @@ class Campaign:
return list(self.rounds) return list(self.rounds)
def add_round(self) -> Round: def add_round(self) -> Round:
if self.is_over:
raise ForbiddenOperation("Can't add round in a closed campaign.")
round = Round() round = Round()
self.rounds.append(round) self.rounds.append(round)
return round return round
def remove_round(self, round_id: str) -> None: def remove_round(self, round_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove round in a closed campaign.")
rnd = next((r for r in self.rounds if r.id == round_id), None) rnd = next((r for r in self.rounds if r.id == round_id), None)
if rnd:
if rnd.is_over:
raise ForbiddenOperation("Can't remove closed round.")
if rnd.has_finished_battle():
raise ForbiddenOperation("Can't remove round with finished battle(s).")
for sect in self.sectors.values(): for sect in self.sectors.values():
if sect.round_id == round_id: if sect.round_id == round_id:
sect.round_id = None sect.round_id = None
@ -321,9 +342,6 @@ class Campaign:
# Battle methods # Battle methods
def all_rounds_finished(self) -> bool:
return all(r.is_over for r in self.rounds)
def create_battle(self, round_id: str, sector_id: str) -> Battle: def create_battle(self, round_id: str, sector_id: str) -> Battle:
rnd = self.get_round(round_id) rnd = self.get_round(round_id)
return rnd.create_battle(sector_id) return rnd.create_battle(sector_id)

View file

@ -0,0 +1,19 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from warchron.model.war import War
from warchron.model.war import War
from warchron.model.war import War
from warchron.model.closure_service import ClosureService
class RoundClosureWorkflow:
def close_round(self, round_id):
rnd = repo.get_round(round_id)
ties = ClosureService.close_round(rnd)
repo.save()
return ties

View file

@ -1,31 +1,25 @@
from typing import Callable from typing import Callable
class DeletionForbidden(Exception): class DomainError(Exception):
def __init__(self, reason: str): """Base class for all domain rule violations."""
self.reason = reason
super().__init__(reason) pass
class DeletionRequiresConfirmation(Exception): class ForbiddenOperation(DomainError):
def __init__( """Generic 'you can't do this' rule."""
self,
message: str, pass
*,
cleanup_action: Callable[[], None],
): class DomainDecision(Exception):
self.message = message """Base class for domain actions requiring user decision."""
self.cleanup_action = cleanup_action
super().__init__(message) pass
class UpdateRequiresConfirmation(Exception): class RequiresConfirmation(DomainDecision):
def __init__( def __init__(self, message: str, action: Callable[[], None]):
self,
message: str,
*,
apply_update: Callable[[], None],
):
self.message = message
self.apply_update = apply_update
super().__init__(message) super().__init__(message)
self.action = action

View file

@ -4,7 +4,7 @@ import json
import shutil import shutil
from datetime import datetime from datetime import datetime
from warchron.model.exception import DeletionForbidden from warchron.model.exception import ForbiddenOperation
from warchron.model.player import Player from warchron.model.player import Player
from warchron.model.war import War from warchron.model.war import War
from warchron.model.war_participant import WarParticipant from warchron.model.war_participant import WarParticipant
@ -91,7 +91,7 @@ class Model:
wars_using_player.append(war.name) wars_using_player.append(war.name)
if wars_using_player: if wars_using_player:
wars_str = ", ".join(wars_using_player) wars_str = ", ".join(wars_using_player)
raise DeletionForbidden( raise ForbiddenOperation(
f"This player is participating in war(s): {wars_str}.\n" f"This player is participating in war(s): {wars_str}.\n"
"Remove it from participants first." "Remove it from participants first."
) )
@ -159,25 +159,42 @@ class Model:
def update_war(self, war_id: str, *, name: str, year: int) -> None: def update_war(self, war_id: str, *, name: str, year: int) -> None:
war = self.get_war(war_id) war = self.get_war(war_id)
if war.is_over:
raise ForbiddenOperation("Can't update a closed war.")
war.set_name(name) war.set_name(name)
war.set_year(year) war.set_year(year)
def set_major_value(self, war_id: str, value: int) -> None: def set_major_value(self, war_id: str, value: int) -> None:
war = self.get_war(war_id) war = self.get_war(war_id)
war.set_major(value) war.set_major_value(value)
def set_minor_value(self, war_id: str, value: int) -> None: def set_minor_value(self, war_id: str, value: int) -> None:
war = self.get_war(war_id) war = self.get_war(war_id)
war.set_minor(value) war.set_minor_value(value)
def set_influence_token(self, war_id: str, value: bool) -> None: def set_influence_token(self, war_id: str, value: bool) -> None:
war = self.get_war(war_id) war = self.get_war(war_id)
war.set_influence(value) war.set_influence_token(value)
def get_all_wars(self) -> List[War]: def get_all_wars(self) -> List[War]:
return list(self.wars.values()) return list(self.wars.values())
def remove_war(self, war_id: str) -> None: def remove_war(self, war_id: str) -> None:
war = self.wars[war_id]
if war:
if war.is_over:
raise ForbiddenOperation("Can't remove closed war.")
if war.has_finished_campaign():
raise ForbiddenOperation("Can't remove war with finished campaign(s).")
if war.has_finished_round():
raise ForbiddenOperation(
"Can't remove war with finished round(s) in campaign(s)."
)
if war.has_finished_battle():
raise ForbiddenOperation(
"Can't remove war with finished battle(s) in round(s) "
"in campaign(s)."
)
del self.wars[war_id] del self.wars[war_id]
# Objective methods # Objective methods

View file

@ -2,6 +2,7 @@ from __future__ import annotations
from uuid import uuid4 from uuid import uuid4
from typing import Any, Dict from typing import Any, Dict
from warchron.model.exception import ForbiddenOperation
from warchron.model.choice import Choice from warchron.model.choice import Choice
from warchron.model.battle import Battle from warchron.model.battle import Battle
@ -58,6 +59,8 @@ class Round:
) )
def create_choice(self, participant_id: str) -> Choice: def create_choice(self, participant_id: str) -> Choice:
if self.is_over:
raise ForbiddenOperation("Can't create choice in a closed round.")
if participant_id not in self.choices: if participant_id not in self.choices:
choice = Choice( choice = Choice(
participant_id=participant_id, participant_id=participant_id,
@ -74,6 +77,8 @@ class Round:
secondary_sector_id: str | None, secondary_sector_id: str | None,
comment: str | None, comment: str | None,
) -> None: ) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update choice in a closed round.")
choice = self.get_choice(participant_id) choice = self.get_choice(participant_id)
if choice: if choice:
choice.set_priority(priority_sector_id) choice.set_priority(priority_sector_id)
@ -88,6 +93,8 @@ class Round:
choice.secondary_sector_id = None choice.secondary_sector_id = None
def remove_choice(self, participant_id: str) -> None: def remove_choice(self, participant_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove choice in a closed round.")
del self.choices[participant_id] del self.choices[participant_id]
# Battle methods # Battle methods
@ -104,12 +111,17 @@ class Round:
for bat in self.battles.values() for bat in self.battles.values()
) )
def has_finished_battle(self) -> bool:
return any(b.is_finished() for b in self.battles.values())
def all_battles_finished(self) -> bool: def all_battles_finished(self) -> bool:
return all( return all(
b.winner_id is not None or b.is_draw() for b in self.battles.values() b.winner_id is not None or b.is_draw() for b in self.battles.values()
) )
def create_battle(self, sector_id: str) -> Battle: def create_battle(self, sector_id: str) -> Battle:
if self.is_over:
raise ForbiddenOperation("Can't create battle in a closed round.")
if sector_id not in self.battles: if sector_id not in self.battles:
battle = Battle(sector_id=sector_id, player_1_id=None, player_2_id=None) battle = Battle(sector_id=sector_id, player_1_id=None, player_2_id=None)
self.battles[sector_id] = battle self.battles[sector_id] = battle
@ -125,6 +137,8 @@ class Round:
victory_condition: str | None, victory_condition: str | None,
comment: str | None, comment: str | None,
) -> None: ) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update battle in a closed round.")
bat = self.get_battle(sector_id) bat = self.get_battle(sector_id)
if bat: if bat:
bat.set_player_1(player_1_id) bat.set_player_1(player_1_id)
@ -144,4 +158,9 @@ class Round:
battle.winner_id = None battle.winner_id = None
def remove_battle(self, sector_id: str) -> None: def remove_battle(self, sector_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove battle in a closed round.")
bat = self.battles[sector_id]
if bat and bat.is_finished():
raise ForbiddenOperation("Can't remove finished battle.")
del self.battles[sector_id] del self.battles[sector_id]

View file

@ -37,3 +37,9 @@ class ScoreService:
if sector.minor_objective_id: if sector.minor_objective_id:
totals[sector.minor_objective_id] += war.minor_value totals[sector.minor_objective_id] += war.minor_value
return totals return totals
# def compute_round_results(round)
# def compute_campaign_winner(campaign)
# def compute_war_winner(war)

View file

@ -3,7 +3,7 @@ from uuid import uuid4
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List from typing import Any, Dict, List
from warchron.model.exception import DeletionForbidden from warchron.model.exception import ForbiddenOperation
from warchron.model.war_participant import WarParticipant from warchron.model.war_participant import WarParticipant
from warchron.model.objective import Objective from warchron.model.objective import Objective
from warchron.model.campaign_participant import CampaignParticipant from warchron.model.campaign_participant import CampaignParticipant
@ -36,17 +36,23 @@ class War:
def set_year(self, new_year: int) -> None: def set_year(self, new_year: int) -> None:
self.year = new_year self.year = new_year
def set_major(self, new_value: int) -> None: def set_major_value(self, new_value: int) -> None:
if self.is_over:
raise ForbiddenOperation("Can't set major value of a closed war.")
if new_value < self.minor_value: if new_value < self.minor_value:
raise ValueError("major_value cannot be < minor_value") raise ValueError("Can' set major value < minor value")
self.major_value = new_value self.major_value = new_value
def set_minor(self, new_value: int) -> None: def set_minor_value(self, new_value: int) -> None:
if self.is_over:
raise ForbiddenOperation("Can't set minor value of a closed war.")
if new_value > self.major_value: if new_value > self.major_value:
raise ValueError("minor_value cannot be > major_value") raise ValueError("Can't set minor value > major value")
self.minor_value = new_value self.minor_value = new_value
def set_influence(self, new_state: bool) -> None: def set_influence_token(self, new_state: bool) -> None:
if self.is_over:
raise ForbiddenOperation("Can't set influence token of a closed war.")
self.influence_token = new_state self.influence_token = new_state
def set_state(self, new_state: bool) -> None: def set_state(self, new_state: bool) -> None:
@ -87,6 +93,8 @@ class War:
# Objective methods # Objective methods
def add_objective(self, name: str, description: str | None) -> Objective: def add_objective(self, name: str, description: str | None) -> Objective:
if self.is_over:
raise ForbiddenOperation("Can't add objective in a closed war.")
obj = Objective(name, description) obj = Objective(name, description)
self.objectives[obj.id] = obj self.objectives[obj.id] = obj
return obj return obj
@ -106,18 +114,22 @@ class War:
def update_objective( def update_objective(
self, objective_id: str, *, name: str, description: str | None self, objective_id: str, *, name: str, description: str | None
) -> None: ) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update objective in a closed war.")
obj = self.get_objective(objective_id) obj = self.get_objective(objective_id)
obj.set_name(name) obj.set_name(name)
obj.set_description(description) obj.set_description(description)
def remove_objective(self, objective_id: str) -> None: def remove_objective(self, objective_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove objective in a closed war.")
camp_using_obj: List[str] = [] camp_using_obj: List[str] = []
for camp in self.campaigns: for camp in self.campaigns:
if camp.has_sector_with_objective(objective_id): if camp.has_sector_with_objective(objective_id):
camp_using_obj.append(camp.name) camp_using_obj.append(camp.name)
if camp_using_obj: if camp_using_obj:
camps_str = ", ".join(camp_using_obj) camps_str = ", ".join(camp_using_obj)
raise DeletionForbidden( raise ForbiddenOperation(
f"This objective is used in campaign(s) sector(s): {camps_str}.\n" f"This objective is used in campaign(s) sector(s): {camps_str}.\n"
"Remove it from campaign(s) first." "Remove it from campaign(s) first."
) )
@ -135,6 +147,8 @@ class War:
return any(part.player_id == player_id for part in self.participants.values()) return any(part.player_id == player_id for part in self.participants.values())
def add_war_participant(self, player_id: str, faction: str) -> WarParticipant: def add_war_participant(self, player_id: str, faction: str) -> WarParticipant:
if self.is_over:
raise ForbiddenOperation("Can't add participant in a closed war.")
if self.has_player(player_id): if self.has_player(player_id):
raise ValueError("Player already registered in this war") raise ValueError("Player already registered in this war")
participant = WarParticipant(player_id=player_id, faction=faction) participant = WarParticipant(player_id=player_id, faction=faction)
@ -148,18 +162,22 @@ class War:
return list(self.participants.values()) return list(self.participants.values())
def update_war_participant(self, participant_id: str, *, faction: str) -> None: def update_war_participant(self, participant_id: str, *, faction: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update participant in a closed war.")
part = self.get_war_participant(participant_id) part = self.get_war_participant(participant_id)
# Can't change referred Model.players # Can't change referred Model.players
part.set_faction(faction) part.set_faction(faction)
def remove_war_participant(self, participant_id: str) -> None: def remove_war_participant(self, participant_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove participant in a closed war.")
camp_using_part: List[str] = [] camp_using_part: List[str] = []
for camp in self.campaigns: for camp in self.campaigns:
if camp.has_war_participant(participant_id): if camp.has_war_participant(participant_id):
camp_using_part.append(camp.name) camp_using_part.append(camp.name)
if camp_using_part: if camp_using_part:
camps_str = ", ".join(camp_using_part) camps_str = ", ".join(camp_using_part)
raise DeletionForbidden( raise ForbiddenOperation(
f"This war participant is used in campaign(s): {camps_str}.\n" f"This war participant is used in campaign(s): {camps_str}.\n"
"Remove it from campaign(s) first." "Remove it from campaign(s) first."
) )
@ -173,10 +191,21 @@ class War:
def get_default_campaign_values(self) -> Dict[str, Any]: def get_default_campaign_values(self) -> Dict[str, Any]:
return {"month": datetime.now().month} return {"month": datetime.now().month}
def has_finished_campaign(self) -> bool:
return any(c.is_over for c in self.campaigns)
def has_finished_round(self) -> bool:
return any(c.has_finished_round() for c in self.campaigns)
def has_finished_battle(self) -> bool:
return any(c.has_finished_battle() for c in self.campaigns)
def all_campaigns_finished(self) -> bool: def all_campaigns_finished(self) -> bool:
return all(c.is_over for c in self.campaigns) return all(c.is_over for c in self.campaigns)
def add_campaign(self, name: str, month: int | None = None) -> Campaign: def add_campaign(self, name: str, month: int | None = None) -> Campaign:
if self.is_over:
raise ForbiddenOperation("Can't add campaign in a closed war.")
if month is None: if month is None:
month = self.get_default_campaign_values()["month"] month = self.get_default_campaign_values()["month"]
campaign = Campaign(name, month) campaign = Campaign(name, month)
@ -212,7 +241,11 @@ class War:
raise KeyError(f"Participant {participant_id} not found in any Campaign") raise KeyError(f"Participant {participant_id} not found in any Campaign")
def update_campaign(self, campaign_id: str, *, name: str, month: int) -> None: def update_campaign(self, campaign_id: str, *, name: str, month: int) -> None:
if self.is_over:
raise ForbiddenOperation("Can't update campaign in a closed war.")
camp = self.get_campaign(campaign_id) camp = self.get_campaign(campaign_id)
if camp.is_over:
raise ForbiddenOperation("Can't update a closed campaign.")
camp.set_name(name) camp.set_name(name)
camp.set_month(month) camp.set_month(month)
@ -220,7 +253,20 @@ class War:
return list(self.campaigns) return list(self.campaigns)
def remove_campaign(self, campaign_id: str) -> None: def remove_campaign(self, campaign_id: str) -> None:
if self.is_over:
raise ForbiddenOperation("Can't remove campaign in a closed war.")
camp = self.get_campaign(campaign_id) camp = self.get_campaign(campaign_id)
if camp:
if camp.is_over:
raise ForbiddenOperation("Can't remove closed campaign.")
if camp.has_finished_round():
raise ForbiddenOperation(
"Can't remove campaign with finished round(s)."
)
if camp.has_finished_battle():
raise ForbiddenOperation(
"Can't remove campaign with finished battle(s) in round(s)."
)
self.campaigns.remove(camp) self.campaigns.remove(camp)
# Sector methods # Sector methods

View file

@ -41,8 +41,7 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
self.majorValue.setMinimum(0) self.majorValue.setMinimum(0)
self.minorValue.setMinimum(0) self.minorValue.setMinimum(0)
self.on_influence_token_changed: Callable[[int], None] | None = None self.on_influence_token_changed: Callable[[int], None] | None = None
self.on_add_campaign: Callable[[], None] | None = None self.on_add_item: Callable[[str], None] | None = None
self.on_add_round: Callable[[], None] | None = None
self.on_edit_item: Callable[[str, str], None] | None = None self.on_edit_item: Callable[[str, str], None] | None = None
self.on_delete_item: Callable[[str, str], None] | None = None self.on_delete_item: Callable[[str, str], None] | None = None
self.splitter.setSizes([200, 800]) self.splitter.setSizes([200, 800])
@ -202,12 +201,12 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
# Wars view # Wars view
def _on_add_campaign_clicked(self) -> None: def _on_add_campaign_clicked(self) -> None:
if self.on_add_campaign: if self.on_add_item:
self.on_add_campaign() self.on_add_item(ItemType.CAMPAIGN)
def _on_add_round_clicked(self) -> None: def _on_add_round_clicked(self) -> None:
if self.on_add_round: if self.on_add_item:
self.on_add_round() self.on_add_item(ItemType.ROUND)
def set_add_campaign_enabled(self, enabled: bool) -> None: def set_add_campaign_enabled(self, enabled: bool) -> None:
self.addCampaignBtn.setEnabled(enabled) self.addCampaignBtn.setEnabled(enabled)
@ -238,6 +237,10 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
def display_wars_tree(self, wars: List[WarDTO]) -> None: def display_wars_tree(self, wars: List[WarDTO]) -> None:
tree = self.warsTree tree = self.warsTree
try:
tree.currentItemChanged.disconnect()
except TypeError:
pass
tree.clear() tree.clear()
tree.setColumnCount(1) tree.setColumnCount(1)
tree.setHeaderLabels(["Wars"]) tree.setHeaderLabels(["Wars"])