Compare commits

...

2 commits

Author SHA1 Message Date
Maxime Réaux
1218f32752 add campaign & round 2026-01-21 07:43:04 +01:00
Maxime Réaux
9e966baf9b war add & display 2026-01-20 08:46:58 +01:00
12 changed files with 580 additions and 46 deletions

View file

@ -1,31 +1,52 @@
from pathlib import Path from pathlib import Path
from PyQt6.QtWidgets import QMessageBox, QDialog from PyQt6.QtWidgets import QMessageBox, QDialog
from warchron.model.model import Model
from warchron.view.view import View
from warchron.view.view import PlayerDialog from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog
class Controller: class Controller:
def __init__(self, model, view): def __init__(self, model: Model, view: View):
self.model = model self.model = model
self.view = view self.view = view
self.current_file: Path | None = None self.current_file: Path | None = None
self.selected_war_id = None
self.selected_campaign_id = None
self.selected_round_id = None
self.view.on_close_callback = self.on_app_close self.view.on_close_callback = self.on_app_close
self.is_dirty = False self.is_dirty = False
self.__connect() self.__connect()
self.refresh_players_view() self.refresh_players_view()
self.refresh_wars_view()
self.update_window_title() self.update_window_title()
self.update_actions_state()
self.view.on_tree_selection_changed = self.on_tree_selection_changed
self.view.on_add_campaign = self.add_campaign
self.view.on_add_round = self.add_round
def __connect(self): def __connect(self):
self.view.addPlayerBtn.clicked.connect(self.add_player)
self.view.actionExit.triggered.connect(self.view.close) self.view.actionExit.triggered.connect(self.view.close)
self.view.actionNew.triggered.connect(self.new) self.view.actionNew.triggered.connect(self.new)
self.view.actionOpen.triggered.connect(self.open_file) self.view.actionOpen.triggered.connect(self.open_file)
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.addPlayerBtn.clicked.connect(self.add_player)
self.view.addWarBtn.clicked.connect(self.add_war)
def refresh_players_view(self): def on_app_close(self) -> bool:
players = self.model.get_all_players() if self.is_dirty:
self.view.display_players(players) reply = QMessageBox.question(
self.view,
"Unsaved changes",
"You have unsaved changes. Do you want to save before quitting?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel
)
if reply == QMessageBox.StandardButton.Yes:
self.save()
elif reply == QMessageBox.StandardButton.Cancel:
return False
return True
def new(self): def new(self):
if self.is_dirty: if self.is_dirty:
@ -41,6 +62,7 @@ class Controller:
self.current_file = None self.current_file = None
self.is_dirty = False self.is_dirty = False
self.refresh_players_view() self.refresh_players_view()
self.refresh_wars_view()
self.update_window_title() self.update_window_title()
def open_file(self): def open_file(self):
@ -60,22 +82,9 @@ class Controller:
self.current_file = path self.current_file = path
self.is_dirty = False self.is_dirty = False
self.refresh_players_view() self.refresh_players_view()
self.refresh_wars_view()
self.update_window_title() self.update_window_title()
def on_app_close(self) -> bool:
if self.is_dirty:
reply = QMessageBox.question(
self.view,
"Unsaved changes",
"You have unsaved changes. Do you want to save before quitting?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel
)
if reply == QMessageBox.StandardButton.Yes:
self.save()
elif reply == QMessageBox.StandardButton.Cancel:
return False
return True
def save(self): def save(self):
if not self.current_file: if not self.current_file:
self.save_as() self.save_as()
@ -103,6 +112,30 @@ class Controller:
base = base + " *" base = base + " *"
self.view.setWindowTitle(base) self.view.setWindowTitle(base)
def refresh_players_view(self):
players = self.model.get_all_players()
self.view.display_players(players)
def refresh_wars_view(self):
wars = self.model.get_all_wars()
self.view.display_wars(wars)
def on_tree_selection_changed(self, selection):
self.selected_war_id = None
self.selected_campaign_id = None
self.selected_round_id = None
if selection:
if selection["type"] == "war":
self.selected_war_id = selection["id"]
elif selection["type"] == "campaign":
self.selected_campaign_id = selection["id"]
elif selection["type"] == "round":
self.selected_round_id = selection["id"]
self.update_actions_state()
def update_actions_state(self):
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 add_player(self): def add_player(self):
dialog = PlayerDialog(self.view) dialog = PlayerDialog(self.view)
@ -120,3 +153,42 @@ class Controller:
self.is_dirty = True self.is_dirty = True
self.refresh_players_view() self.refresh_players_view()
self.update_window_title() self.update_window_title()
def add_war(self):
dialog = WarDialog(self.view)
result = dialog.exec() # modal blocking dialog
if result == QDialog.DialogCode.Accepted:
name = dialog.get_war_name()
if not name:
QMessageBox.warning(
self.view,
"Invalid name",
"War name cannot be empty."
)
return
self.model.add_war(name)
self.is_dirty = True
self.refresh_wars_view()
self.update_window_title()
def add_campaign(self):
if not self.selected_war_id:
return
dialog = CampaignDialog(self.view)
if dialog.exec() != QDialog.DialogCode.Accepted:
return
name = dialog.get_campaign_name()
if not name:
return
self.model.add_campaign(self.selected_war_id, name)
self.is_dirty = True
self.refresh_wars_view()
self.update_window_title()
def add_round(self):
if not self.selected_campaign_id:
return
self.model.add_round(self.selected_campaign_id)
self.is_dirty = True
self.refresh_wars_view()
self.update_window_title()

View file

@ -9,7 +9,7 @@ class Campaign:
self.name = name self.name = name
self.month = datetime.now().month self.month = datetime.now().month
self.entrants = {} self.entrants = {}
self.rounds = {} self.rounds = []
self.is_over = False self.is_over = False
def set_id(self, new_id): def set_id(self, new_id):
@ -43,3 +43,19 @@ class Campaign:
## rounds placeholder ## rounds placeholder
tmp.set_state(is_over) tmp.set_state(is_over)
return tmp return tmp
def add_round(self, number: int) -> Round:
round = Round()
self.rounds.append(round)
return round
def get_round(self, round_id) -> Round:
return self.rounds[round_id]
def get_all_rounds(self) -> list[Round]:
return list(self.rounds)
def add_round(self) -> Round:
round = Round()
self.rounds.append(round)
return round

View file

@ -4,6 +4,8 @@ import shutil
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.campaign import Campaign
from warchron.model.round import Round
class Model: class Model:
def __init__(self): def __init__(self):
@ -69,13 +71,36 @@ class Model:
def get_all_players(self) -> list[Player]: def get_all_players(self) -> list[Player]:
return list(self.players.values()) return list(self.players.values())
def add_war(self, name): def add_war(self, name) -> War:
war = War(name) war = War(name)
self.wars[war.id] = war self.wars[war.id] = war
return war return war
def get_war(self, id): def get_war(self, id) -> War:
return self.wars[id] return self.wars[id]
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 add_campaign(self, war_id: str, name: str) -> Campaign:
war = self.get_war(war_id)
return war.add_campaign(name)
def get_campaign(self, campaign_id) -> Campaign:
for war in self.wars.values():
for campaign in war.campaigns:
if campaign.id == campaign_id:
return campaign
raise KeyError("Campaign not found")
def add_round(self, campaign_id: str) -> Round:
campaign = self.get_campaign(campaign_id)
return campaign.add_round()
def get_round(self, round_id: str) -> Round:
for war in self.wars.values():
for campaign in war.campaigns:
for rnd in campaign.rounds:
if rnd.id == round_id:
return rnd
raise KeyError("Round not found")

View file

@ -1,20 +1,21 @@
from uuid import uuid4
class Round: class Round:
def __init__(self, number): def __init__(self):
self.number = number self.id = str(uuid4())
self.sectors = {} self.sectors = {}
self.choices = {} self.choices = {}
self.battles = {} self.battles = {}
self.is_over = False self.is_over = False
def set_number(self, new_number): def set_id(self, new_id):
self.number = new_number self.id = new_id
def set_state(self, new_state): def set_state(self, new_state):
self.is_over = new_state self.is_over = new_state
def toDict(self): def toDict(self):
return { return {
"number" : self.number,
"sectors" : self.sectors, "sectors" : self.sectors,
"choices" : self.choices, "choices" : self.choices,
"battles" : self.battles, "battles" : self.battles,
@ -22,8 +23,9 @@ class Round:
} }
@staticmethod @staticmethod
def fromDict(id, number, sectors, choices, battles, is_over): def fromDict(id, sectors, choices, battles, is_over):
tmp = Round(number=number) tmp = Round()
tmp.set_id(id)
## sectors placeholder ## sectors placeholder
## choices placeholder ## choices placeholder
## battles placeholder ## battles placeholder

View file

@ -9,10 +9,9 @@ class War:
self.name = name self.name = name
self.year = datetime.now().year self.year = datetime.now().year
self.entrants = {} self.entrants = {}
self.campaigns = {} self.campaigns = []
self.is_over = False self.is_over = False
def set_id(self, new_id): def set_id(self, new_id):
self.id = new_id self.id = new_id
@ -44,3 +43,19 @@ class War:
## campaigns placeholder ## campaigns placeholder
tmp.set_state(is_over) tmp.set_state(is_over)
return tmp return tmp
def add_campaign(self, name) -> Campaign:
campaign = Campaign(name)
self.campaigns.append(campaign)
return campaign
def get_campaign(self, campaign_id) -> Campaign:
return self.campaigns[campaign_id]
def get_all_campaigns(self) -> list[Campaign]:
return list(self.campaigns)
def add_campaign(self, name: str) -> Campaign:
campaign = Campaign(name)
self.campaigns.append(campaign)
return campaign

View file

@ -0,0 +1,50 @@
# Form implementation generated from reading ui file '.\src\warchron\view\ui\ui_campaign_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_campaignDialog(object):
def setupUi(self, campaignDialog):
campaignDialog.setObjectName("campaignDialog")
campaignDialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
campaignDialog.resize(378, 98)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/warchron_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
campaignDialog.setWindowIcon(icon)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=campaignDialog)
self.buttonBox.setGeometry(QtCore.QRect(10, 60, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setObjectName("buttonBox")
self.label = QtWidgets.QLabel(parent=campaignDialog)
self.label.setGeometry(QtCore.QRect(10, 20, 47, 14))
self.label.setObjectName("label")
self.campaignName = QtWidgets.QLineEdit(parent=campaignDialog)
self.campaignName.setGeometry(QtCore.QRect(60, 20, 113, 20))
self.campaignName.setObjectName("campaignName")
self.retranslateUi(campaignDialog)
self.buttonBox.accepted.connect(campaignDialog.accept) # type: ignore
self.buttonBox.rejected.connect(campaignDialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(campaignDialog)
def retranslateUi(self, campaignDialog):
_translate = QtCore.QCoreApplication.translate
campaignDialog.setWindowTitle(_translate("campaignDialog", "Campaign"))
self.label.setText(_translate("campaignDialog", "Name:"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
campaignDialog = QtWidgets.QDialog()
ui = Ui_campaignDialog()
ui.setupUi(campaignDialog)
campaignDialog.show()
sys.exit(app.exec())

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>campaignDialog</class>
<widget class="QDialog" name="campaignDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>378</width>
<height>98</height>
</rect>
</property>
<property name="windowTitle">
<string>Campaign</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../resources/warchron_logo.png</normaloff>../resources/warchron_logo.png</iconset>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>47</width>
<height>14</height>
</rect>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
<widget class="QLineEdit" name="campaignName">
<property name="geometry">
<rect>
<x>60</x>
<y>20</y>
<width>113</width>
<height>20</height>
</rect>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>campaignDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>campaignDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -51,12 +51,16 @@ class Ui_MainWindow(object):
self.addCampaignBtn.setEnabled(False) self.addCampaignBtn.setEnabled(False)
self.addCampaignBtn.setGeometry(QtCore.QRect(110, 20, 91, 23)) self.addCampaignBtn.setGeometry(QtCore.QRect(110, 20, 91, 23))
self.addCampaignBtn.setObjectName("addCampaignBtn") self.addCampaignBtn.setObjectName("addCampaignBtn")
self.addRoundBtn = QtWidgets.QPushButton(parent=self.warsTab)
self.addRoundBtn.setEnabled(False)
self.addRoundBtn.setGeometry(QtCore.QRect(220, 20, 91, 23))
self.addRoundBtn.setObjectName("addRoundBtn")
icon2 = QtGui.QIcon() icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/swords-small.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) icon2.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/swords-small.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.tabWidget.addTab(self.warsTab, icon2, "") self.tabWidget.addTab(self.warsTab, icon2, "")
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(parent=MainWindow) self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22)) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(parent=self.menubar) self.menuFile = QtWidgets.QMenu(parent=self.menubar)
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
@ -144,6 +148,7 @@ class Ui_MainWindow(object):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.playersTab), _translate("MainWindow", "Players")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.playersTab), _translate("MainWindow", "Players"))
self.addWarBtn.setText(_translate("MainWindow", "Add war")) self.addWarBtn.setText(_translate("MainWindow", "Add war"))
self.addCampaignBtn.setText(_translate("MainWindow", "Add Campaign")) self.addCampaignBtn.setText(_translate("MainWindow", "Add Campaign"))
self.addRoundBtn.setText(_translate("MainWindow", "Add Round"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.warsTab), _translate("MainWindow", "Wars")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.warsTab), _translate("MainWindow", "Wars"))
self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuEdit.setTitle(_translate("MainWindow", "Edit")) self.menuEdit.setTitle(_translate("MainWindow", "Edit"))

View file

@ -124,6 +124,22 @@
<string>Add Campaign</string> <string>Add Campaign</string>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="addRoundBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>220</x>
<y>20</y>
<width>91</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Add Round</string>
</property>
</widget>
</widget> </widget>
</widget> </widget>
</widget> </widget>
@ -133,7 +149,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>800</width>
<height>22</height> <height>21</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">

View file

@ -0,0 +1,50 @@
# Form implementation generated from reading ui file '.\src\warchron\view\ui\ui_war_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_warDialog(object):
def setupUi(self, warDialog):
warDialog.setObjectName("warDialog")
warDialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
warDialog.resize(378, 98)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(".\\src\\warchron\\view\\ui\\../resources/warchron_logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
warDialog.setWindowIcon(icon)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=warDialog)
self.buttonBox.setGeometry(QtCore.QRect(10, 60, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setObjectName("buttonBox")
self.label = QtWidgets.QLabel(parent=warDialog)
self.label.setGeometry(QtCore.QRect(10, 20, 47, 14))
self.label.setObjectName("label")
self.warName = QtWidgets.QLineEdit(parent=warDialog)
self.warName.setGeometry(QtCore.QRect(60, 20, 113, 20))
self.warName.setObjectName("warName")
self.retranslateUi(warDialog)
self.buttonBox.accepted.connect(warDialog.accept) # type: ignore
self.buttonBox.rejected.connect(warDialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(warDialog)
def retranslateUi(self, warDialog):
_translate = QtCore.QCoreApplication.translate
warDialog.setWindowTitle(_translate("warDialog", "War"))
self.label.setText(_translate("warDialog", "Name:"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
warDialog = QtWidgets.QDialog()
ui = Ui_warDialog()
ui.setupUi(warDialog)
warDialog.show()
sys.exit(app.exec())

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>warDialog</class>
<widget class="QDialog" name="warDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>378</width>
<height>98</height>
</rect>
</property>
<property name="windowTitle">
<string>War</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../resources/warchron_logo.png</normaloff>../resources/warchron_logo.png</iconset>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>47</width>
<height>14</height>
</rect>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
<widget class="QLineEdit" name="warName">
<property name="geometry">
<rect>
<x>60</x>
<y>20</y>
<width>113</width>
<height>20</height>
</rect>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>warDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>warDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -1,25 +1,49 @@
from pathlib import Path from pathlib import Path
import calendar
from PyQt6 import QtWidgets from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QDialog, QFileDialog from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QDialog, QFileDialog, QTreeWidgetItem
from PyQt6.QtGui import QCloseEvent from PyQt6.QtGui import QCloseEvent
from warchron.view.ui.ui_main_window import Ui_MainWindow 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_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
ROLE_TYPE = Qt.ItemDataRole.UserRole
ROLE_ID = Qt.ItemDataRole.UserRole + 1
class View(QtWidgets.QMainWindow, Ui_MainWindow): class View(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
super(View, self).__init__(parent) super(View, self).__init__(parent)
self.setupUi(self) self.setupUi(self)
self.on_close_callback = None self.on_close_callback = None
self.on_selection_changed = None
self.on_add_campaign = None
self.on_add_round = None
self.warsTree.currentItemChanged.connect(self._emit_selection_changed)
self.addCampaignBtn.clicked.connect(self._on_add_campaign_clicked)
self.addRoundBtn.clicked.connect(self._on_add_round_clicked)
def display_players(self, players: list): def _emit_selection_changed(self, current, previous):
table = self.playersTable if not self.on_tree_selection_changed:
table.setRowCount(len(players)) return
for row, player in enumerate(players): if not current:
table.setItem(row, 0, QtWidgets.QTableWidgetItem(player.name)) self.on_tree_selection_changed(None)
table.setItem(row, 1, QtWidgets.QTableWidgetItem(player.id)) return
table.resizeColumnsToContents() self.on_tree_selection_changed({
"type": current.data(0, ROLE_TYPE),
"id": current.data(0, ROLE_ID),
})
def _on_add_campaign_clicked(self):
if self.on_add_campaign:
self.on_add_campaign()
def _on_add_round_clicked(self):
if self.on_add_round:
self.on_add_round()
def closeEvent(self, event: QCloseEvent): def closeEvent(self, event: QCloseEvent):
if self.on_close_callback: if self.on_close_callback:
@ -47,6 +71,51 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
) )
return Path(filename) if filename else None return Path(filename) if filename else None
def display_players(self, players: list):
table = self.playersTable
table.setRowCount(len(players))
for row, player in enumerate(players):
table.setItem(row, 0, QtWidgets.QTableWidgetItem(player.name))
table.setItem(row, 1, QtWidgets.QTableWidgetItem(player.id))
table.resizeColumnsToContents()
def display_wars(self, wars: list):
tree = self.warsTree
tree.clear()
tree.setColumnCount(1)
tree.setHeaderLabels(["Wars"])
for war in wars:
war_item = QTreeWidgetItem([f"{war.name} ({war.year})"])
war_item.setData(0, ROLE_TYPE, "war")
war_item.setData(0, ROLE_ID, war.id)
tree.addTopLevelItem(war_item)
for camp in war.get_all_campaigns():
camp_item = QTreeWidgetItem([f"{camp.name} ({calendar.month_name[camp.month]})"])
camp_item.setData(0, ROLE_TYPE, "campaign")
camp_item.setData(0, ROLE_ID, camp.id)
war_item.addChild(camp_item)
for index, rnd in enumerate(camp.get_all_rounds(), start=1):
rnd_item = QTreeWidgetItem([f"Round {index}"])
rnd_item.setData(0, ROLE_TYPE, "round")
rnd_item.setData(0, ROLE_ID, rnd.id)
camp_item.addChild(rnd_item)
tree.expandAll()
def get_selected_tree_item(self):
item = self.warsTree.currentItem()
if not item:
return None
return {
"type": item.data(0, ROLE_TYPE),
"id": item.data(0, ROLE_ID)
}
def set_add_campaign_enabled(self, enabled: bool):
self.addCampaignBtn.setEnabled(enabled)
def set_add_round_enabled(self, enabled: bool):
self.addRoundBtn.setEnabled(enabled)
class PlayerDialog(QDialog): class PlayerDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -55,3 +124,21 @@ class PlayerDialog(QDialog):
def get_player_name(self) -> str: def get_player_name(self) -> str:
return self.ui.playerName.text().strip() return self.ui.playerName.text().strip()
class WarDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_warDialog()
self.ui.setupUi(self)
def get_war_name(self) -> str:
return self.ui.warName.text().strip()
class CampaignDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_campaignDialog()
self.ui.setupUi(self)
def get_campaign_name(self) -> str:
return self.ui.campaignName.text().strip()