diff --git a/changelog.md b/changelog.md index d1241271..180ef1c1 100644 --- a/changelog.md +++ b/changelog.md @@ -59,7 +59,7 @@ * **[Maps/Campaign]** Now using Vasiani airbase instead of Soganlung airport in Caucasus campaigns (more parking slot) * **[Info Panel]** Message displayed on base capture event stated that the enemy captured an airbase, while it was the player who captured it. * **[Map View]** Graphical glitch on map when one building of an objective was destroyed, but not the others - +* **[Mission Planner]** The list of flights was not updated on departure time change. # 2.0 RC 6 diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 8ca7dc68..bef13954 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -659,8 +659,8 @@ class FlightPlanner: ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, self.doctrine["PATTERN_ALTITUDE"]) ascend.name = "ASCEND" ascend.alt_type = "RADIO" - ascend.description = "Ascend to alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL], then proceed to next waypoint" - ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL]" + ascend.description = "Ascend" + ascend.pretty_name = "Ascend" ascend.waypoint_type = FlightWaypointType.ASCEND_POINT return ascend @@ -676,8 +676,8 @@ class FlightPlanner: descend = FlightWaypoint(descend.x, descend.y, self.doctrine["PATTERN_ALTITUDE"]) descend.name = "DESCEND" descend.alt_type = "RADIO" - descend.description = "Descend to pattern alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL], contact tower, and land" - descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL]" + descend.description = "Descend to pattern alt" + descend.pretty_name = "Descend to pattern alt" descend.waypoint_type = FlightWaypointType.DESCENT_POINT return descend diff --git a/qt_ui/main.py b/qt_ui/main.py index 649a3ec5..cf4a9eae 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -1,19 +1,17 @@ import logging import os import sys -from shutil import copyfile import dcs from PySide2 import QtWidgets from PySide2.QtGui import QPixmap from PySide2.QtWidgets import QApplication, QSplashScreen -from dcs import installation from qt_ui import uiconstants from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QLiberationWindow import QLiberationWindow from qt_ui.windows.preferences.QLiberationFirstStartWindow import QLiberationFirstStartWindow -from userdata import persistency, logging as logging_module, liberation_install +from userdata import liberation_install, persistency, logging_config if __name__ == "__main__": @@ -24,8 +22,8 @@ if __name__ == "__main__": app.setStyleSheet(stylesheet.read()) # Logging setup - VERSION_STRING = "2.0RC6" - logging_module.setup_version_string(VERSION_STRING) + VERSION_STRING = "2.0RC7" + logging_config.init_logging(VERSION_STRING) # Inject custom payload in pydcs framework custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads") @@ -80,6 +78,7 @@ if __name__ == "__main__": logging.info("QT App terminated with status code : " + str(qt_execution_code)) logging.info("Attempt to restore original mission scripting file") liberation_install.restore_original_mission_scripting() - sys.exit(qt_execution_code) + logging.info("QT process exited with code : " + str(qt_execution_code)) + sys.exit(0) diff --git a/qt_ui/windows/QNewGameWizard.py b/qt_ui/windows/QNewGameWizard.py index c0d2cf96..f858b084 100644 --- a/qt_ui/windows/QNewGameWizard.py +++ b/qt_ui/windows/QNewGameWizard.py @@ -9,7 +9,6 @@ import qt_ui.uiconstants as CONST from game import db, Game from gen import namegen from theater import start_generator, persiangulf, nevada, caucasus, ConflictTheater, normandy, thechannel -from userdata.logging import version_string class NewGameWizard(QtWidgets.QWizard): @@ -109,7 +108,7 @@ class NewGameWizard(QtWidgets.QWizard): game.budget = int(game.budget * multiplier) game.settings.multiplier = multiplier game.settings.sams = True - game.settings.version = version_string() + game.settings.version = "2.0RC7" if midgame: game.budget = game.budget * 4 * len(list(conflicttheater.conflicts())) diff --git a/qt_ui/windows/mission/QFlightItem.py b/qt_ui/windows/mission/QFlightItem.py index e93a9b9e..5e4c4c11 100644 --- a/qt_ui/windows/mission/QFlightItem.py +++ b/qt_ui/windows/mission/QFlightItem.py @@ -18,3 +18,9 @@ class QFlightItem(QStandardItem): self.setText("["+str(self.flight.flight_type.name[:6])+"] " + str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type) + " in " + str(self.flight.scheduled_in) + " minutes") + + def update(self, flight): + self.flight = flight + self.setText("[" + str(self.flight.flight_type.name[:6]) + "] " + + str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type) + + " in " + str(self.flight.scheduled_in) + " minutes") \ No newline at end of file diff --git a/qt_ui/windows/mission/QMissionPlanning.py b/qt_ui/windows/mission/QMissionPlanning.py index 256e41db..858df43a 100644 --- a/qt_ui/windows/mission/QMissionPlanning.py +++ b/qt_ui/windows/mission/QMissionPlanning.py @@ -16,7 +16,7 @@ class QMissionPlanning(QDialog): super(QMissionPlanning, self).__init__() self.game = game self.setWindowFlags(Qt.WindowStaysOnTopHint) - self.setMinimumSize(800, 420) + self.setMinimumSize(1000, 420) self.setModal(True) self.setWindowTitle("Mission Preparation") self.setWindowIcon(EVENT_ICONS["strike"]) @@ -42,9 +42,11 @@ class QMissionPlanning(QDialog): self.planned_flight_view.selectionModel().selectionChanged.connect(self.on_flight_selection_change) if len(self.planned_flight_view.flight_planner.flights) > 0: - self.flight_planner = QFlightPlanner(self.planned_flight_view.flight_planner.flights[0], self.game, self.planned_flight_view.flight_planner) + self.flight_planner = QFlightPlanner(self.planned_flight_view.flight_planner.flights[0], self.game, self.planned_flight_view.flight_planner, 0) + self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view) else: - self.flight_planner = QFlightPlanner(None, self.game, self.planned_flight_view.flight_planner) + self.flight_planner = QFlightPlanner(None, self.game, self.planned_flight_view.flight_planner, 0) + self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view) self.add_flight_button = QPushButton("Add Flight") self.add_flight_button.clicked.connect(self.on_add_flight) @@ -88,7 +90,7 @@ class QMissionPlanning(QDialog): print("On flight selection change") index = self.planned_flight_view.selectionModel().currentIndex().row() - self.planned_flight_view.repaint(); + self.planned_flight_view.repaint() if self.flight_planner is not None: self.flight_planner.clearTabs() @@ -97,9 +99,12 @@ class QMissionPlanning(QDialog): flight = self.planner.flights[index] except IndexError: flight = None - self.flight_planner = QFlightPlanner(flight, self.game, self.planner) + self.flight_planner = QFlightPlanner(flight, self.game, self.planner, self.flight_planner.currentIndex()) + self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view) self.layout.addWidget(self.flight_planner, 0, 1) + def update_planned_flight_view(self): + self.planned_flight_view.update_content() def on_add_flight(self): possible_aircraft_type = list(self.selected_cp.base.aircraft.keys()) diff --git a/qt_ui/windows/mission/QPlannedFlightsView.py b/qt_ui/windows/mission/QPlannedFlightsView.py index 38e280df..fb6f5316 100644 --- a/qt_ui/windows/mission/QPlannedFlightsView.py +++ b/qt_ui/windows/mission/QPlannedFlightsView.py @@ -12,14 +12,21 @@ class QPlannedFlightsView(QListView): super(QPlannedFlightsView, self).__init__() self.model = QStandardItemModel(self) self.setModel(self.model) + self.flightitems = [] self.setIconSize(QSize(91, 24)) self.setSelectionBehavior(QAbstractItemView.SelectItems) if flight_planner: self.set_flight_planner(flight_planner) - def update_content(self, row=0): + def update_content(self): for i, f in enumerate(self.flight_planner.flights): - self.model.appendRow(QFlightItem(f)) + self.flightitems[i].update(f) + + def setup_content(self, row=0): + for i, f in enumerate(self.flight_planner.flights): + item = QFlightItem(f) + self.model.appendRow(item) + self.flightitems.append(item) self.setSelectedFlight(row) self.repaint() @@ -38,4 +45,4 @@ class QPlannedFlightsView(QListView): self.clear_layout() self.flight_planner = flight_planner if self.flight_planner: - self.update_content(row) + self.setup_content(row) diff --git a/qt_ui/windows/mission/flight/QFlightPlanner.py b/qt_ui/windows/mission/flight/QFlightPlanner.py index 47ffe10f..6e422b93 100644 --- a/qt_ui/windows/mission/flight/QFlightPlanner.py +++ b/qt_ui/windows/mission/flight/QFlightPlanner.py @@ -1,3 +1,4 @@ +from PySide2.QtCore import Signal from PySide2.QtWidgets import QTabWidget, QFrame, QGridLayout, QLabel from gen.flights.flight import Flight @@ -9,17 +10,25 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWay class QFlightPlanner(QTabWidget): - def __init__(self, flight: Flight, game: Game, planner): + on_planned_flight_changed = Signal() + + def __init__(self, flight: Flight, game: Game, planner, selected_tab): super(QFlightPlanner, self).__init__() + + print(selected_tab) + self.tabCount = 0 if flight: self.general_settings_tab = QGeneralFlightSettingsTab(flight, game, planner) + self.general_settings_tab.on_flight_settings_changed.connect(lambda: self.on_planned_flight_changed.emit()) self.payload_tab = QFlightPayloadTab(flight, game) self.waypoint_tab = QFlightWaypointTab(game, flight) + self.waypoint_tab.on_flight_changed.connect(lambda: self.on_planned_flight_changed.emit()) self.addTab(self.general_settings_tab, "General Flight settings") self.addTab(self.payload_tab, "Payload") self.addTab(self.waypoint_tab, "Waypoints") self.tabCount = 3 + self.setCurrentIndex(selected_tab) else: tabError = QFrame() l = QGridLayout() @@ -30,4 +39,4 @@ class QFlightPlanner(QTabWidget): def clearTabs(self): for i in range(self.tabCount): - self.removeTab(i) \ No newline at end of file + self.removeTab(i) diff --git a/qt_ui/windows/mission/flight/settings/QFlightDepartureEditor.py b/qt_ui/windows/mission/flight/settings/QFlightDepartureEditor.py index 69b9e90c..25e75e7c 100644 --- a/qt_ui/windows/mission/flight/settings/QFlightDepartureEditor.py +++ b/qt_ui/windows/mission/flight/settings/QFlightDepartureEditor.py @@ -24,5 +24,7 @@ class QFlightDepartureEditor(QGroupBox): layout.addWidget(self.minutes) self.setLayout(layout) + self.changed = self.departure_delta.valueChanged + def change_scheduled(self): self.flight.scheduled_in = int(self.departure_delta.value()) diff --git a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py index 0caacc1d..cabd99cf 100644 --- a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py +++ b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py @@ -1,7 +1,8 @@ +from PySide2.QtCore import Signal from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout -from gen.flights.flight import Flight from game import Game +from gen.flights.flight import Flight from qt_ui.windows.mission.flight.settings.QFlightDepartureEditor import QFlightDepartureEditor from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import QFlightSlotEditor from qt_ui.windows.mission.flight.settings.QFlightStartType import QFlightStartType @@ -9,6 +10,7 @@ from qt_ui.windows.mission.flight.settings.QFlightTypeTaskInfo import QFlightTyp class QGeneralFlightSettingsTab(QFrame): + on_flight_settings_changed = Signal() def __init__(self, flight: Flight, game: Game, planner): super(QGeneralFlightSettingsTab, self).__init__() @@ -19,18 +21,19 @@ class QGeneralFlightSettingsTab(QFrame): def init_ui(self): layout = QGridLayout() - self.flight_info = QFlightTypeTaskInfo(self.flight) - self.flight_departure = QFlightDepartureEditor(self.flight) - self.flight_slots = QFlightSlotEditor(self.flight, self.game, self.planner) - self.flight_start_type = QFlightStartType(self.flight) - layout.addWidget(self.flight_info, 0, 0) - layout.addWidget(self.flight_departure, 1, 0) - layout.addWidget(self.flight_slots, 2, 0) - layout.addWidget(self.flight_start_type, 3, 0) + flight_info = QFlightTypeTaskInfo(self.flight) + flight_departure = QFlightDepartureEditor(self.flight) + flight_slots = QFlightSlotEditor(self.flight, self.game, self.planner) + flight_start_type = QFlightStartType(self.flight) + layout.addWidget(flight_info, 0, 0) + layout.addWidget(flight_departure, 1, 0) + layout.addWidget(flight_slots, 2, 0) + layout.addWidget(flight_start_type, 3, 0) vstretch = QVBoxLayout() vstretch.addStretch() layout.addLayout(vstretch, 3, 0) self.setLayout(layout) - self.flight_start_type.setEnabled(self.flight.client_count > 0) - self.flight_slots.changed.connect(lambda: self.flight_start_type.setEnabled(self.flight.client_count > 0)) + flight_start_type.setEnabled(self.flight.client_count > 0) + flight_slots.changed.connect(lambda: flight_start_type.setEnabled(self.flight.client_count > 0)) + flight_departure.changed.connect(lambda: self.on_flight_settings_changed.emit()) diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py index 6efdfe79..bc740a34 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py @@ -1,7 +1,8 @@ -from PySide2.QtCore import QItemSelectionModel, QPoint, Qt -from PySide2.QtGui import QStandardItemModel -from PySide2.QtWidgets import QListView, QTableView, QHeaderView +from PySide2.QtCore import QItemSelectionModel, QPoint +from PySide2.QtGui import QStandardItemModel, QStandardItem +from PySide2.QtWidgets import QTableView, QHeaderView +from game.utils import meter_to_feet from gen.flights.flight import Flight, FlightWaypoint from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import QWaypointItem @@ -13,7 +14,11 @@ class QFlightWaypointList(QTableView): self.model = QStandardItemModel(self) self.setModel(self.model) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) - self.model.setHorizontalHeaderLabels(["Name"]) + self.model.setHorizontalHeaderLabels(["Name", "Alt"]) + + header = self.horizontalHeader() + header.setSectionResizeMode(0, QHeaderView.ResizeToContents) + self.flight = flight if len(self.flight.points) > 0: @@ -28,10 +33,14 @@ class QFlightWaypointList(QTableView): def update_list(self): self.model.clear() + self.model.setHorizontalHeaderLabels(["Name", "Alt"]) takeoff = FlightWaypoint(self.flight.from_cp.position.x, self.flight.from_cp.position.y, 0) takeoff.description = "Take Off" takeoff.name = takeoff.pretty_name = "Take Off from " + self.flight.from_cp.name self.model.appendRow(QWaypointItem(takeoff, 0)) + self.model.setItem(0, 1, QStandardItem("0 ft AGL")) for i, point in enumerate(self.flight.points): - self.model.appendRow(QWaypointItem(point, i + 1)) + self.model.insertRow(self.model.rowCount()) + self.model.setItem(self.model.rowCount()-1, 0, QWaypointItem(point, i + 1)) + self.model.setItem(self.model.rowCount()-1, 1, QStandardItem(str(meter_to_feet(point.alt)) + " ft " + str(["AGL" if point.alt_type == "RADIO" else "MSL"][0]))) self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) \ No newline at end of file diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py index d08e94d5..69870d1c 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py @@ -1,16 +1,20 @@ +from PySide2.QtCore import Signal from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout -from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType +from game import Game +from gen.flights.flight import Flight from qt_ui.windows.mission.flight.generator.QCAPMissionGenerator import QCAPMissionGenerator from qt_ui.windows.mission.flight.generator.QCASMissionGenerator import QCASMissionGenerator from qt_ui.windows.mission.flight.generator.QSEADMissionGenerator import QSEADMissionGenerator from qt_ui.windows.mission.flight.generator.QSTRIKEMissionGenerator import QSTRIKEMissionGenerator from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import QFlightWaypointList from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import QPredefinedWaypointSelectionWindow -from game import Game + class QFlightWaypointTab(QFrame): + on_flight_changed = Signal() + def __init__(self, game: Game, flight: Flight): super(QFlightWaypointTab, self).__init__() self.flight = flight @@ -73,39 +77,53 @@ class QFlightWaypointTab(QFrame): if wpt > 0: del self.flight.points[wpt-1] self.flight_waypoint_list.update_list() + self.on_change() def on_fast_waypoint(self): self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.finished.connect(self.on_change) self.subwindow.show() def on_ascend_waypoint(self): ascend = self.planner.generate_ascend_point(self.flight.from_cp) self.flight.points.append(ascend) self.flight_waypoint_list.update_list() + self.on_change() def on_rtb_waypoint(self): rtb = self.planner.generate_rtb_waypoint(self.flight.from_cp) self.flight.points.append(rtb) self.flight_waypoint_list.update_list() + self.on_change() def on_descend_waypoint(self): descend = self.planner.generate_descend_point(self.flight.from_cp) self.flight.points.append(descend) self.flight_waypoint_list.update_list() + self.on_change() def on_cas_generator(self): self.subwindow = QCASMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.finished.connect(self.on_change) self.subwindow.show() def on_cap_generator(self): self.subwindow = QCAPMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.finished.connect(self.on_change) self.subwindow.show() def on_sead_generator(self): self.subwindow = QSEADMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.finished.connect(self.on_change) self.subwindow.show() def on_strike_generator(self): self.subwindow = QSTRIKEMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.finished.connect(self.on_change) self.subwindow.show() + def on_change(self): + self.flight_waypoint_list.update_list() + self.on_flight_changed.emit() + + diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index a0b9e33b..ea87fd2b 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -208,7 +208,7 @@ class QSettingsWindow(QDialog): self.culling_distance.setMinimum(50) self.culling_distance.setMaximum(10000) self.culling_distance.setValue(self.game.settings.perf_culling_distance) - self.culling_distance.valueChanged.connect(self.applySettings()) + self.culling_distance.valueChanged.connect(self.applySettings) self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0) self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) diff --git a/userdata/logging.py b/userdata/logging.py deleted file mode 100644 index 387925fc..00000000 --- a/userdata/logging.py +++ /dev/null @@ -1,49 +0,0 @@ -import logging -import traceback -import sys - -from io import StringIO -from tkinter import * -from tkinter.scrolledtext import * - -_version_string = None - - -class ShowLogsException(Exception): - pass - - -def _error_prompt(oops=True): - tk = Tk() - if oops: - Label(tk, text="Oops, something went wrong.").grid(row=0) - Label(tk, text="Please send following text to the developer:").grid(row=1) - - text = ScrolledText(tk) - text.insert("0.0", log_stream.getvalue()) - text.grid(row=2, sticky=NSEW) - tk.focus() - - -def _handle_exception(self, exception: BaseException, *args): - logging.exception(exception) - _error_prompt(isinstance(exception, ShowLogsException)) - - -def setup_version_string(str): - global _version_string - _version_string = str - - -def version_string(): - return _version_string - - -if "--stdout" in sys.argv: - logging.basicConfig(stream=sys.stdout, level=logging.INFO) -else: - log_stream = StringIO() - logging.basicConfig(stream=log_stream, level=logging.INFO) - Tk.report_callback_exception = _handle_exception - -logging.info("DCS Liberation {}".format(_version_string)) diff --git a/userdata/logging_config.py b/userdata/logging_config.py new file mode 100644 index 00000000..abc9f209 --- /dev/null +++ b/userdata/logging_config.py @@ -0,0 +1,25 @@ +import logging +import os +from logging.handlers import RotatingFileHandler + + +def init_logging(version_string): + if not os.path.isdir("./logs"): + os.mkdir("logs") + + logger = logging.getLogger() + + formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s') + + handler = RotatingFileHandler('./logs/liberation.log', 'a', 5000000, 1) + handler.setLevel(logging.INFO) + handler.setFormatter(formatter) + + stream_handler = logging.StreamHandler() + stream_handler.setLevel(logging.DEBUG) + stream_handler.setFormatter(formatter) + + logger.addHandler(stream_handler) + logger.addHandler(handler) + + logger.info("DCS Liberation {}".format(version_string))