# The main file for the GUI from statusicon import StatusIcon from statusbar import Statusbar from info import FlightInfo from update import Updater from mlx.gui.common import * from mlx.gui.flight import Wizard from mlx.gui.monitor import MonitorWindow from mlx.gui.weighthelp import WeightHelp from mlx.gui.gates import FleetGateStatus from mlx.gui.prefs import Preferences from mlx.gui.checklist import ChecklistEditor from mlx.gui.pirep import PIREPViewer import mlx.const as const import mlx.fs as fs import mlx.flight as flight import mlx.logger as logger import mlx.acft as acft import mlx.web as web import mlx.singleton as singleton from mlx.i18n import xstr from mlx.pirep import PIREP import time import threading import sys import datetime #------------------------------------------------------------------------------ class GUI(fs.ConnectionListener): """The main GUI class.""" def __init__(self, programDirectory, config): """Construct the GUI.""" gobject.threads_init() self._programDirectory = programDirectory self.config = config self._connecting = False self._reconnecting = False self._connected = False self._logger = logger.Logger(self) self._flight = None self._simulator = None self._monitoring = False self._fleet = None self._fleetCallback = None self._updatePlaneCallback = None self._updatePlaneTailNumber = None self._updatePlaneStatus = None self._updatePlaneGateNumber = None self._stdioLock = threading.Lock() self._stdioText = "" self._stdioStartingLine = True self._sendPIREPCallback = None self.webHandler = web.Handler() self.webHandler.start() self.toRestart = False def build(self, iconDirectory): """Build the GUI.""" self._mainWindow = window = gtk.Window() window.set_title(WINDOW_TITLE_BASE) window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico")) window.set_resizable(False) window.connect("delete-event", lambda a, b: self.hideMainWindow()) window.connect("window-state-event", self._handleMainWindowState) accelGroup = gtk.AccelGroup() window.add_accel_group(accelGroup) mainVBox = gtk.VBox() window.add(mainVBox) self._preferences = Preferences(self) self._checklistEditor = ChecklistEditor(self) menuBar = self._buildMenuBar(accelGroup) mainVBox.pack_start(menuBar, False, False, 0) self._notebook = gtk.Notebook() mainVBox.pack_start(self._notebook, True, True, 4) self._wizard = Wizard(self) label = gtk.Label(xstr("tab_flight")) label.set_use_underline(True) label.set_tooltip_text(xstr("tab_flight_tooltip")) self._notebook.append_page(self._wizard, label) self._flightInfo = FlightInfo(self) label = gtk.Label(xstr("tab_flight_info")) label.set_use_underline(True) label.set_tooltip_text(xstr("tab_flight_info_tooltip")) self._notebook.append_page(self._flightInfo, label) self._flightInfo.disable() self._weightHelp = WeightHelp(self) label = gtk.Label(xstr("tab_weight_help")) label.set_use_underline(True) label.set_tooltip_text(xstr("tab_weight_help_tooltip")) self._notebook.append_page(self._weightHelp, label) (logWidget, self._logView) = self._buildLogWidget() addFaultTag(self._logView.get_buffer()) label = gtk.Label(xstr("tab_log")) label.set_use_underline(True) label.set_tooltip_text(xstr("tab_log_tooltip")) self._notebook.append_page(logWidget, label) self._fleetGateStatus = FleetGateStatus(self) label = gtk.Label(xstr("tab_gates")) label.set_use_underline(True) label.set_tooltip_text(xstr("tab_gates_tooltip")) self._notebook.append_page(self._fleetGateStatus, label) (self._debugLogWidget, self._debugLogView) = self._buildLogWidget() self._debugLogWidget.show_all() mainVBox.pack_start(gtk.HSeparator(), False, False, 0) self._statusbar = Statusbar() mainVBox.pack_start(self._statusbar, False, False, 0) self._notebook.connect("switch-page", self._notebookPageSwitch) self._monitorWindow = MonitorWindow(self, iconDirectory) self._monitorWindow.add_accel_group(accelGroup) self._monitorWindowX = None self._monitorWindowY = None self._selfToggling = False self._pirepViewer = PIREPViewer(self) window.show_all() self._wizard.grabDefault() self._weightHelp.reset() self._weightHelp.disable() self._statusIcon = StatusIcon(iconDirectory, self) self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject else gdk.WATCH) self._loadPIREPDialog = None self._lastLoadedPIREP = None self._hotkeySetID = None self._pilotHotkeyIndex = None self._checklistHotkeyIndex = None @property def mainWindow(self): """Get the main window of the GUI.""" return self._mainWindow @property def logger(self): """Get the logger used by us.""" return self._logger @property def simulator(self): """Get the simulator used by us.""" return self._simulator @property def flight(self): """Get the flight being performed.""" return self._flight @property def entranceExam(self): """Get whether an entrance exam is about to be taken.""" return self._wizard.entranceExam @property def loggedIn(self): """Indicate if the user has logged in properly.""" return self._wizard.loggedIn @property def loginResult(self): """Get the result of the login.""" return self._wizard.loginResult @property def bookedFlight(self): """Get the booked flight selected, if any.""" return self._wizard.bookedFlight @property def cargoWeight(self): """Get the cargo weight.""" return self._wizard.cargoWeight @property def zfw(self): """Get Zero-Fuel Weight calculated for the current flight.""" return self._wizard.zfw @property def filedCruiseAltitude(self): """Get cruise altitude filed for the current flight.""" return self._wizard.filedCruiseAltitude @property def cruiseAltitude(self): """Get cruise altitude set for the current flight.""" return self._wizard.cruiseAltitude @property def route(self): """Get the flight route.""" return self._wizard.route @property def departureMETAR(self): """Get the METAR of the deprature airport.""" return self._wizard.departureMETAR @property def arrivalMETAR(self): """Get the METAR of the deprature airport.""" return self._wizard.arrivalMETAR @property def departureRunway(self): """Get the name of the departure runway.""" return self._wizard.departureRunway @property def sid(self): """Get the SID.""" return self._wizard.sid @property def v1(self): """Get the V1 speed calculated for the flight.""" return self._wizard.v1 @property def vr(self): """Get the Vr speed calculated for the flight.""" return self._wizard.vr @property def v2(self): """Get the V2 speed calculated for the flight.""" return self._wizard.v2 @property def arrivalRunway(self): """Get the arrival runway.""" return self._wizard.arrivalRunway @property def star(self): """Get the STAR.""" return self._wizard.star @property def transition(self): """Get the transition.""" return self._wizard.transition @property def approachType(self): """Get the approach type.""" return self._wizard.approachType @property def vref(self): """Get the Vref speed calculated for the flight.""" return self._wizard.vref @property def flightType(self): """Get the flight type.""" return self._wizard.flightType @property def online(self): """Get whether the flight was online or not.""" return self._wizard.online @property def comments(self): """Get the comments.""" return self._flightInfo.comments @property def flightDefects(self): """Get the flight defects.""" return self._flightInfo.flightDefects @property def delayCodes(self): """Get the delay codes.""" return self._flightInfo.delayCodes def run(self): """Run the GUI.""" if self.config.autoUpdate: self._updater = Updater(self, self._programDirectory, self.config.updateURL, self._mainWindow) self._updater.start() singleton.raiseCallback = self.raiseCallback gtk.main() singleton.raiseCallback = None self._disconnect() def connected(self, fsType, descriptor): """Called when we have connected to the simulator.""" self._connected = True self._logger.untimedMessage("Connected to the simulator %s" % (descriptor,)) fs.sendMessage(const.MESSAGETYPE_INFORMATION, "Welcome to MAVA Logger X " + const.VERSION) gobject.idle_add(self._handleConnected, fsType, descriptor) def _handleConnected(self, fsType, descriptor): """Called when the connection to the simulator has succeeded.""" self._statusbar.updateConnection(self._connecting, self._connected) self.endBusy() if not self._reconnecting: self._wizard.connected(fsType, descriptor) self._reconnecting = False self._listenHotkeys() def connectionFailed(self): """Called when the connection failed.""" self._logger.untimedMessage("Connection to the simulator failed") gobject.idle_add(self._connectionFailed) def _connectionFailed(self): """Called when the connection failed.""" self.endBusy() self._statusbar.updateConnection(self._connecting, self._connected) dialog = gtk.MessageDialog(parent = self._mainWindow, type = MESSAGETYPE_ERROR, message_format = xstr("conn_failed")) dialog.set_title(WINDOW_TITLE_BASE) dialog.format_secondary_markup(xstr("conn_failed_sec")) dialog.add_button(xstr("button_cancel"), 0) dialog.add_button(xstr("button_tryagain"), 1) dialog.set_default_response(1) result = dialog.run() dialog.hide() if result == 1: self.beginBusy(xstr("connect_busy")) self._simulator.reconnect() else: self.reset() def disconnected(self): """Called when we have disconnected from the simulator.""" self._connected = False self._logger.untimedMessage("Disconnected from the simulator") gobject.idle_add(self._disconnected) def _disconnected(self): """Called when we have disconnected from the simulator unexpectedly.""" self._statusbar.updateConnection(self._connecting, self._connected) dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR, message_format = xstr("conn_broken"), parent = self._mainWindow) dialog.set_title(WINDOW_TITLE_BASE) dialog.format_secondary_markup(xstr("conn_broken_sec")) dialog.add_button(xstr("button_cancel"), 0) dialog.add_button(xstr("button_reconnect"), 1) dialog.set_default_response(1) result = dialog.run() dialog.hide() if result == 1: self.beginBusy(xstr("connect_busy")) self._reconnecting = True self._simulator.reconnect() else: self.reset() def enableFlightInfo(self): """Enable the flight info tab.""" self._flightInfo.enable() def cancelFlight(self): """Cancel the current file, if the user confirms it.""" dialog = gtk.MessageDialog(parent = self._mainWindow, type = MESSAGETYPE_QUESTION, message_format = xstr("cancelFlight_question")) dialog.add_button(xstr("button_no"), RESPONSETYPE_NO) dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES) dialog.set_title(WINDOW_TITLE_BASE) result = dialog.run() dialog.hide() if result==RESPONSETYPE_YES: self.reset() def reset(self): """Reset the GUI.""" self._disconnect() self._flightInfo.reset() self._flightInfo.disable() self.resetFlightStatus() self._weightHelp.reset() self._weightHelp.disable() self._notebook.set_current_page(0) self._logView.get_buffer().set_text("") if self.loggedIn: self._wizard.reloadFlights(self._handleReloadResult) else: self._wizard.reset(None) def _handleReloadResult(self, returned, result): """Handle the result of the reloading of the flights.""" self._wizard.reset(result if returned and result.loggedIn else None) def _disconnect(self, closingMessage = None, duration = 3): """Disconnect from the simulator if connected.""" self.stopMonitoring() self._clearHotkeys() if self._connected: if closingMessage is None: self._flight.simulator.disconnect() else: fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT, closingMessage, duration, disconnect = True) self._connected = False self._connecting = False self._reconnecting = False self._statusbar.updateConnection(False, False) self._weightHelp.disable() return True def addFlightLogLine(self, timeStr, line, isFault = False): """Write the given message line to the log.""" gobject.idle_add(self._writeLog, formatFlightLogLine(timeStr, line), self._logView, isFault) def updateFlightLogLine(self, index, timeStr, line): """Update the line with the given index.""" gobject.idle_add(self._updateFlightLogLine, index, formatFlightLogLine(timeStr, line)) def _updateFlightLogLine(self, index, line): """Replace the contents of the given line in the log.""" buffer = self._logView.get_buffer() startIter = buffer.get_iter_at_line(index) endIter = buffer.get_iter_at_line(index + 1) buffer.delete(startIter, endIter) buffer.insert(startIter, line) self._logView.scroll_mark_onscreen(buffer.get_insert()) def check(self, flight, aircraft, logger, oldState, state): """Update the data.""" gobject.idle_add(self._monitorWindow.setData, state) gobject.idle_add(self._statusbar.updateTime, state.timestamp) def resetFlightStatus(self): """Reset the status of the flight.""" self._statusbar.resetFlightStatus() self._statusbar.updateTime() self._statusIcon.resetFlightStatus() def setStage(self, stage): """Set the stage of the flight.""" gobject.idle_add(self._setStage, stage) def _setStage(self, stage): """Set the stage of the flight.""" self._statusbar.setStage(stage) self._statusIcon.setStage(stage) self._wizard.setStage(stage) if stage==const.STAGE_END: self._disconnect(closingMessage = "Flight plan closed. Welcome to %s" % \ (self.bookedFlight.arrivalICAO,), duration = 5) def setRating(self, rating): """Set the rating of the flight.""" gobject.idle_add(self._setRating, rating) def _setRating(self, rating): """Set the rating of the flight.""" self._statusbar.setRating(rating) self._statusIcon.setRating(rating) def setNoGo(self, reason): """Set the rating of the flight to No-Go with the given reason.""" gobject.idle_add(self._setNoGo, reason) def _setNoGo(self, reason): """Set the rating of the flight.""" self._statusbar.setNoGo(reason) self._statusIcon.setNoGo(reason) def _handleMainWindowState(self, window, event): """Hande a change in the state of the window""" iconified = gdk.WindowState.ICONIFIED if pygobject \ else gdk.WINDOW_STATE_ICONIFIED if self.config.hideMinimizedWindow and \ (event.changed_mask&iconified)!=0 and \ (event.new_window_state&iconified)!=0: self.hideMainWindow(savePosition = False) def raiseCallback(self): """Callback for the singleton handling code.""" gobject.idle_add(self.raiseMainWindow) def raiseMainWindow(self): """SHow the main window if invisible, and raise it.""" if not self._mainWindow.get_visible(): self.showMainWindow() self._mainWindow.present() def hideMainWindow(self, savePosition = True): """Hide the main window and save its position.""" if savePosition: (self._mainWindowX, self._mainWindowY) = \ self._mainWindow.get_window().get_root_origin() else: self._mainWindowX = self._mainWindowY = None self._mainWindow.hide() self._statusIcon.mainWindowHidden() return True def showMainWindow(self): """Show the main window at its former position.""" if self._mainWindowX is not None and self._mainWindowY is not None: self._mainWindow.move(self._mainWindowX, self._mainWindowY) self._mainWindow.show() self._mainWindow.deiconify() self._statusIcon.mainWindowShown() def toggleMainWindow(self): """Toggle the main window.""" if self._mainWindow.get_visible(): self.hideMainWindow() else: self.showMainWindow() def hideMonitorWindow(self, savePosition = True): """Hide the monitor window.""" if savePosition: (self._monitorWindowX, self._monitorWindowY) = \ self._monitorWindow.get_window().get_root_origin() else: self._monitorWindowX = self._monitorWindowY = None self._monitorWindow.hide() self._statusIcon.monitorWindowHidden() if self._showMonitorMenuItem.get_active(): self._selfToggling = True self._showMonitorMenuItem.set_active(False) return True def showMonitorWindow(self): """Show the monitor window.""" if self._monitorWindowX is not None and self._monitorWindowY is not None: self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY) self._monitorWindow.show_all() self._statusIcon.monitorWindowShown() if not self._showMonitorMenuItem.get_active(): self._selfToggling = True self._showMonitorMenuItem.set_active(True) def _toggleMonitorWindow(self, menuItem): if self._selfToggling: self._selfToggling = False elif self._monitorWindow.get_visible(): self.hideMonitorWindow() else: self.showMonitorWindow() def restart(self): """Quit and restart the application.""" self.toRestart = True self._quit(force = True) def flushStdIO(self): """Flush any text to the standard error that could not be logged.""" if self._stdioText: sys.__stderr__.write(self._stdioText) def writeStdIO(self, text): """Write the given text into standard I/O log.""" with self._stdioLock: self._stdioText += text gobject.idle_add(self._writeStdIO) def beginBusy(self, message): """Begin a period of background processing.""" self._wizard.set_sensitive(False) self._weightHelp.set_sensitive(False) self._mainWindow.get_window().set_cursor(self._busyCursor) self._statusbar.updateBusyState(message) def endBusy(self): """End a period of background processing.""" self._mainWindow.get_window().set_cursor(None) self._weightHelp.set_sensitive(True) self._wizard.set_sensitive(True) self._statusbar.updateBusyState(None) def initializeWeightHelp(self): """Initialize the weight help tab.""" self._weightHelp.reset() self._weightHelp.enable() def getFleetAsync(self, callback = None, force = None): """Get the fleet asynchronously.""" gobject.idle_add(self.getFleet, callback, force) def getFleet(self, callback = None, force = False): """Get the fleet. If force is False, and we already have a fleet retrieved, that one will be used.""" if self._fleet is None or force: self._fleetCallback = callback self.beginBusy(xstr("fleet_busy")) self.webHandler.getFleet(self._fleetResultCallback) else: callback(self._fleet) def _fleetResultCallback(self, returned, result): """Called when the fleet has been queried.""" gobject.idle_add(self._handleFleetResult, returned, result) def _handleFleetResult(self, returned, result): """Handle the fleet result.""" self.endBusy() if returned: self._fleet = result.fleet else: self._fleet = None dialog = gtk.MessageDialog(parent = self.mainWindow, type = MESSAGETYPE_ERROR, message_format = xstr("fleet_failed")) dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK) dialog.set_title(WINDOW_TITLE_BASE) dialog.run() dialog.hide() callback = self._fleetCallback self._fleetCallback = None if callback is not None: callback(self._fleet) self._fleetGateStatus.handleFleet(self._fleet) def updatePlane(self, tailNumber, status, gateNumber = None, callback = None): """Update the status of the given plane.""" self.beginBusy(xstr("fleet_update_busy")) self._updatePlaneCallback = callback self._updatePlaneTailNumber = tailNumber self._updatePlaneStatus = status self._updatePlaneGateNumber = gateNumber self.webHandler.updatePlane(self._updatePlaneResultCallback, tailNumber, status, gateNumber) def _updatePlaneResultCallback(self, returned, result): """Called when the status of a plane has been updated.""" gobject.idle_add(self._handleUpdatePlaneResult, returned, result) def _handleUpdatePlaneResult(self, returned, result): """Handle the plane update result.""" self.endBusy() if returned: success = result.success if success: if self._fleet is not None: self._fleet.updatePlane(self._updatePlaneTailNumber, self._updatePlaneStatus, self._updatePlaneGateNumber) self._fleetGateStatus.handleFleet(self._fleet) else: dialog = gtk.MessageDialog(parent = self.mainWindow, type = MESSAGETYPE_ERROR, message_format = xstr("fleet_update_failed")) dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT) dialog.set_title(WINDOW_TITLE_BASE) dialog.run() dialog.hide() success = None callback = self._updatePlaneCallback self._updatePlaneCallback = None if callback is not None: callback(success) def _writeStdIO(self): """Perform the real writing.""" with self._stdioLock: text = self._stdioText self._stdioText = "" if not text: return lines = text.splitlines() if text[-1]=="\n": text = "" else: text = lines[-1] lines = lines[:-1] now = datetime.datetime.now() timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second) for line in lines: #print >> sys.__stdout__, line if self._stdioStartingLine: self._writeLog(timeStr, self._debugLogView) self._writeLog(line + "\n", self._debugLogView) self._stdioStartingLine = True if text: #print >> sys.__stdout__, text, if self._stdioStartingLine: self._writeLog(timeStr, self._debugLogView) self._writeLog(text, self._debugLogView) self._stdioStartingLine = False def connectSimulator(self, aircraftType): """Connect to the simulator for the first time.""" self._logger.reset() self._flight = flight.Flight(self._logger, self) self._flight.flareTimeFromFS = self.config.flareTimeFromFS self._flight.aircraftType = aircraftType self._flight.aircraft = acft.Aircraft.create(self._flight) self._flight.aircraft._checkers.append(self) if self._simulator is None: self._simulator = fs.createSimulator(const.SIM_MSFS9, self) fs.setupMessageSending(self.config, self._simulator) self._setupTimeSync() self._flight.simulator = self._simulator self.beginBusy(xstr("connect_busy")) self._statusbar.updateConnection(self._connecting, self._connected) self._connecting = True self._simulator.connect(self._flight.aircraft) def startMonitoring(self): """Start monitoring.""" if not self._monitoring: self.simulator.startMonitoring() self._monitoring = True def stopMonitoring(self): """Stop monitoring.""" if self._monitoring: self.simulator.stopMonitoring() self._monitoring = False def _buildMenuBar(self, accelGroup): """Build the main menu bar.""" menuBar = gtk.MenuBar() fileMenuItem = gtk.MenuItem(xstr("menu_file")) fileMenu = gtk.Menu() fileMenuItem.set_submenu(fileMenu) menuBar.append(fileMenuItem) loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN) loadPIREPMenuItem.set_use_stock(True) loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP")) loadPIREPMenuItem.add_accelerator("activate", accelGroup, ord(xstr("menu_file_loadPIREP_key")), CONTROL_MASK, ACCEL_VISIBLE) loadPIREPMenuItem.connect("activate", self._loadPIREP) fileMenu.append(loadPIREPMenuItem) fileMenu.append(gtk.SeparatorMenuItem()) quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT) quitMenuItem.set_use_stock(True) quitMenuItem.set_label(xstr("menu_file_quit")) quitMenuItem.add_accelerator("activate", accelGroup, ord(xstr("menu_file_quit_key")), CONTROL_MASK, ACCEL_VISIBLE) quitMenuItem.connect("activate", self._quit) fileMenu.append(quitMenuItem) toolsMenuItem = gtk.MenuItem(xstr("menu_tools")) toolsMenu = gtk.Menu() toolsMenuItem.set_submenu(toolsMenu) menuBar.append(toolsMenuItem) checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY) checklistMenuItem.set_use_stock(True) checklistMenuItem.set_label(xstr("menu_tools_chklst")) checklistMenuItem.add_accelerator("activate", accelGroup, ord(xstr("menu_tools_chklst_key")), CONTROL_MASK, ACCEL_VISIBLE) checklistMenuItem.connect("activate", self._editChecklist) toolsMenu.append(checklistMenuItem) prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES) prefsMenuItem.set_use_stock(True) prefsMenuItem.set_label(xstr("menu_tools_prefs")) prefsMenuItem.add_accelerator("activate", accelGroup, ord(xstr("menu_tools_prefs_key")), CONTROL_MASK, ACCEL_VISIBLE) prefsMenuItem.connect("activate", self._editPreferences) toolsMenu.append(prefsMenuItem) viewMenuItem = gtk.MenuItem(xstr("menu_view")) viewMenu = gtk.Menu() viewMenuItem.set_submenu(viewMenu) menuBar.append(viewMenuItem) self._showMonitorMenuItem = gtk.CheckMenuItem() self._showMonitorMenuItem.set_label(xstr("menu_view_monitor")) self._showMonitorMenuItem.set_use_underline(True) self._showMonitorMenuItem.set_active(False) self._showMonitorMenuItem.add_accelerator("activate", accelGroup, ord(xstr("menu_view_monitor_key")), CONTROL_MASK, ACCEL_VISIBLE) self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow) viewMenu.append(self._showMonitorMenuItem) showDebugMenuItem = gtk.CheckMenuItem() showDebugMenuItem.set_label(xstr("menu_view_debug")) showDebugMenuItem.set_use_underline(True) showDebugMenuItem.set_active(False) showDebugMenuItem.add_accelerator("activate", accelGroup, ord(xstr("menu_view_debug_key")), CONTROL_MASK, ACCEL_VISIBLE) showDebugMenuItem.connect("toggled", self._toggleDebugLog) viewMenu.append(showDebugMenuItem) return menuBar def _toggleDebugLog(self, menuItem): """Toggle the debug log.""" if menuItem.get_active(): label = gtk.Label(xstr("tab_debug_log")) label.set_use_underline(True) label.set_tooltip_text(xstr("tab_debug_log_tooltip")) self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label) self._notebook.set_current_page(self._debugLogPage) else: self._notebook.remove_page(self._debugLogPage) def _buildLogWidget(self): """Build the widget for the log.""" alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0) alignment.set_padding(padding_top = 8, padding_bottom = 8, padding_left = 16, padding_right = 16) logScroller = gtk.ScrolledWindow() # FIXME: these should be constants in common logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject else gtk.POLICY_AUTOMATIC, gtk.PolicyType.AUTOMATIC if pygobject else gtk.POLICY_AUTOMATIC) logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject else gtk.SHADOW_IN) logView = gtk.TextView() logView.set_editable(False) logView.set_cursor_visible(False) logScroller.add(logView) logBox = gtk.VBox() logBox.pack_start(logScroller, True, True, 0) logBox.set_size_request(-1, 200) alignment.add(logBox) return (alignment, logView) def _writeLog(self, msg, logView, isFault = False): """Write the given message to the log.""" buffer = logView.get_buffer() appendTextBuffer(buffer, msg, isFault = isFault) logView.scroll_mark_onscreen(buffer.get_insert()) def _quit(self, what = None, force = False): """Quit from the application.""" if force: result=RESPONSETYPE_YES else: dialog = gtk.MessageDialog(parent = self._mainWindow, type = MESSAGETYPE_QUESTION, message_format = xstr("quit_question")) dialog.add_button(xstr("button_no"), RESPONSETYPE_NO) dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES) dialog.set_title(WINDOW_TITLE_BASE) result = dialog.run() dialog.hide() if result==RESPONSETYPE_YES: self._statusIcon.destroy() return gtk.main_quit() def _notebookPageSwitch(self, notebook, page, page_num): """Called when the current page of the notebook has changed.""" if page_num==0: gobject.idle_add(self._wizard.grabDefault) else: self._mainWindow.set_default(None) def _editChecklist(self, menuItem): """Callback for editing the checklists.""" self._checklistEditor.run() def _editPreferences(self, menuItem): """Callback for editing the preferences.""" self._clearHotkeys() self._preferences.run(self.config) self._setupTimeSync() self._listenHotkeys() def _setupTimeSync(self): """Enable or disable the simulator time synchronization based on the configuration.""" simulator = self._simulator if simulator is not None: if self.config.syncFSTime: simulator.enableTimeSync() else: simulator.disableTimeSync() def _loadPIREP(self, menuItem): """Load a PIREP for sending.""" dialog = self._getLoadPirepDialog() if self._lastLoadedPIREP: dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP)) else: pirepDirectory = self.config.pirepDirectory if pirepDirectory is not None: dialog.set_current_folder(pirepDirectory) result = dialog.run() dialog.hide() if result==RESPONSETYPE_OK: self._lastLoadedPIREP = text2unicode(dialog.get_filename()) pirep = PIREP.load(self._lastLoadedPIREP) if pirep is None: dialog = gtk.MessageDialog(parent = self._mainWindow, type = MESSAGETYPE_ERROR, message_format = xstr("loadPIREP_failed")) dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK) dialog.set_title(WINDOW_TITLE_BASE) dialog.format_secondary_markup(xstr("loadPIREP_failed_sec")) dialog.run() dialog.hide() else: dialog = self._getSendLoadedDialog(pirep) dialog.show_all() result = dialog.run() dialog.hide() if result==RESPONSETYPE_OK: self.sendPIREP(pirep) elif result==1: self._pirepViewer.setPIREP(pirep) self._pirepViewer.show_all() self._pirepViewer.run() self._pirepViewer.hide() def _getLoadPirepDialog(self): """Get the PIREP loading file chooser dialog. If it is not created yet, it will be created.""" if self._loadPIREPDialog is None: dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " + xstr("loadPIREP_browser_title"), action = FILE_CHOOSER_ACTION_OPEN, buttons = (gtk.STOCK_CANCEL, RESPONSETYPE_CANCEL, gtk.STOCK_OK, RESPONSETYPE_OK), parent = self._mainWindow) dialog.set_modal(True) filter = gtk.FileFilter() filter.set_name(xstr("file_filter_pireps")) filter.add_pattern("*.pirep") dialog.add_filter(filter) filter = gtk.FileFilter() filter.set_name(xstr("file_filter_all")) filter.add_pattern("*.*") dialog.add_filter(filter) self._loadPIREPDialog = dialog return self._loadPIREPDialog def _getSendLoadedDialog(self, pirep): """Get a dialog displaying the main information of the flight from the PIREP and providing Cancel and Send buttons.""" dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " + xstr("loadPIREP_send_title"), parent = self._mainWindow, flags = DIALOG_MODAL) contentArea = dialog.get_content_area() label = gtk.Label(xstr("loadPIREP_send_help")) alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5, xscale = 0.0, yscale = 0.0) alignment.set_padding(padding_top = 16, padding_bottom = 0, padding_left = 48, padding_right = 48) alignment.add(label) contentArea.pack_start(alignment, False, False, 8) table = gtk.Table(5, 2) tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5, xscale = 0.0, yscale = 0.0) tableAlignment.set_padding(padding_top = 0, padding_bottom = 32, padding_left = 48, padding_right = 48) table.set_row_spacings(4) table.set_col_spacings(16) tableAlignment.add(table) contentArea.pack_start(tableAlignment, True, True, 8) bookedFlight = pirep.bookedFlight label = gtk.Label("" + xstr("loadPIREP_send_flightno") + "") label.set_use_markup(True) labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 0, 1, 0, 1) label = gtk.Label(bookedFlight.callsign) labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 1, 2, 0, 1) label = gtk.Label("" + xstr("loadPIREP_send_date") + "") label.set_use_markup(True) labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 0, 1, 1, 2) label = gtk.Label(str(bookedFlight.departureTime.date())) labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 1, 2, 1, 2) label = gtk.Label("" + xstr("loadPIREP_send_from") + "") label.set_use_markup(True) labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 0, 1, 2, 3) label = gtk.Label(bookedFlight.departureICAO) labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 1, 2, 2, 3) label = gtk.Label("" + xstr("loadPIREP_send_to") + "") label.set_use_markup(True) labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 0, 1, 3, 4) label = gtk.Label(bookedFlight.arrivalICAO) labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 1, 2, 3, 4) label = gtk.Label("" + xstr("loadPIREP_send_rating") + "") label.set_use_markup(True) labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 0, 1, 4, 5) rating = pirep.rating label = gtk.Label() if rating<0: label.set_markup('NO GO') else: label.set_text("%.1f %%" % (rating,)) labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5, xscale = 0.0, yscale = 0.0) labelAlignment.add(label) table.attach(labelAlignment, 1, 2, 4, 5) dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT) dialog.add_button(xstr("viewPIREP"), 1) dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK) return dialog def sendPIREP(self, pirep, callback = None): """Send the given PIREP.""" self.beginBusy(xstr("sendPIREP_busy")) self._sendPIREPCallback = callback self.webHandler.sendPIREP(self._pirepSentCallback, pirep) def _pirepSentCallback(self, returned, result): """Callback for the PIREP sending result.""" gobject.idle_add(self._handlePIREPSent, returned, result) def _handlePIREPSent(self, returned, result): """Callback for the PIREP sending result.""" self.endBusy() secondaryMarkup = None type = MESSAGETYPE_ERROR if returned: if result.success: type = MESSAGETYPE_INFO messageFormat = xstr("sendPIREP_success") secondaryMarkup = xstr("sendPIREP_success_sec") elif result.alreadyFlown: messageFormat = xstr("sendPIREP_already") secondaryMarkup = xstr("sendPIREP_already_sec") elif result.notAvailable: messageFormat = xstr("sendPIREP_notavail") else: messageFormat = xstr("sendPIREP_unknown") secondaryMarkup = xstr("sendPIREP_unknown_sec") else: print "PIREP sending failed", result messageFormat = xstr("sendPIREP_failed") secondaryMarkup = xstr("sendPIREP_failed_sec") dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow, type = type, message_format = messageFormat) dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK) dialog.set_title(WINDOW_TITLE_BASE) if secondaryMarkup is not None: dialog.format_secondary_markup(secondaryMarkup) dialog.run() dialog.hide() callback = self._sendPIREPCallback self._sendPIREPCallback = None if callback is not None: callback(returned, result) def _listenHotkeys(self): """Setup the hotkeys based on the configuration.""" if self._hotkeySetID is None and self._simulator is not None: self._pilotHotkeyIndex = None self._checklistHotkeyIndex = None hotkeys = [] config = self.config if config.enableSounds and config.pilotControlsSounds: self._pilotHotkeyIndex = len(hotkeys) hotkeys.append(config.pilotHotkey) if config.enableChecklists: self._checklistHotkeyIndex = len(hotkeys) hotkeys.append(config.checklistHotkey) if hotkeys: self._hotkeySetID = \ self._simulator.listenHotkeys(hotkeys, self._handleHotkeys) def _clearHotkeys(self): """Clear the hotkeys.""" if self._hotkeySetID is not None: self._hotkeySetID=None self._simulator.clearHotkeys() def _handleHotkeys(self, id, hotkeys): """Handle the hotkeys.""" if id==self._hotkeySetID: for index in hotkeys: if index==self._pilotHotkeyIndex: print "gui.GUI._handleHotkeys: pilot hotkey pressed" self._flight.pilotHotkeyPressed() elif index==self._checklistHotkeyIndex: print "gui.GUI._handleHotkeys: checklist hotkey pressed" self._flight.checklistHotkeyPressed() else: print "gui.GUI._handleHotkeys: unhandled hotkey index:", index