mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
279 lines
11 KiB
Python
279 lines
11 KiB
Python
import logging
|
|
import traceback
|
|
import webbrowser
|
|
from typing import Optional
|
|
|
|
from PySide2.QtCore import Qt
|
|
from PySide2.QtGui import QCloseEvent, QIcon
|
|
from PySide2.QtWidgets import (
|
|
QAction,
|
|
QActionGroup, QDesktopWidget,
|
|
QFileDialog,
|
|
QMainWindow,
|
|
QMessageBox,
|
|
QSplitter,
|
|
QVBoxLayout,
|
|
QWidget,
|
|
)
|
|
|
|
import qt_ui.uiconstants as CONST
|
|
from game import Game, VERSION, persistency
|
|
from qt_ui.dialogs import Dialog
|
|
from qt_ui.displayoptions import DisplayGroup, DisplayOptions, DisplayRule
|
|
from qt_ui.models import GameModel
|
|
from qt_ui.uiconstants import URLS
|
|
from qt_ui.widgets.QTopPanel import QTopPanel
|
|
from qt_ui.widgets.ato import QAirTaskingOrderPanel
|
|
from qt_ui.widgets.map.QLiberationMap import QLiberationMap
|
|
from qt_ui.windows.GameUpdateSignal import DebriefingSignal, GameUpdateSignal
|
|
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
|
|
from qt_ui.windows.infos.QInfoPanel import QInfoPanel
|
|
from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard
|
|
from qt_ui.windows.preferences.QLiberationPreferencesWindow import \
|
|
QLiberationPreferencesWindow
|
|
|
|
|
|
class QLiberationWindow(QMainWindow):
|
|
|
|
def __init__(self, game: Optional[Game]) -> None:
|
|
super(QLiberationWindow, self).__init__()
|
|
|
|
self.game = game
|
|
self.game_model = GameModel(game)
|
|
Dialog.set_game(self.game_model)
|
|
self.ato_panel = QAirTaskingOrderPanel(self.game_model)
|
|
self.info_panel = QInfoPanel(self.game)
|
|
self.liberation_map = QLiberationMap(self.game_model)
|
|
|
|
self.setGeometry(300, 100, 270, 100)
|
|
self.setWindowTitle(f"DCS Liberation - v{VERSION}")
|
|
self.setWindowIcon(QIcon("./resources/icon.png"))
|
|
self.statusBar().showMessage('Ready')
|
|
|
|
self.initUi()
|
|
self.initActions()
|
|
self.initMenuBar()
|
|
self.initToolbar()
|
|
self.connectSignals()
|
|
|
|
screen = QDesktopWidget().screenGeometry()
|
|
self.setGeometry(0, 0, screen.width(), screen.height())
|
|
self.setWindowState(Qt.WindowMaximized)
|
|
|
|
if self.game is None:
|
|
self.onGameGenerated(persistency.restore_game())
|
|
else:
|
|
self.onGameGenerated(self.game)
|
|
|
|
def initUi(self):
|
|
hbox = QSplitter(Qt.Horizontal)
|
|
vbox = QSplitter(Qt.Vertical)
|
|
hbox.addWidget(self.ato_panel)
|
|
hbox.addWidget(vbox)
|
|
vbox.addWidget(self.liberation_map)
|
|
vbox.addWidget(self.info_panel)
|
|
|
|
# Will make the ATO sidebar as small as necessary to fit the content. In
|
|
# practice this means it is sized by the hints in the panel.
|
|
hbox.setSizes([1, 10000000])
|
|
vbox.setSizes([600, 100])
|
|
|
|
vbox = QVBoxLayout()
|
|
vbox.setMargin(0)
|
|
vbox.addWidget(QTopPanel(self.game_model))
|
|
vbox.addWidget(hbox)
|
|
|
|
central_widget = QWidget()
|
|
central_widget.setLayout(vbox)
|
|
self.setCentralWidget(central_widget)
|
|
|
|
def connectSignals(self):
|
|
GameUpdateSignal.get_instance().gameupdated.connect(self.setGame)
|
|
GameUpdateSignal.get_instance().debriefingReceived.connect(self.onDebriefing)
|
|
|
|
def initActions(self):
|
|
self.newGameAction = QAction("&New Game", self)
|
|
self.newGameAction.setIcon(QIcon(CONST.ICONS["New"]))
|
|
self.newGameAction.triggered.connect(self.newGame)
|
|
self.newGameAction.setShortcut('CTRL+N')
|
|
|
|
self.openAction = QAction("&Open", self)
|
|
self.openAction.setIcon(QIcon(CONST.ICONS["Open"]))
|
|
self.openAction.triggered.connect(self.openFile)
|
|
self.openAction.setShortcut('CTRL+O')
|
|
|
|
self.saveGameAction = QAction("&Save", self)
|
|
self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"]))
|
|
self.saveGameAction.triggered.connect(self.saveGame)
|
|
self.saveGameAction.setShortcut('CTRL+S')
|
|
|
|
self.saveAsAction = QAction("Save &As", self)
|
|
self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"]))
|
|
self.saveAsAction.triggered.connect(self.saveGameAs)
|
|
self.saveAsAction.setShortcut('CTRL+A')
|
|
|
|
self.showAboutDialogAction = QAction("&About DCS Liberation", self)
|
|
self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
|
self.showAboutDialogAction.triggered.connect(self.showAboutDialog)
|
|
|
|
self.showLiberationPrefDialogAction = QAction("&Preferences", self)
|
|
self.showLiberationPrefDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
|
self.showLiberationPrefDialogAction.triggered.connect(self.showLiberationDialog)
|
|
|
|
def initToolbar(self):
|
|
self.tool_bar = self.addToolBar("File")
|
|
self.tool_bar.addAction(self.newGameAction)
|
|
self.tool_bar.addAction(self.openAction)
|
|
self.tool_bar.addAction(self.saveGameAction)
|
|
|
|
def initMenuBar(self):
|
|
self.menu = self.menuBar()
|
|
|
|
file_menu = self.menu.addMenu("&File")
|
|
file_menu.addAction(self.newGameAction)
|
|
file_menu.addAction(self.openAction)
|
|
file_menu.addSeparator()
|
|
file_menu.addAction(self.saveGameAction)
|
|
file_menu.addAction(self.saveAsAction)
|
|
file_menu.addSeparator()
|
|
file_menu.addAction(self.showLiberationPrefDialogAction)
|
|
file_menu.addSeparator()
|
|
file_menu.addAction("E&xit", self.close)
|
|
|
|
displayMenu = self.menu.addMenu("&Display")
|
|
|
|
last_was_group = True
|
|
for item in DisplayOptions.menu_items():
|
|
if isinstance(item, DisplayRule):
|
|
displayMenu.addAction(self.make_display_rule_action(item))
|
|
last_was_group = False
|
|
elif isinstance(item, DisplayGroup):
|
|
if not last_was_group:
|
|
displayMenu.addSeparator()
|
|
group = QActionGroup(displayMenu)
|
|
for display_rule in item:
|
|
displayMenu.addAction(
|
|
self.make_display_rule_action(display_rule, group))
|
|
last_was_group = True
|
|
|
|
help_menu = self.menu.addMenu("&Help")
|
|
help_menu.addAction("&Discord Server", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ"))
|
|
help_menu.addAction("&Github Repository", lambda: webbrowser.open_new_tab("https://github.com/khopa/dcs_liberation"))
|
|
help_menu.addAction("&Releases", lambda: webbrowser.open_new_tab("https://github.com/Khopa/dcs_liberation/releases"))
|
|
help_menu.addAction("&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"]))
|
|
help_menu.addAction("&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"]))
|
|
help_menu.addAction("Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"]))
|
|
|
|
help_menu.addSeparator()
|
|
help_menu.addAction(self.showAboutDialogAction)
|
|
|
|
@staticmethod
|
|
def make_display_rule_action(
|
|
display_rule, group: Optional[QActionGroup] = None) -> QAction:
|
|
def make_check_closure():
|
|
def closure():
|
|
display_rule.value = action.isChecked()
|
|
|
|
return closure
|
|
|
|
action = QAction(f"&{display_rule.menu_text}", group)
|
|
action.setCheckable(True)
|
|
action.setChecked(display_rule.value)
|
|
action.toggled.connect(make_check_closure())
|
|
return action
|
|
|
|
def newGame(self):
|
|
wizard = NewGameWizard(self)
|
|
wizard.show()
|
|
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
|
|
|
|
def openFile(self):
|
|
file = QFileDialog.getOpenFileName(self, "Select game file to open",
|
|
dir=persistency._dcs_saved_game_folder,
|
|
filter="*.liberation")
|
|
if file is not None:
|
|
game = persistency.load_game(file[0])
|
|
GameUpdateSignal.get_instance().updateGame(game)
|
|
|
|
def saveGame(self):
|
|
logging.info("Saving game")
|
|
|
|
if self.game.savepath:
|
|
persistency.save_game(self.game)
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
else:
|
|
self.saveGameAs()
|
|
|
|
def saveGameAs(self):
|
|
file = QFileDialog.getSaveFileName(self, "Save As", dir=persistency._dcs_saved_game_folder, filter="*.liberation")
|
|
if file is not None:
|
|
self.game.savepath = file[0]
|
|
persistency.save_game(self.game)
|
|
|
|
def onGameGenerated(self, game: Game):
|
|
logging.info("On Game generated")
|
|
self.game = game
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
|
|
def setGame(self, game: Optional[Game]):
|
|
try:
|
|
if game is not None:
|
|
game.on_load()
|
|
self.game = game
|
|
if self.info_panel is not None:
|
|
self.info_panel.setGame(game)
|
|
self.game_model.set(self.game)
|
|
if self.liberation_map is not None:
|
|
self.liberation_map.setGame(game)
|
|
except AttributeError:
|
|
logging.exception("Incompatible save game")
|
|
QMessageBox.critical(
|
|
self,
|
|
"Could not load save game",
|
|
"The save game you have loaded is incompatible with this "
|
|
"version of DCS Liberation.\n"
|
|
"\n"
|
|
f"{traceback.format_exc()}",
|
|
QMessageBox.Ok
|
|
)
|
|
GameUpdateSignal.get_instance().updateGame(None)
|
|
|
|
def showAboutDialog(self):
|
|
text = "<h3>DCS Liberation " + VERSION + "</h3>" + \
|
|
"<b>Source code :</b> https://github.com/khopa/dcs_liberation" + \
|
|
"<h4>Authors</h4>" + \
|
|
"<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \
|
|
"<h4>Contributors</h4>" + \
|
|
"shdwp, Khopa, ColonelPanic, Roach, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57" + \
|
|
"<h4>Special Thanks :</h4>" \
|
|
"<b>rp-</b> <i>for the pydcs framework</i><br/>"\
|
|
"<b>Grimes (mrSkortch)</b> & <b>Speed</b> <i>for the MIST framework</i><br/>"\
|
|
"<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"\
|
|
"<b>Walder </b> <i>for the Skynet-IADS script</i><br/>"
|
|
about = QMessageBox()
|
|
about.setWindowTitle("About DCS Liberation")
|
|
about.setIcon(QMessageBox.Icon.Information)
|
|
about.setText(text)
|
|
logging.info(about.textFormat())
|
|
about.exec_()
|
|
|
|
def showLiberationDialog(self):
|
|
self.subwindow = QLiberationPreferencesWindow()
|
|
self.subwindow.show()
|
|
|
|
def onDebriefing(self, debrief: DebriefingSignal):
|
|
logging.info("On Debriefing")
|
|
self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game)
|
|
self.debriefing.show()
|
|
|
|
def closeEvent(self, event: QCloseEvent) -> None:
|
|
result = QMessageBox.question(
|
|
self, "Quit Liberation?",
|
|
"Are you sure you want to quit? All unsaved progress will be lost.",
|
|
QMessageBox.Yes | QMessageBox.No
|
|
)
|
|
if result == QMessageBox.Yes:
|
|
super().closeEvent(event)
|
|
else:
|
|
event.ignore()
|