diff --git a/qt_ui/displayoptions.py b/qt_ui/displayoptions.py new file mode 100644 index 00000000..a9b6320c --- /dev/null +++ b/qt_ui/displayoptions.py @@ -0,0 +1,43 @@ +"""Visibility options for the game map.""" +from dataclasses import dataclass +from typing import Iterator + + +@dataclass +class DisplayRule: + name: str + _value: bool + + @property + def menu_text(self) -> str: + return self.name + + @property + def value(self) -> bool: + return self._value + + @value.setter + def value(self, value: bool) -> None: + from qt_ui.widgets.map.QLiberationMap import QLiberationMap + self._value = value + QLiberationMap.instance.reload_scene() + QLiberationMap.instance.update() + + def __bool__(self) -> bool: + return self.value + + +class DisplayOptions: + ground_objects = DisplayRule("Ground Objects", True) + control_points = DisplayRule("Control Points", True) + lines = DisplayRule("Lines", True) + events = DisplayRule("Events", True) + sam_ranges = DisplayRule("SAM Ranges", True) + flight_paths = DisplayRule("Flight Paths", False) + + @classmethod + def menu_items(cls) -> Iterator[DisplayRule]: + # Python 3.6 enforces that __dict__ is order preserving by default. + for value in cls.__dict__.values(): + if isinstance(value, DisplayRule): + yield value diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index 3d775367..1f3fdd57 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import logging -from typing import Dict, List, Optional, Tuple +from typing import List, Optional, Tuple from PySide2.QtCore import Qt from PySide2.QtGui import QBrush, QColor, QPen, QPixmap, QWheelEvent @@ -18,11 +20,12 @@ from game import Game, db from game.data.radar_db import UNITS_WITH_RADAR from gen import Conflict from gen.flights.flight import Flight +from qt_ui.displayoptions import DisplayOptions from qt_ui.models import GameModel +from qt_ui.widgets.map.QFrontLine import QFrontLine from qt_ui.widgets.map.QLiberationScene import QLiberationScene from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject -from qt_ui.widgets.map.QFrontLine import QFrontLine from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from theater import ControlPoint, FrontLine @@ -30,15 +33,7 @@ from theater import ControlPoint, FrontLine class QLiberationMap(QGraphicsView): WAYPOINT_SIZE = 4 - instance = None - display_rules: Dict[str, bool] = { - "cp": True, - "go": True, - "lines": True, - "events": True, - "sam": True, - "flight_paths": False - } + instance: Optional[QLiberationMap] = None def __init__(self, game_model: GameModel): super(QLiberationMap, self).__init__() @@ -161,7 +156,8 @@ class QLiberationMap(QGraphicsView): buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name) scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 14, 12, cp, ground_object, self.game, buildings)) - if ground_object.category == "aa" and self.get_display_rule("sam"): + is_aa = ground_object.category == "aa" + if is_aa and DisplayOptions.sam_ranges: max_range = 0 has_radar = False if ground_object.groups: @@ -177,11 +173,11 @@ class QLiberationMap(QGraphicsView): added_objects.append(ground_object.obj_name) for cp in self.game.theater.enemy_points(): - if self.get_display_rule("lines"): + if DisplayOptions.lines: self.scene_create_lines_for_cp(cp, playerColor, enemyColor) for cp in self.game.theater.player_points(): - if self.get_display_rule("lines"): + if DisplayOptions.lines: self.scene_create_lines_for_cp(cp, playerColor, enemyColor) self.draw_flight_plans(scene) @@ -202,7 +198,7 @@ class QLiberationMap(QGraphicsView): # Something may have caused those items to already be removed. pass self.flight_path_items.clear() - if not self.get_display_rule("flight_paths"): + if not DisplayOptions.flight_paths: return for package in self.game_model.ato_model.packages: for flight in package.flights: @@ -367,18 +363,3 @@ class QLiberationMap(QGraphicsView): effect = QGraphicsOpacityEffect() effect.setOpacity(0.3) overlay.setGraphicsEffect(effect) - - - @staticmethod - def set_display_rule(rule: str, value: bool): - QLiberationMap.display_rules[rule] = value - QLiberationMap.instance.reload_scene() - QLiberationMap.instance.update() - - @staticmethod - def get_display_rules() -> Dict[str, bool]: - return QLiberationMap.display_rules - - @staticmethod - def get_display_rule(rule) -> bool: - return QLiberationMap.display_rules[rule] diff --git a/qt_ui/widgets/map/QMapControlPoint.py b/qt_ui/widgets/map/QMapControlPoint.py index f5b2e1c4..ef9bf5c9 100644 --- a/qt_ui/widgets/map/QMapControlPoint.py +++ b/qt_ui/widgets/map/QMapControlPoint.py @@ -7,6 +7,7 @@ from qt_ui.models import GameModel from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2 from theater import ControlPoint from .QMapObject import QMapObject +from ...displayoptions import DisplayOptions class QMapControlPoint(QMapObject): @@ -21,7 +22,7 @@ class QMapControlPoint(QMapObject): self.base_details_dialog: Optional[QBaseMenu2] = None def paint(self, painter, option, widget=None) -> None: - if self.parent.get_display_rule("cp"): + if DisplayOptions.control_points: painter.save() painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(self.brush_color) diff --git a/qt_ui/widgets/map/QMapGroundObject.py b/qt_ui/widgets/map/QMapGroundObject.py index 1ed9f3d2..af0789a8 100644 --- a/qt_ui/widgets/map/QMapGroundObject.py +++ b/qt_ui/widgets/map/QMapGroundObject.py @@ -8,8 +8,9 @@ import qt_ui.uiconstants as const from game import Game from game.data.building_data import FORTIFICATION_BUILDINGS from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu -from theater import TheaterGroundObject, ControlPoint +from theater import ControlPoint, TheaterGroundObject from .QMapObject import QMapObject +from ...displayoptions import DisplayOptions class QMapGroundObject(QMapObject): @@ -50,7 +51,7 @@ class QMapGroundObject(QMapObject): player_icons = "_blue" enemy_icons = "" - if self.parent.get_display_rule("go"): + if DisplayOptions.ground_objects: painter.save() cat = self.ground_object.category diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index 3933083c..acda1bb0 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -19,6 +19,7 @@ from PySide2.QtWidgets import ( import qt_ui.uiconstants as CONST from game import Game, persistency from qt_ui.dialogs import Dialog +from qt_ui.displayoptions import DisplayOptions from qt_ui.models import GameModel from qt_ui.uiconstants import URLS from qt_ui.widgets.QTopPanel import QTopPanel @@ -134,48 +135,21 @@ class QLiberationWindow(QMainWindow): file_menu.addSeparator() file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addSeparator() - #file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working file_menu.addAction("E&xit" , lambda: self.exit()) displayMenu = self.menu.addMenu("&Display") - tg_cp_visibility = QAction('&Control Point', displayMenu) - tg_cp_visibility.setCheckable(True) - tg_cp_visibility.setChecked(True) - tg_cp_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("cp", tg_cp_visibility.isChecked())) + for display_rule in DisplayOptions.menu_items(): + def make_check_closure(): + def closure(): + display_rule.value = action.isChecked() + return closure - tg_go_visibility = QAction('&Ground Objects', displayMenu) - tg_go_visibility.setCheckable(True) - tg_go_visibility.setChecked(True) - tg_go_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("go", tg_go_visibility.isChecked())) - - tg_line_visibility = QAction('&Lines', displayMenu) - tg_line_visibility.setCheckable(True) - tg_line_visibility.setChecked(True) - tg_line_visibility.toggled.connect( - lambda: QLiberationMap.set_display_rule("lines", tg_line_visibility.isChecked())) - - tg_event_visibility = QAction('&Events', displayMenu) - tg_event_visibility.setCheckable(True) - tg_event_visibility.setChecked(True) - tg_event_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("events", tg_event_visibility.isChecked())) - - tg_sam_visibility = QAction('&SAM Range', displayMenu) - tg_sam_visibility.setCheckable(True) - tg_sam_visibility.setChecked(True) - tg_sam_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("sam", tg_sam_visibility.isChecked())) - - tg_flight_path_visibility = QAction('&Flight Paths', displayMenu) - tg_flight_path_visibility.setCheckable(True) - tg_flight_path_visibility.setChecked(False) - tg_flight_path_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("flight_paths", tg_flight_path_visibility.isChecked())) - - displayMenu.addAction(tg_go_visibility) - displayMenu.addAction(tg_cp_visibility) - displayMenu.addAction(tg_line_visibility) - displayMenu.addAction(tg_event_visibility) - displayMenu.addAction(tg_sam_visibility) - displayMenu.addAction(tg_flight_path_visibility) + action = QAction(f"&{display_rule.menu_text}", displayMenu) + action.setCheckable(True) + action.setChecked(display_rule.value) + action.toggled.connect(make_check_closure()) + displayMenu.addAction(action) help_menu = self.menu.addMenu("&Help") help_menu.addAction("&Discord Server", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ"))