source: src/mlx/gui/gui.py@ 300:f101bd18f39d

Last change on this file since 300:f101bd18f39d was 300:f101bd18f39d, checked in by István Váradi <ivaradi@…>, 12 years ago

Added the module comments for the GUI

File size: 50.2 KB
RevLine 
[227]1# -*- coding: utf-8 -*-
[28]2
[29]3from statusicon import StatusIcon
[32]4from statusbar import Statusbar
[90]5from info import FlightInfo
[36]6from update import Updater
[29]7from mlx.gui.common import *
[42]8from mlx.gui.flight import Wizard
[77]9from mlx.gui.monitor import MonitorWindow
[117]10from mlx.gui.weighthelp import WeightHelp
[118]11from mlx.gui.gates import FleetGateStatus
[123]12from mlx.gui.prefs import Preferences
[172]13from mlx.gui.checklist import ChecklistEditor
[264]14from mlx.gui.callouts import ApproachCalloutsEditor
[220]15from mlx.gui.pirep import PIREPViewer
[28]16
17import mlx.const as const
18import mlx.fs as fs
19import mlx.flight as flight
20import mlx.logger as logger
21import mlx.acft as acft
[41]22import mlx.web as web
[182]23import mlx.singleton as singleton
[261]24import mlx.airports as airports
[227]25from mlx.i18n import xstr, getLanguage
[151]26from mlx.pirep import PIREP
[28]27
28import time
[38]29import threading
30import sys
[203]31import datetime
[227]32import webbrowser
[28]33
[77]34#------------------------------------------------------------------------------
35
[300]36## @package mlx.gui.gui
37#
38# The main GUI class.
39#
40# The \ref GUI class is the main class of the GUI. It is a connection listener,
41# and aggregates all the windows, the menu, etc. It maintains the connection to
42# the simulator as well as the flight object.
43
44#------------------------------------------------------------------------------
45
[28]46class GUI(fs.ConnectionListener):
47 """The main GUI class."""
[276]48 _authors = [ (u"Váradi", u"István", "prog_test"),
49 (u"Galyassy", u"Tamás", "negotiation"),
50 (u"Petrovszki", u"Gábor", "test"),
51 (u"Zsebényi-Loksa", u"Gergely", "test"),
52 (u"Kurják", u"Ákos", "test"),
53 (u"Nagy", u"Dániel", "test"),
54 (u"Radó", u"Iván", "test") ]
[227]55
[36]56 def __init__(self, programDirectory, config):
[28]57 """Construct the GUI."""
58 gobject.threads_init()
59
[36]60 self._programDirectory = programDirectory
[42]61 self.config = config
[28]62 self._connecting = False
[59]63 self._reconnecting = False
[28]64 self._connected = False
[96]65 self._logger = logger.Logger(self)
[28]66 self._flight = None
67 self._simulator = None
[59]68 self._monitoring = False
[130]69
[119]70 self._fleet = None
[130]71
[119]72 self._fleetCallback = None
[38]73
[130]74 self._updatePlaneCallback = None
75 self._updatePlaneTailNumber = None
76 self._updatePlaneStatus = None
77 self._updatePlaneGateNumber = None
78
[38]79 self._stdioLock = threading.Lock()
80 self._stdioText = ""
[203]81 self._stdioStartingLine = True
[28]82
[151]83 self._sendPIREPCallback = None
84
[41]85 self.webHandler = web.Handler()
86 self.webHandler.start()
87
[38]88 self.toRestart = False
89
[29]90 def build(self, iconDirectory):
[28]91 """Build the GUI."""
[29]92
[123]93 self._mainWindow = window = gtk.Window()
[105]94 window.set_title(WINDOW_TITLE_BASE)
[36]95 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
[202]96 window.set_resizable(False)
[249]97 window.connect("delete-event", self.deleteMainWindow)
[36]98 window.connect("window-state-event", self._handleMainWindowState)
[93]99 accelGroup = gtk.AccelGroup()
100 window.add_accel_group(accelGroup)
[28]101
102 mainVBox = gtk.VBox()
[36]103 window.add(mainVBox)
[28]104
[123]105 self._preferences = Preferences(self)
[172]106 self._checklistEditor = ChecklistEditor(self)
[264]107 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
[123]108
[93]109 menuBar = self._buildMenuBar(accelGroup)
110 mainVBox.pack_start(menuBar, False, False, 0)
111
[92]112 self._notebook = gtk.Notebook()
[93]113 mainVBox.pack_start(self._notebook, True, True, 4)
[142]114
[46]115 self._wizard = Wizard(self)
[107]116 label = gtk.Label(xstr("tab_flight"))
[46]117 label.set_use_underline(True)
[108]118 label.set_tooltip_text(xstr("tab_flight_tooltip"))
[92]119 self._notebook.append_page(self._wizard, label)
[42]120
[90]121 self._flightInfo = FlightInfo(self)
[107]122 label = gtk.Label(xstr("tab_flight_info"))
[90]123 label.set_use_underline(True)
[108]124 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
[92]125 self._notebook.append_page(self._flightInfo, label)
[93]126 self._flightInfo.disable()
[90]127
[117]128 self._weightHelp = WeightHelp(self)
129 label = gtk.Label(xstr("tab_weight_help"))
130 label.set_use_underline(True)
131 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
132 self._notebook.append_page(self._weightHelp, label)
133
[93]134 (logWidget, self._logView) = self._buildLogWidget()
[226]135 addFaultTag(self._logView.get_buffer())
[107]136 label = gtk.Label(xstr("tab_log"))
[46]137 label.set_use_underline(True)
[108]138 label.set_tooltip_text(xstr("tab_log_tooltip"))
[93]139 self._notebook.append_page(logWidget, label)
140
[118]141 self._fleetGateStatus = FleetGateStatus(self)
142 label = gtk.Label(xstr("tab_gates"))
143 label.set_use_underline(True)
144 label.set_tooltip_text(xstr("tab_gates_tooltip"))
145 self._notebook.append_page(self._fleetGateStatus, label)
146
[93]147 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
148 self._debugLogWidget.show_all()
[32]149
150 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
151
[281]152 self._statusbar = Statusbar(iconDirectory)
[32]153 mainVBox.pack_start(self._statusbar, False, False, 0)
154
[92]155 self._notebook.connect("switch-page", self._notebookPageSwitch)
[46]156
[77]157 self._monitorWindow = MonitorWindow(self, iconDirectory)
[93]158 self._monitorWindow.add_accel_group(accelGroup)
[77]159 self._monitorWindowX = None
160 self._monitorWindowY = None
[93]161 self._selfToggling = False
[77]162
[220]163 self._pirepViewer = PIREPViewer(self)
164
[46]165 window.show_all()
166 self._wizard.grabDefault()
[117]167 self._weightHelp.reset()
168 self._weightHelp.disable()
[28]169
[29]170 self._statusIcon = StatusIcon(iconDirectory, self)
[28]171
[49]172 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
173 else gdk.WATCH)
174
[151]175 self._loadPIREPDialog = None
176 self._lastLoadedPIREP = None
177
[168]178 self._hotkeySetID = None
[178]179 self._pilotHotkeyIndex = None
180 self._checklistHotkeyIndex = None
[168]181
[227]182 self._aboutDialog = None
183
[59]184 @property
[105]185 def mainWindow(self):
186 """Get the main window of the GUI."""
187 return self._mainWindow
188
189 @property
[97]190 def logger(self):
191 """Get the logger used by us."""
192 return self._logger
193
194 @property
[59]195 def simulator(self):
196 """Get the simulator used by us."""
197 return self._simulator
198
[71]199 @property
200 def flight(self):
201 """Get the flight being performed."""
202 return self._flight
[84]203
204 @property
[184]205 def entranceExam(self):
206 """Get whether an entrance exam is about to be taken."""
207 return self._wizard.entranceExam
[215]208
209 @property
210 def loggedIn(self):
211 """Indicate if the user has logged in properly."""
212 return self._wizard.loggedIn
[184]213
214 @property
[139]215 def loginResult(self):
216 """Get the result of the login."""
217 return self._wizard.loginResult
218
219 @property
[97]220 def bookedFlight(self):
221 """Get the booked flight selected, if any."""
222 return self._wizard.bookedFlight
223
224 @property
225 def cargoWeight(self):
226 """Get the cargo weight."""
227 return self._wizard.cargoWeight
228
229 @property
[84]230 def zfw(self):
231 """Get Zero-Fuel Weight calculated for the current flight."""
232 return self._wizard.zfw
233
234 @property
[97]235 def filedCruiseAltitude(self):
236 """Get cruise altitude filed for the current flight."""
237 return self._wizard.filedCruiseAltitude
238
239 @property
[84]240 def cruiseAltitude(self):
[97]241 """Get cruise altitude set for the current flight."""
[84]242 return self._wizard.cruiseAltitude
[97]243
244 @property
245 def route(self):
246 """Get the flight route."""
247 return self._wizard.route
248
249 @property
250 def departureMETAR(self):
251 """Get the METAR of the deprature airport."""
252 return self._wizard.departureMETAR
[84]253
254 @property
[97]255 def arrivalMETAR(self):
256 """Get the METAR of the deprature airport."""
257 return self._wizard.arrivalMETAR
258
259 @property
260 def departureRunway(self):
261 """Get the name of the departure runway."""
262 return self._wizard.departureRunway
263
264 @property
265 def sid(self):
266 """Get the SID."""
267 return self._wizard.sid
268
269 @property
[84]270 def v1(self):
271 """Get the V1 speed calculated for the flight."""
272 return self._wizard.v1
273
274 @property
275 def vr(self):
276 """Get the Vr speed calculated for the flight."""
277 return self._wizard.vr
278
279 @property
280 def v2(self):
281 """Get the V2 speed calculated for the flight."""
282 return self._wizard.v2
[71]283
[86]284 @property
[97]285 def arrivalRunway(self):
286 """Get the arrival runway."""
287 return self._wizard.arrivalRunway
288
289 @property
290 def star(self):
291 """Get the STAR."""
292 return self._wizard.star
293
294 @property
295 def transition(self):
296 """Get the transition."""
297 return self._wizard.transition
298
299 @property
300 def approachType(self):
301 """Get the approach type."""
302 return self._wizard.approachType
303
304 @property
[86]305 def vref(self):
306 """Get the Vref speed calculated for the flight."""
307 return self._wizard.vref
308
[97]309 @property
310 def flightType(self):
311 """Get the flight type."""
312 return self._wizard.flightType
313
314 @property
315 def online(self):
316 """Get whether the flight was online or not."""
317 return self._wizard.online
318
319 @property
320 def comments(self):
321 """Get the comments."""
322 return self._flightInfo.comments
323
324 @property
325 def flightDefects(self):
326 """Get the flight defects."""
327 return self._flightInfo.flightDefects
328
[99]329 @property
330 def delayCodes(self):
331 """Get the delay codes."""
332 return self._flightInfo.delayCodes
333
[28]334 def run(self):
335 """Run the GUI."""
[42]336 if self.config.autoUpdate:
[38]337 self._updater = Updater(self,
338 self._programDirectory,
[42]339 self.config.updateURL,
[36]340 self._mainWindow)
341 self._updater.start()
[182]342
343 singleton.raiseCallback = self.raiseCallback
[28]344 gtk.main()
[182]345 singleton.raiseCallback = None
[36]346
[91]347 self._disconnect()
[28]348
349 def connected(self, fsType, descriptor):
350 """Called when we have connected to the simulator."""
351 self._connected = True
[241]352 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
353 (const.VERSION, descriptor))
[133]354 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
355 "Welcome to MAVA Logger X " + const.VERSION)
[59]356 gobject.idle_add(self._handleConnected, fsType, descriptor)
357
358 def _handleConnected(self, fsType, descriptor):
359 """Called when the connection to the simulator has succeeded."""
360 self._statusbar.updateConnection(self._connecting, self._connected)
361 self.endBusy()
362 if not self._reconnecting:
363 self._wizard.connected(fsType, descriptor)
364 self._reconnecting = False
[168]365 self._listenHotkeys()
[59]366
367 def connectionFailed(self):
368 """Called when the connection failed."""
369 self._logger.untimedMessage("Connection to the simulator failed")
370 gobject.idle_add(self._connectionFailed)
371
372 def _connectionFailed(self):
373 """Called when the connection failed."""
374 self.endBusy()
375 self._statusbar.updateConnection(self._connecting, self._connected)
376
[105]377 dialog = gtk.MessageDialog(parent = self._mainWindow,
378 type = MESSAGETYPE_ERROR,
[108]379 message_format = xstr("conn_failed"))
380
[105]381 dialog.set_title(WINDOW_TITLE_BASE)
[108]382 dialog.format_secondary_markup(xstr("conn_failed_sec"))
[59]383
[108]384 dialog.add_button(xstr("button_cancel"), 0)
385 dialog.add_button(xstr("button_tryagain"), 1)
[59]386 dialog.set_default_response(1)
387
388 result = dialog.run()
389 dialog.hide()
390 if result == 1:
[107]391 self.beginBusy(xstr("connect_busy"))
[59]392 self._simulator.reconnect()
393 else:
[91]394 self.reset()
[28]395
396 def disconnected(self):
397 """Called when we have disconnected from the simulator."""
398 self._connected = False
399 self._logger.untimedMessage("Disconnected from the simulator")
[59]400
401 gobject.idle_add(self._disconnected)
402
403 def _disconnected(self):
404 """Called when we have disconnected from the simulator unexpectedly."""
405 self._statusbar.updateConnection(self._connecting, self._connected)
406
407 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
[108]408 message_format = xstr("conn_broken"),
[59]409 parent = self._mainWindow)
[105]410 dialog.set_title(WINDOW_TITLE_BASE)
[108]411 dialog.format_secondary_markup(xstr("conn_broken_sec"))
[59]412
[108]413 dialog.add_button(xstr("button_cancel"), 0)
414 dialog.add_button(xstr("button_reconnect"), 1)
[59]415 dialog.set_default_response(1)
416
417 result = dialog.run()
418 dialog.hide()
419 if result == 1:
[108]420 self.beginBusy(xstr("connect_busy"))
[59]421 self._reconnecting = True
422 self._simulator.reconnect()
423 else:
[91]424 self.reset()
425
[93]426 def enableFlightInfo(self):
427 """Enable the flight info tab."""
428 self._flightInfo.enable()
429
[208]430 def cancelFlight(self):
431 """Cancel the current file, if the user confirms it."""
432 dialog = gtk.MessageDialog(parent = self._mainWindow,
433 type = MESSAGETYPE_QUESTION,
434 message_format = xstr("cancelFlight_question"))
435
436 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
437 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
438
439 dialog.set_title(WINDOW_TITLE_BASE)
440 result = dialog.run()
441 dialog.hide()
442
443 if result==RESPONSETYPE_YES:
444 self.reset()
445
[91]446 def reset(self):
447 """Reset the GUI."""
448 self._disconnect()
[92]449
[91]450 self._flightInfo.reset()
[93]451 self._flightInfo.disable()
[91]452 self.resetFlightStatus()
[28]453
[117]454 self._weightHelp.reset()
455 self._weightHelp.disable()
[92]456 self._notebook.set_current_page(0)
457
458 self._logView.get_buffer().set_text("")
459
[215]460 if self.loggedIn:
461 self._wizard.reloadFlights(self._handleReloadResult)
462 else:
463 self._wizard.reset(None)
[208]464
465 def _handleReloadResult(self, returned, result):
466 """Handle the result of the reloading of the flights."""
467 self._wizard.reset(result if returned and result.loggedIn else None)
468
[152]469 def _disconnect(self, closingMessage = None, duration = 3):
[91]470 """Disconnect from the simulator if connected."""
[92]471 self.stopMonitoring()
[168]472 self._clearHotkeys()
[92]473
[91]474 if self._connected:
[152]475 if closingMessage is None:
476 self._flight.simulator.disconnect()
477 else:
478 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
479 closingMessage, duration,
480 disconnect = True)
[91]481 self._connected = False
482
483 self._connecting = False
484 self._reconnecting = False
485 self._statusbar.updateConnection(False, False)
[128]486 self._weightHelp.disable()
[134]487
488 return True
[91]489
[225]490 def addFlightLogLine(self, timeStr, line, isFault = False):
[96]491 """Write the given message line to the log."""
492 gobject.idle_add(self._writeLog,
[226]493 formatFlightLogLine(timeStr, line),
494 self._logView, isFault)
[96]495
[226]496 def updateFlightLogLine(self, index, timeStr, line):
[96]497 """Update the line with the given index."""
498 gobject.idle_add(self._updateFlightLogLine, index,
[226]499 formatFlightLogLine(timeStr, line))
[96]500
501 def _updateFlightLogLine(self, index, line):
502 """Replace the contents of the given line in the log."""
503 buffer = self._logView.get_buffer()
504 startIter = buffer.get_iter_at_line(index)
505 endIter = buffer.get_iter_at_line(index + 1)
506 buffer.delete(startIter, endIter)
507 buffer.insert(startIter, line)
508 self._logView.scroll_mark_onscreen(buffer.get_insert())
509
[28]510 def check(self, flight, aircraft, logger, oldState, state):
511 """Update the data."""
[77]512 gobject.idle_add(self._monitorWindow.setData, state)
[80]513 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
[28]514
[31]515 def resetFlightStatus(self):
516 """Reset the status of the flight."""
[32]517 self._statusbar.resetFlightStatus()
[80]518 self._statusbar.updateTime()
[31]519 self._statusIcon.resetFlightStatus()
520
521 def setStage(self, stage):
522 """Set the stage of the flight."""
523 gobject.idle_add(self._setStage, stage)
524
525 def _setStage(self, stage):
526 """Set the stage of the flight."""
[32]527 self._statusbar.setStage(stage)
[31]528 self._statusIcon.setStage(stage)
[84]529 self._wizard.setStage(stage)
[88]530 if stage==const.STAGE_END:
[261]531 welcomeMessage = \
532 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
[152]533 self._disconnect(closingMessage =
[261]534 "Flight plan closed. " + welcomeMessage,
[152]535 duration = 5)
[31]536
537 def setRating(self, rating):
538 """Set the rating of the flight."""
539 gobject.idle_add(self._setRating, rating)
540
541 def _setRating(self, rating):
542 """Set the rating of the flight."""
[32]543 self._statusbar.setRating(rating)
[31]544 self._statusIcon.setRating(rating)
545
546 def setNoGo(self, reason):
547 """Set the rating of the flight to No-Go with the given reason."""
548 gobject.idle_add(self._setNoGo, reason)
549
550 def _setNoGo(self, reason):
551 """Set the rating of the flight."""
[32]552 self._statusbar.setNoGo(reason)
[31]553 self._statusIcon.setNoGo(reason)
554
[29]555 def _handleMainWindowState(self, window, event):
556 """Hande a change in the state of the window"""
557 iconified = gdk.WindowState.ICONIFIED if pygobject \
558 else gdk.WINDOW_STATE_ICONIFIED
[246]559
560 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
561 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
562 self._statusIcon.mainWindowHidden()
563 else:
564 self._statusIcon.mainWindowShown()
565
566 if self.config.hideMinimizedWindow and not pygobject and \
567 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
568 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
[35]569 self.hideMainWindow(savePosition = False)
[246]570 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
571 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
572 self._mainWindow.present()
573
[182]574 def raiseCallback(self):
575 """Callback for the singleton handling code."""
576 gobject.idle_add(self.raiseMainWindow)
577
578 def raiseMainWindow(self):
[246]579 """Show the main window if invisible, and raise it."""
[182]580 if not self._mainWindow.get_visible():
581 self.showMainWindow()
582 self._mainWindow.present()
583
[249]584 def deleteMainWindow(self, window, event):
585 """Handle the delete event for the main window."""
586 if self.config.quitOnClose:
587 self._quit()
588 else:
589 self.hideMainWindow()
590 return True
591
[35]592 def hideMainWindow(self, savePosition = True):
[29]593 """Hide the main window and save its position."""
[35]594 if savePosition:
595 (self._mainWindowX, self._mainWindowY) = \
596 self._mainWindow.get_window().get_root_origin()
597 else:
598 self._mainWindowX = self._mainWindowY = None
[29]599 self._mainWindow.hide()
600 return True
601
602 def showMainWindow(self):
603 """Show the main window at its former position."""
[35]604 if self._mainWindowX is not None and self._mainWindowY is not None:
605 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
606
[246]607 if pygobject:
608 self._mainWindow.show()
609 else:
610 self._mainWindow.present()
[29]611 self._mainWindow.deiconify()
[35]612
[29]613 def toggleMainWindow(self):
614 """Toggle the main window."""
615 if self._mainWindow.get_visible():
616 self.hideMainWindow()
617 else:
618 self.showMainWindow()
[36]619
[77]620 def hideMonitorWindow(self, savePosition = True):
621 """Hide the monitor window."""
622 if savePosition:
623 (self._monitorWindowX, self._monitorWindowY) = \
624 self._monitorWindow.get_window().get_root_origin()
625 else:
626 self._monitorWindowX = self._monitorWindowY = None
627 self._monitorWindow.hide()
628 self._statusIcon.monitorWindowHidden()
[93]629 if self._showMonitorMenuItem.get_active():
630 self._selfToggling = True
631 self._showMonitorMenuItem.set_active(False)
[77]632 return True
633
634 def showMonitorWindow(self):
635 """Show the monitor window."""
636 if self._monitorWindowX is not None and self._monitorWindowY is not None:
637 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
638 self._monitorWindow.show_all()
639 self._statusIcon.monitorWindowShown()
[93]640 if not self._showMonitorMenuItem.get_active():
641 self._selfToggling = True
642 self._showMonitorMenuItem.set_active(True)
643
644 def _toggleMonitorWindow(self, menuItem):
645 if self._selfToggling:
646 self._selfToggling = False
647 elif self._monitorWindow.get_visible():
648 self.hideMonitorWindow()
649 else:
650 self.showMonitorWindow()
[77]651
[38]652 def restart(self):
653 """Quit and restart the application."""
654 self.toRestart = True
[81]655 self._quit(force = True)
[38]656
657 def flushStdIO(self):
658 """Flush any text to the standard error that could not be logged."""
659 if self._stdioText:
660 sys.__stderr__.write(self._stdioText)
661
[36]662 def writeStdIO(self, text):
663 """Write the given text into standard I/O log."""
[38]664 with self._stdioLock:
665 self._stdioText += text
666
667 gobject.idle_add(self._writeStdIO)
[36]668
[49]669 def beginBusy(self, message):
670 """Begin a period of background processing."""
[93]671 self._wizard.set_sensitive(False)
[117]672 self._weightHelp.set_sensitive(False)
[49]673 self._mainWindow.get_window().set_cursor(self._busyCursor)
674 self._statusbar.updateBusyState(message)
675
676 def endBusy(self):
677 """End a period of background processing."""
678 self._mainWindow.get_window().set_cursor(None)
[117]679 self._weightHelp.set_sensitive(True)
[93]680 self._wizard.set_sensitive(True)
[49]681 self._statusbar.updateBusyState(None)
682
[117]683 def initializeWeightHelp(self):
684 """Initialize the weight help tab."""
685 self._weightHelp.reset()
686 self._weightHelp.enable()
687
[134]688 def getFleetAsync(self, callback = None, force = None):
689 """Get the fleet asynchronously."""
690 gobject.idle_add(self.getFleet, callback, force)
691
[119]692 def getFleet(self, callback = None, force = False):
693 """Get the fleet.
694
695 If force is False, and we already have a fleet retrieved,
696 that one will be used."""
697 if self._fleet is None or force:
698 self._fleetCallback = callback
699 self.beginBusy(xstr("fleet_busy"))
700 self.webHandler.getFleet(self._fleetResultCallback)
701 else:
702 callback(self._fleet)
703
704 def _fleetResultCallback(self, returned, result):
705 """Called when the fleet has been queried."""
706 gobject.idle_add(self._handleFleetResult, returned, result)
707
708 def _handleFleetResult(self, returned, result):
709 """Handle the fleet result."""
710 self.endBusy()
711 if returned:
712 self._fleet = result.fleet
713 else:
714 self._fleet = None
715
[120]716 dialog = gtk.MessageDialog(parent = self.mainWindow,
[119]717 type = MESSAGETYPE_ERROR,
718 message_format = xstr("fleet_failed"))
[130]719 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
720 dialog.set_title(WINDOW_TITLE_BASE)
721 dialog.run()
722 dialog.hide()
723
724 callback = self._fleetCallback
725 self._fleetCallback = None
726 if callback is not None:
727 callback(self._fleet)
728 self._fleetGateStatus.handleFleet(self._fleet)
729
730 def updatePlane(self, tailNumber, status,
731 gateNumber = None, callback = None):
732 """Update the status of the given plane."""
733 self.beginBusy(xstr("fleet_update_busy"))
734
735 self._updatePlaneCallback = callback
736
737 self._updatePlaneTailNumber = tailNumber
738 self._updatePlaneStatus = status
739 self._updatePlaneGateNumber = gateNumber
740
741 self.webHandler.updatePlane(self._updatePlaneResultCallback,
742 tailNumber, status, gateNumber)
743
744 def _updatePlaneResultCallback(self, returned, result):
745 """Called when the status of a plane has been updated."""
746 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
747
748 def _handleUpdatePlaneResult(self, returned, result):
749 """Handle the plane update result."""
750 self.endBusy()
751 if returned:
752 success = result.success
753 if success:
754 if self._fleet is not None:
755 self._fleet.updatePlane(self._updatePlaneTailNumber,
756 self._updatePlaneStatus,
757 self._updatePlaneGateNumber)
758 self._fleetGateStatus.handleFleet(self._fleet)
759 else:
760 dialog = gtk.MessageDialog(parent = self.mainWindow,
761 type = MESSAGETYPE_ERROR,
762 message_format = xstr("fleet_update_failed"))
[124]763 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
[119]764 dialog.set_title(WINDOW_TITLE_BASE)
765 dialog.run()
766 dialog.hide()
767
[130]768 success = None
769
770 callback = self._updatePlaneCallback
771 self._updatePlaneCallback = None
772 if callback is not None:
773 callback(success)
[119]774
[38]775 def _writeStdIO(self):
[36]776 """Perform the real writing."""
[38]777 with self._stdioLock:
778 text = self._stdioText
779 self._stdioText = ""
780 if not text: return
781
[36]782 lines = text.splitlines()
783 if text[-1]=="\n":
784 text = ""
785 else:
786 text = lines[-1]
787 lines = lines[:-1]
[203]788
789 now = datetime.datetime.now()
790 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
[36]791
792 for line in lines:
[96]793 #print >> sys.__stdout__, line
[203]794 if self._stdioStartingLine:
795 self._writeLog(timeStr, self._debugLogView)
[93]796 self._writeLog(line + "\n", self._debugLogView)
[203]797 self._stdioStartingLine = True
[36]798
799 if text:
[96]800 #print >> sys.__stdout__, text,
[203]801 if self._stdioStartingLine:
802 self._writeLog(timeStr, self._debugLogView)
[93]803 self._writeLog(text, self._debugLogView)
[203]804 self._stdioStartingLine = False
[51]805
[59]806 def connectSimulator(self, aircraftType):
807 """Connect to the simulator for the first time."""
808 self._logger.reset()
809
810 self._flight = flight.Flight(self._logger, self)
[131]811 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
[59]812 self._flight.aircraftType = aircraftType
813 self._flight.aircraft = acft.Aircraft.create(self._flight)
814 self._flight.aircraft._checkers.append(self)
815
816 if self._simulator is None:
817 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
[133]818 fs.setupMessageSending(self.config, self._simulator)
[148]819 self._setupTimeSync()
820
[59]821 self._flight.simulator = self._simulator
822
[107]823 self.beginBusy(xstr("connect_busy"))
[59]824 self._statusbar.updateConnection(self._connecting, self._connected)
825
826 self._connecting = True
827 self._simulator.connect(self._flight.aircraft)
828
[70]829 def startMonitoring(self):
830 """Start monitoring."""
[88]831 if not self._monitoring:
832 self.simulator.startMonitoring()
833 self._monitoring = True
[70]834
835 def stopMonitoring(self):
836 """Stop monitoring."""
[88]837 if self._monitoring:
838 self.simulator.stopMonitoring()
839 self._monitoring = False
[70]840
[93]841 def _buildMenuBar(self, accelGroup):
842 """Build the main menu bar."""
843 menuBar = gtk.MenuBar()
844
[110]845 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
[93]846 fileMenu = gtk.Menu()
847 fileMenuItem.set_submenu(fileMenu)
848 menuBar.append(fileMenuItem)
849
[151]850 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
851 loadPIREPMenuItem.set_use_stock(True)
852 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
853 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
854 ord(xstr("menu_file_loadPIREP_key")),
855 CONTROL_MASK, ACCEL_VISIBLE)
856 loadPIREPMenuItem.connect("activate", self._loadPIREP)
857 fileMenu.append(loadPIREPMenuItem)
858
859 fileMenu.append(gtk.SeparatorMenuItem())
860
[93]861 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
862 quitMenuItem.set_use_stock(True)
[110]863 quitMenuItem.set_label(xstr("menu_file_quit"))
[93]864 quitMenuItem.add_accelerator("activate", accelGroup,
[110]865 ord(xstr("menu_file_quit_key")),
866 CONTROL_MASK, ACCEL_VISIBLE)
[93]867 quitMenuItem.connect("activate", self._quit)
868 fileMenu.append(quitMenuItem)
869
[123]870 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
871 toolsMenu = gtk.Menu()
872 toolsMenuItem.set_submenu(toolsMenu)
873 menuBar.append(toolsMenuItem)
874
[204]875 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
[172]876 checklistMenuItem.set_use_stock(True)
877 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
878 checklistMenuItem.add_accelerator("activate", accelGroup,
879 ord(xstr("menu_tools_chklst_key")),
880 CONTROL_MASK, ACCEL_VISIBLE)
881 checklistMenuItem.connect("activate", self._editChecklist)
882 toolsMenu.append(checklistMenuItem)
883
[264]884 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
885 approachCalloutsMenuItem.set_use_stock(True)
886 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
887 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
888 ord(xstr("menu_tools_callouts_key")),
889 CONTROL_MASK, ACCEL_VISIBLE)
890 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
891 toolsMenu.append(approachCalloutsMenuItem)
892
[204]893 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
[123]894 prefsMenuItem.set_use_stock(True)
895 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
896 prefsMenuItem.add_accelerator("activate", accelGroup,
897 ord(xstr("menu_tools_prefs_key")),
898 CONTROL_MASK, ACCEL_VISIBLE)
899 prefsMenuItem.connect("activate", self._editPreferences)
900 toolsMenu.append(prefsMenuItem)
[93]901
[110]902 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
[93]903 viewMenu = gtk.Menu()
904 viewMenuItem.set_submenu(viewMenu)
905 menuBar.append(viewMenuItem)
[28]906
[93]907 self._showMonitorMenuItem = gtk.CheckMenuItem()
[110]908 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
[93]909 self._showMonitorMenuItem.set_use_underline(True)
910 self._showMonitorMenuItem.set_active(False)
911 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
[110]912 ord(xstr("menu_view_monitor_key")),
913 CONTROL_MASK, ACCEL_VISIBLE)
[93]914 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
915 viewMenu.append(self._showMonitorMenuItem)
916
917 showDebugMenuItem = gtk.CheckMenuItem()
[110]918 showDebugMenuItem.set_label(xstr("menu_view_debug"))
[93]919 showDebugMenuItem.set_use_underline(True)
920 showDebugMenuItem.set_active(False)
921 showDebugMenuItem.add_accelerator("activate", accelGroup,
[110]922 ord(xstr("menu_view_debug_key")),
923 CONTROL_MASK, ACCEL_VISIBLE)
[93]924 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
925 viewMenu.append(showDebugMenuItem)
[28]926
[227]927 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
928 helpMenu = gtk.Menu()
929 helpMenuItem.set_submenu(helpMenu)
930 menuBar.append(helpMenuItem)
931
932 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
933 manualMenuItem.set_use_stock(True)
934 manualMenuItem.set_label(xstr("menu_help_manual"))
935 manualMenuItem.add_accelerator("activate", accelGroup,
936 ord(xstr("menu_help_manual_key")),
937 CONTROL_MASK, ACCEL_VISIBLE)
938 manualMenuItem.connect("activate", self._showManual)
939 helpMenu.append(manualMenuItem)
940
941 helpMenu.append(gtk.SeparatorMenuItem())
942
943 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
944 aboutMenuItem.set_use_stock(True)
945 aboutMenuItem.set_label(xstr("menu_help_about"))
946 aboutMenuItem.add_accelerator("activate", accelGroup,
947 ord(xstr("menu_help_about_key")),
948 CONTROL_MASK, ACCEL_VISIBLE)
949 aboutMenuItem.connect("activate", self._showAbout)
950 helpMenu.append(aboutMenuItem)
951
[93]952 return menuBar
[28]953
[93]954 def _toggleDebugLog(self, menuItem):
955 """Toggle the debug log."""
956 if menuItem.get_active():
[107]957 label = gtk.Label(xstr("tab_debug_log"))
[93]958 label.set_use_underline(True)
[110]959 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
[93]960 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
961 self._notebook.set_current_page(self._debugLogPage)
962 else:
963 self._notebook.remove_page(self._debugLogPage)
964
965 def _buildLogWidget(self):
966 """Build the widget for the log."""
967 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
968
969 alignment.set_padding(padding_top = 8, padding_bottom = 8,
970 padding_left = 16, padding_right = 16)
[28]971
972 logScroller = gtk.ScrolledWindow()
[93]973 # FIXME: these should be constants in common
974 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
975 else gtk.POLICY_AUTOMATIC,
976 gtk.PolicyType.AUTOMATIC if pygobject
977 else gtk.POLICY_AUTOMATIC)
978 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
979 else gtk.SHADOW_IN)
980 logView = gtk.TextView()
981 logView.set_editable(False)
[171]982 logView.set_cursor_visible(False)
[93]983 logScroller.add(logView)
[28]984
985 logBox = gtk.VBox()
986 logBox.pack_start(logScroller, True, True, 0)
987 logBox.set_size_request(-1, 200)
988
[93]989 alignment.add(logBox)
[28]990
[93]991 return (alignment, logView)
[28]992
[226]993 def _writeLog(self, msg, logView, isFault = False):
[28]994 """Write the given message to the log."""
[93]995 buffer = logView.get_buffer()
[226]996 appendTextBuffer(buffer, msg, isFault = isFault)
[93]997 logView.scroll_mark_onscreen(buffer.get_insert())
[28]998
[81]999 def _quit(self, what = None, force = False):
[38]1000 """Quit from the application."""
[81]1001 if force:
1002 result=RESPONSETYPE_YES
1003 else:
[105]1004 dialog = gtk.MessageDialog(parent = self._mainWindow,
1005 type = MESSAGETYPE_QUESTION,
[110]1006 message_format = xstr("quit_question"))
1007
[124]1008 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1009 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1010
[105]1011 dialog.set_title(WINDOW_TITLE_BASE)
[81]1012 result = dialog.run()
1013 dialog.hide()
[76]1014
1015 if result==RESPONSETYPE_YES:
1016 self._statusIcon.destroy()
1017 return gtk.main_quit()
[38]1018
[46]1019 def _notebookPageSwitch(self, notebook, page, page_num):
1020 """Called when the current page of the notebook has changed."""
1021 if page_num==0:
[48]1022 gobject.idle_add(self._wizard.grabDefault)
[46]1023 else:
1024 self._mainWindow.set_default(None)
[123]1025
[172]1026 def _editChecklist(self, menuItem):
1027 """Callback for editing the checklists."""
1028 self._checklistEditor.run()
1029
[264]1030 def _editApproachCallouts(self, menuItem):
1031 """Callback for editing the approach callouts."""
1032 self._approachCalloutsEditor.run()
1033
[123]1034 def _editPreferences(self, menuItem):
1035 """Callback for editing the preferences."""
[168]1036 self._clearHotkeys()
[123]1037 self._preferences.run(self.config)
[148]1038 self._setupTimeSync()
[168]1039 self._listenHotkeys()
[148]1040
1041 def _setupTimeSync(self):
1042 """Enable or disable the simulator time synchronization based on the
1043 configuration."""
1044 simulator = self._simulator
1045 if simulator is not None:
1046 if self.config.syncFSTime:
1047 simulator.enableTimeSync()
1048 else:
1049 simulator.disableTimeSync()
[151]1050
1051 def _loadPIREP(self, menuItem):
1052 """Load a PIREP for sending."""
1053 dialog = self._getLoadPirepDialog()
1054
1055 if self._lastLoadedPIREP:
1056 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1057 else:
1058 pirepDirectory = self.config.pirepDirectory
1059 if pirepDirectory is not None:
1060 dialog.set_current_folder(pirepDirectory)
1061
1062 result = dialog.run()
1063 dialog.hide()
1064
1065 if result==RESPONSETYPE_OK:
[164]1066 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
[151]1067
1068 pirep = PIREP.load(self._lastLoadedPIREP)
1069 if pirep is None:
1070 dialog = gtk.MessageDialog(parent = self._mainWindow,
1071 type = MESSAGETYPE_ERROR,
1072 message_format = xstr("loadPIREP_failed"))
1073 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1074 dialog.set_title(WINDOW_TITLE_BASE)
1075 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1076 dialog.run()
1077 dialog.hide()
1078 else:
1079 dialog = self._getSendLoadedDialog(pirep)
1080 dialog.show_all()
[239]1081 while True:
1082 result = dialog.run()
[151]1083
[239]1084 if result==RESPONSETYPE_OK:
1085 self.sendPIREP(pirep)
1086 elif result==1:
1087 self._pirepViewer.setPIREP(pirep)
1088 self._pirepViewer.show_all()
1089 self._pirepViewer.run()
1090 self._pirepViewer.hide()
1091 else:
1092 break
1093
1094 dialog.hide()
[151]1095
1096 def _getLoadPirepDialog(self):
1097 """Get the PIREP loading file chooser dialog.
1098
1099 If it is not created yet, it will be created."""
1100 if self._loadPIREPDialog is None:
1101 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1102 xstr("loadPIREP_browser_title"),
1103 action = FILE_CHOOSER_ACTION_OPEN,
1104 buttons = (gtk.STOCK_CANCEL,
1105 RESPONSETYPE_CANCEL,
1106 gtk.STOCK_OK, RESPONSETYPE_OK),
1107 parent = self._mainWindow)
1108 dialog.set_modal(True)
1109
1110
1111 filter = gtk.FileFilter()
[184]1112 filter.set_name(xstr("file_filter_pireps"))
[151]1113 filter.add_pattern("*.pirep")
1114 dialog.add_filter(filter)
1115
1116 filter = gtk.FileFilter()
[184]1117 filter.set_name(xstr("file_filter_all"))
[151]1118 filter.add_pattern("*.*")
1119 dialog.add_filter(filter)
1120
1121 self._loadPIREPDialog = dialog
1122
1123 return self._loadPIREPDialog
1124
1125 def _getSendLoadedDialog(self, pirep):
1126 """Get a dialog displaying the main information of the flight from the
1127 PIREP and providing Cancel and Send buttons."""
1128 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1129 xstr("loadPIREP_send_title"),
1130 parent = self._mainWindow,
1131 flags = DIALOG_MODAL)
1132
1133 contentArea = dialog.get_content_area()
1134
1135 label = gtk.Label(xstr("loadPIREP_send_help"))
1136 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1137 xscale = 0.0, yscale = 0.0)
1138 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1139 padding_left = 48, padding_right = 48)
1140 alignment.add(label)
1141 contentArea.pack_start(alignment, False, False, 8)
1142
1143 table = gtk.Table(5, 2)
1144 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1145 xscale = 0.0, yscale = 0.0)
1146 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1147 padding_left = 48, padding_right = 48)
1148 table.set_row_spacings(4)
1149 table.set_col_spacings(16)
1150 tableAlignment.add(table)
1151 contentArea.pack_start(tableAlignment, True, True, 8)
1152
1153 bookedFlight = pirep.bookedFlight
1154
1155 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1156 label.set_use_markup(True)
1157 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1158 xscale = 0.0, yscale = 0.0)
1159 labelAlignment.add(label)
1160 table.attach(labelAlignment, 0, 1, 0, 1)
1161
1162 label = gtk.Label(bookedFlight.callsign)
1163 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1164 xscale = 0.0, yscale = 0.0)
1165 labelAlignment.add(label)
1166 table.attach(labelAlignment, 1, 2, 0, 1)
1167
1168 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1169 label.set_use_markup(True)
1170 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1171 xscale = 0.0, yscale = 0.0)
1172 labelAlignment.add(label)
1173 table.attach(labelAlignment, 0, 1, 1, 2)
1174
1175 label = gtk.Label(str(bookedFlight.departureTime.date()))
1176 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1177 xscale = 0.0, yscale = 0.0)
1178 labelAlignment.add(label)
1179 table.attach(labelAlignment, 1, 2, 1, 2)
1180
1181 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1182 label.set_use_markup(True)
1183 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1184 xscale = 0.0, yscale = 0.0)
1185 labelAlignment.add(label)
1186 table.attach(labelAlignment, 0, 1, 2, 3)
1187
1188 label = gtk.Label(bookedFlight.departureICAO)
1189 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1190 xscale = 0.0, yscale = 0.0)
1191 labelAlignment.add(label)
1192 table.attach(labelAlignment, 1, 2, 2, 3)
1193
1194 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1195 label.set_use_markup(True)
1196 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1197 xscale = 0.0, yscale = 0.0)
1198 labelAlignment.add(label)
1199 table.attach(labelAlignment, 0, 1, 3, 4)
1200
1201 label = gtk.Label(bookedFlight.arrivalICAO)
1202 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1203 xscale = 0.0, yscale = 0.0)
1204 labelAlignment.add(label)
1205 table.attach(labelAlignment, 1, 2, 3, 4)
1206
1207 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1208 label.set_use_markup(True)
1209 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1210 xscale = 0.0, yscale = 0.0)
1211 labelAlignment.add(label)
1212 table.attach(labelAlignment, 0, 1, 4, 5)
1213
1214 rating = pirep.rating
1215 label = gtk.Label()
1216 if rating<0:
1217 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1218 else:
1219 label.set_text("%.1f %%" % (rating,))
1220
1221 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1222 xscale = 0.0, yscale = 0.0)
1223 labelAlignment.add(label)
1224 table.attach(labelAlignment, 1, 2, 4, 5)
1225
1226 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
[220]1227 dialog.add_button(xstr("viewPIREP"), 1)
[151]1228 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1229
1230 return dialog
1231
1232 def sendPIREP(self, pirep, callback = None):
1233 """Send the given PIREP."""
1234 self.beginBusy(xstr("sendPIREP_busy"))
1235 self._sendPIREPCallback = callback
1236 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1237
1238 def _pirepSentCallback(self, returned, result):
1239 """Callback for the PIREP sending result."""
1240 gobject.idle_add(self._handlePIREPSent, returned, result)
1241
1242 def _handlePIREPSent(self, returned, result):
1243 """Callback for the PIREP sending result."""
1244 self.endBusy()
1245 secondaryMarkup = None
1246 type = MESSAGETYPE_ERROR
1247 if returned:
1248 if result.success:
1249 type = MESSAGETYPE_INFO
1250 messageFormat = xstr("sendPIREP_success")
1251 secondaryMarkup = xstr("sendPIREP_success_sec")
1252 elif result.alreadyFlown:
1253 messageFormat = xstr("sendPIREP_already")
1254 secondaryMarkup = xstr("sendPIREP_already_sec")
1255 elif result.notAvailable:
1256 messageFormat = xstr("sendPIREP_notavail")
1257 else:
1258 messageFormat = xstr("sendPIREP_unknown")
1259 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1260 else:
1261 print "PIREP sending failed", result
1262 messageFormat = xstr("sendPIREP_failed")
1263 secondaryMarkup = xstr("sendPIREP_failed_sec")
1264
1265 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1266 type = type, message_format = messageFormat)
1267 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1268 dialog.set_title(WINDOW_TITLE_BASE)
1269 if secondaryMarkup is not None:
1270 dialog.format_secondary_markup(secondaryMarkup)
1271
1272 dialog.run()
1273 dialog.hide()
1274
1275 callback = self._sendPIREPCallback
1276 self._sendPIREPCallback = None
1277 if callback is not None:
1278 callback(returned, result)
[168]1279
1280 def _listenHotkeys(self):
1281 """Setup the hotkeys based on the configuration."""
1282 if self._hotkeySetID is None and self._simulator is not None:
[178]1283 self._pilotHotkeyIndex = None
1284 self._checklistHotkeyIndex = None
1285
1286 hotkeys = []
1287
1288 config = self.config
1289 if config.enableSounds and config.pilotControlsSounds:
1290 self._pilotHotkeyIndex = len(hotkeys)
1291 hotkeys.append(config.pilotHotkey)
1292
1293 if config.enableChecklists:
1294 self._checklistHotkeyIndex = len(hotkeys)
1295 hotkeys.append(config.checklistHotkey)
1296
1297 if hotkeys:
1298 self._hotkeySetID = \
1299 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
[168]1300
1301 def _clearHotkeys(self):
1302 """Clear the hotkeys."""
1303 if self._hotkeySetID is not None:
1304 self._hotkeySetID=None
1305 self._simulator.clearHotkeys()
1306
1307 def _handleHotkeys(self, id, hotkeys):
1308 """Handle the hotkeys."""
1309 if id==self._hotkeySetID:
[170]1310 for index in hotkeys:
[178]1311 if index==self._pilotHotkeyIndex:
1312 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
[170]1313 self._flight.pilotHotkeyPressed()
[178]1314 elif index==self._checklistHotkeyIndex:
1315 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1316 self._flight.checklistHotkeyPressed()
[170]1317 else:
[178]1318 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
[227]1319
1320 def _showManual(self, menuitem):
1321 """Show the user's manual."""
1322 webbrowser.open(url ="file://" +
1323 os.path.join(self._programDirectory, "doc", "manual",
1324 getLanguage(), "index.html"),
1325 new = 1)
1326
1327 def _showAbout(self, menuitem):
1328 """Show the about dialog."""
1329 dialog = self._getAboutDialog()
1330 dialog.show_all()
1331 dialog.run()
1332 dialog.hide()
1333
1334 def _getAboutDialog(self):
1335 """Get the about dialog.
1336
1337 If it does not exist yet, it will be created."""
1338 if self._aboutDialog is None:
[276]1339 dialog = gtk.AboutDialog()
[227]1340 dialog.set_transient_for(self._mainWindow)
1341 dialog.set_modal(True)
1342
1343 logoPath = os.path.join(self._programDirectory, "logo.png")
1344 logo = pixbuf_new_from_file(logoPath)
1345 dialog.set_logo(logo)
1346
1347 dialog.set_program_name(PROGRAM_NAME)
1348 dialog.set_version(const.VERSION)
1349 dialog.set_copyright("(c) 2012 by István Váradi")
[237]1350 dialog.set_website("http://mlx.varadiistvan.hu")
1351 dialog.set_website_label(xstr("about_website"))
[227]1352
1353 isHungarian = getLanguage()=="hu"
1354 authors = []
1355 for (familyName, firstName, role) in GUI._authors:
[276]1356 author = "%s %s" % \
1357 (familyName if isHungarian else firstName,
1358 firstName if isHungarian else familyName)
1359 role = xstr("about_role_" + role)
1360 authors.append(author + " (" + role + ")")
[227]1361 dialog.set_authors(authors)
1362
1363 dialog.set_license(xstr("about_license"))
1364
[237]1365 if not pygobject:
1366 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1367
[276]1368 self._aboutDialog = dialog
1369
[227]1370 return self._aboutDialog
[237]1371
1372 def _showAboutURL(self, dialog, link, user_data):
1373 """Show the about URL."""
1374 webbrowser.open(url = link, new = 1)
Note: See TracBrowser for help on using the repository browser.