source: src/mlx/gui/gui.py@ 227:50c3ae93007d

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

Added a Help menu with the manual and the about dialog

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