From 9e966baf9bc5a1b4ae6ea65dbe2fea31f4dc3764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20R=C3=A9aux?= Date: Tue, 20 Jan 2026 08:46:58 +0100 Subject: [PATCH] war add & display --- src/warchron/controller/controller.py | 66 ++++++++++++------ src/warchron/view/ui/ui_war_dialog.py | 50 ++++++++++++++ src/warchron/view/ui/ui_war_dialog.ui | 98 +++++++++++++++++++++++++++ src/warchron/view/view.py | 39 ++++++++--- 4 files changed, 223 insertions(+), 30 deletions(-) create mode 100644 src/warchron/view/ui/ui_war_dialog.py create mode 100644 src/warchron/view/ui/ui_war_dialog.ui diff --git a/src/warchron/controller/controller.py b/src/warchron/controller/controller.py index e7ba6a4..a247488 100644 --- a/src/warchron/controller/controller.py +++ b/src/warchron/controller/controller.py @@ -2,7 +2,7 @@ from pathlib import Path from PyQt6.QtWidgets import QMessageBox, QDialog -from warchron.view.view import PlayerDialog +from warchron.view.view import PlayerDialog, WarDialog class Controller: def __init__(self, model, view): @@ -13,20 +13,32 @@ class Controller: self.is_dirty = False self.__connect() self.refresh_players_view() + self.refresh_wars_view() self.update_window_title() def __connect(self): - self.view.addPlayerBtn.clicked.connect(self.add_player) self.view.actionExit.triggered.connect(self.view.close) self.view.actionNew.triggered.connect(self.new) self.view.actionOpen.triggered.connect(self.open_file) self.view.actionSave.triggered.connect(self.save) 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): - players = self.model.get_all_players() - self.view.display_players(players) - + 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 new(self): if self.is_dirty: reply = QMessageBox.question( @@ -41,6 +53,7 @@ class Controller: self.current_file = None self.is_dirty = False self.refresh_players_view() + self.refresh_wars_view() self.update_window_title() def open_file(self): @@ -60,22 +73,9 @@ class Controller: self.current_file = path self.is_dirty = False self.refresh_players_view() + self.refresh_wars_view() 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): if not self.current_file: self.save_as() @@ -103,6 +103,9 @@ class Controller: base = base + " *" self.view.setWindowTitle(base) + def refresh_players_view(self): + players = self.model.get_all_players() + self.view.display_players(players) def add_player(self): dialog = PlayerDialog(self.view) @@ -120,3 +123,24 @@ class Controller: self.is_dirty = True self.refresh_players_view() self.update_window_title() + + def refresh_wars_view(self): + wars = self.model.get_all_wars() + self.view.display_wars(wars) + + 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() diff --git a/src/warchron/view/ui/ui_war_dialog.py b/src/warchron/view/ui/ui_war_dialog.py new file mode 100644 index 0000000..46404e7 --- /dev/null +++ b/src/warchron/view/ui/ui_war_dialog.py @@ -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()) diff --git a/src/warchron/view/ui/ui_war_dialog.ui b/src/warchron/view/ui/ui_war_dialog.ui new file mode 100644 index 0000000..89ae183 --- /dev/null +++ b/src/warchron/view/ui/ui_war_dialog.ui @@ -0,0 +1,98 @@ + + + warDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 378 + 98 + + + + War + + + + ../resources/warchron_logo.png../resources/warchron_logo.png + + + + + 10 + 60 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 10 + 20 + 47 + 14 + + + + Name: + + + + + + 60 + 20 + 113 + 20 + + + + + + + + buttonBox + accepted() + warDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + warDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/warchron/view/view.py b/src/warchron/view/view.py index 7ca4f03..c9cc662 100644 --- a/src/warchron/view/view.py +++ b/src/warchron/view/view.py @@ -1,11 +1,12 @@ from pathlib import Path from PyQt6 import QtWidgets -from PyQt6.QtWidgets import QDialog, QFileDialog +from PyQt6.QtWidgets import QDialog, QFileDialog, QTreeWidgetItem from PyQt6.QtGui import QCloseEvent 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 class View(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): @@ -13,14 +14,6 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): self.setupUi(self) self.on_close_callback = 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 closeEvent(self, event: QCloseEvent): if self.on_close_callback: proceed = self.on_close_callback() @@ -47,6 +40,25 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): ) 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, 1, war.id) # role=1 to store id + tree.addTopLevelItem(war_item) + tree.expandAll() + class PlayerDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) @@ -55,3 +67,12 @@ class PlayerDialog(QDialog): def get_player_name(self) -> str: 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()