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
Line 
1# -*- coding: utf-8 -*-
2
3from statusicon import StatusIcon
4from statusbar import Statusbar
5from info import FlightInfo
6from update import Updater
7from mlx.gui.common import *
8from mlx.gui.flight import Wizard
9from mlx.gui.monitor import MonitorWindow
10from mlx.gui.weighthelp import WeightHelp
11from mlx.gui.gates import FleetGateStatus
12from mlx.gui.prefs import Preferences
13from mlx.gui.checklist import ChecklistEditor
14from mlx.gui.callouts import ApproachCalloutsEditor
15from mlx.gui.pirep import PIREPViewer
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
22import mlx.web as web
23import mlx.singleton as singleton
24import mlx.airports as airports
25from mlx.i18n import xstr, getLanguage
26from mlx.pirep import PIREP
27
28import time
29import threading
30import sys
31import datetime
32import webbrowser
33
34#------------------------------------------------------------------------------
35
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
46class GUI(fs.ConnectionListener):
47 """The main GUI class."""
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") ]
55
56 def __init__(self, programDirectory, config):
57 """Construct the GUI."""
58 gobject.threads_init()
59
60 self._programDirectory = programDirectory
61 self.config = config
62 self._connecting = False
63 self._reconnecting = False
64 self._connected = False
65 self._logger = logger.Logger(self)
66 self._flight = None
67 self._simulator = None
68 self._monitoring = False
69
70 self._fleet = None
71
72 self._fleetCallback = None
73
74 self._updatePlaneCallback = None
75 self._updatePlaneTailNumber = None
76 self._updatePlaneStatus = None
77 self._updatePlaneGateNumber = None
78
79 self._stdioLock = threading.Lock()
80 self._stdioText = ""
81 self._stdioStartingLine = True
82
83 self._sendPIREPCallback = None
84
85 self.webHandler = web.Handler()
86 self.webHandler.start()
87
88 self.toRestart = False
89
90 def build(self, iconDirectory):
91 """Build the GUI."""
92
93 self._mainWindow = window = gtk.Window()
94 window.set_title(WINDOW_TITLE_BASE)
95 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
96 window.set_resizable(False)
97 window.connect("delete-event", self.deleteMainWindow)
98 window.connect("window-state-event", self._handleMainWindowState)
99 accelGroup = gtk.AccelGroup()
100 window.add_accel_group(accelGroup)
101
102 mainVBox = gtk.VBox()
103 window.add(mainVBox)
104
105 self._preferences = Preferences(self)
106 self._checklistEditor = ChecklistEditor(self)
107 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
108
109 menuBar = self._buildMenuBar(accelGroup)
110 mainVBox.pack_start(menuBar, False, False, 0)
111
112 self._notebook = gtk.Notebook()
113 mainVBox.pack_start(self._notebook, True, True, 4)
114
115 self._wizard = Wizard(self)
116 label = gtk.Label(xstr("tab_flight"))
117 label.set_use_underline(True)
118 label.set_tooltip_text(xstr("tab_flight_tooltip"))
119 self._notebook.append_page(self._wizard, label)
120
121 self._flightInfo = FlightInfo(self)
122 label = gtk.Label(xstr("tab_flight_info"))
123 label.set_use_underline(True)
124 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
125 self._notebook.append_page(self._flightInfo, label)
126 self._flightInfo.disable()
127
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
134 (logWidget, self._logView) = self._buildLogWidget()
135 addFaultTag(self._logView.get_buffer())
136 label = gtk.Label(xstr("tab_log"))
137 label.set_use_underline(True)
138 label.set_tooltip_text(xstr("tab_log_tooltip"))
139 self._notebook.append_page(logWidget, label)
140
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
147 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
148 self._debugLogWidget.show_all()
149
150 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
151
152 self._statusbar = Statusbar(iconDirectory)
153 mainVBox.pack_start(self._statusbar, False, False, 0)
154
155 self._notebook.connect("switch-page", self._notebookPageSwitch)
156
157 self._monitorWindow = MonitorWindow(self, iconDirectory)
158 self._monitorWindow.add_accel_group(accelGroup)
159 self._monitorWindowX = None
160 self._monitorWindowY = None
161 self._selfToggling = False
162
163 self._pirepViewer = PIREPViewer(self)
164
165 window.show_all()
166 self._wizard.grabDefault()
167 self._weightHelp.reset()
168 self._weightHelp.disable()
169
170 self._statusIcon = StatusIcon(iconDirectory, self)
171
172 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
173 else gdk.WATCH)
174
175 self._loadPIREPDialog = None
176 self._lastLoadedPIREP = None
177
178 self._hotkeySetID = None
179 self._pilotHotkeyIndex = None
180 self._checklistHotkeyIndex = None
181
182 self._aboutDialog = None
183
184 @property
185 def mainWindow(self):
186 """Get the main window of the GUI."""
187 return self._mainWindow
188
189 @property
190 def logger(self):
191 """Get the logger used by us."""
192 return self._logger
193
194 @property
195 def simulator(self):
196 """Get the simulator used by us."""
197 return self._simulator
198
199 @property
200 def flight(self):
201 """Get the flight being performed."""
202 return self._flight
203
204 @property
205 def entranceExam(self):
206 """Get whether an entrance exam is about to be taken."""
207 return self._wizard.entranceExam
208
209 @property
210 def loggedIn(self):
211 """Indicate if the user has logged in properly."""
212 return self._wizard.loggedIn
213
214 @property
215 def loginResult(self):
216 """Get the result of the login."""
217 return self._wizard.loginResult
218
219 @property
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
230 def zfw(self):
231 """Get Zero-Fuel Weight calculated for the current flight."""
232 return self._wizard.zfw
233
234 @property
235 def filedCruiseAltitude(self):
236 """Get cruise altitude filed for the current flight."""
237 return self._wizard.filedCruiseAltitude
238
239 @property
240 def cruiseAltitude(self):
241 """Get cruise altitude set for the current flight."""
242 return self._wizard.cruiseAltitude
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
253
254 @property
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
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
283
284 @property
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
305 def vref(self):
306 """Get the Vref speed calculated for the flight."""
307 return self._wizard.vref
308
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
329 @property
330 def delayCodes(self):
331 """Get the delay codes."""
332 return self._flightInfo.delayCodes
333
334 def run(self):
335 """Run the GUI."""
336 if self.config.autoUpdate:
337 self._updater = Updater(self,
338 self._programDirectory,
339 self.config.updateURL,
340 self._mainWindow)
341 self._updater.start()
342
343 singleton.raiseCallback = self.raiseCallback
344 gtk.main()
345 singleton.raiseCallback = None
346
347 self._disconnect()
348
349 def connected(self, fsType, descriptor):
350 """Called when we have connected to the simulator."""
351 self._connected = True
352 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
353 (const.VERSION, descriptor))
354 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
355 "Welcome to MAVA Logger X " + const.VERSION)
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
365 self._listenHotkeys()
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
377 dialog = gtk.MessageDialog(parent = self._mainWindow,
378 type = MESSAGETYPE_ERROR,
379 message_format = xstr("conn_failed"))
380
381 dialog.set_title(WINDOW_TITLE_BASE)
382 dialog.format_secondary_markup(xstr("conn_failed_sec"))
383
384 dialog.add_button(xstr("button_cancel"), 0)
385 dialog.add_button(xstr("button_tryagain"), 1)
386 dialog.set_default_response(1)
387
388 result = dialog.run()
389 dialog.hide()
390 if result == 1:
391 self.beginBusy(xstr("connect_busy"))
392 self._simulator.reconnect()
393 else:
394 self.reset()
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")
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,
408 message_format = xstr("conn_broken"),
409 parent = self._mainWindow)
410 dialog.set_title(WINDOW_TITLE_BASE)
411 dialog.format_secondary_markup(xstr("conn_broken_sec"))
412
413 dialog.add_button(xstr("button_cancel"), 0)
414 dialog.add_button(xstr("button_reconnect"), 1)
415 dialog.set_default_response(1)
416
417 result = dialog.run()
418 dialog.hide()
419 if result == 1:
420 self.beginBusy(xstr("connect_busy"))
421 self._reconnecting = True
422 self._simulator.reconnect()
423 else:
424 self.reset()
425
426 def enableFlightInfo(self):
427 """Enable the flight info tab."""
428 self._flightInfo.enable()
429
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
446 def reset(self):
447 """Reset the GUI."""
448 self._disconnect()
449
450 self._flightInfo.reset()
451 self._flightInfo.disable()
452 self.resetFlightStatus()
453
454 self._weightHelp.reset()
455 self._weightHelp.disable()
456 self._notebook.set_current_page(0)
457
458 self._logView.get_buffer().set_text("")
459
460 if self.loggedIn:
461 self._wizard.reloadFlights(self._handleReloadResult)
462 else:
463 self._wizard.reset(None)
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
469 def _disconnect(self, closingMessage = None, duration = 3):
470 """Disconnect from the simulator if connected."""
471 self.stopMonitoring()
472 self._clearHotkeys()
473
474 if self._connected:
475 if closingMessage is None:
476 self._flight.simulator.disconnect()
477 else:
478 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
479 closingMessage, duration,
480 disconnect = True)
481 self._connected = False
482
483 self._connecting = False
484 self._reconnecting = False
485 self._statusbar.updateConnection(False, False)
486 self._weightHelp.disable()
487
488 return True
489
490 def addFlightLogLine(self, timeStr, line, isFault = False):
491 """Write the given message line to the log."""
492 gobject.idle_add(self._writeLog,
493 formatFlightLogLine(timeStr, line),
494 self._logView, isFault)
495
496 def updateFlightLogLine(self, index, timeStr, line):
497 """Update the line with the given index."""
498 gobject.idle_add(self._updateFlightLogLine, index,
499 formatFlightLogLine(timeStr, line))
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
510 def check(self, flight, aircraft, logger, oldState, state):
511 """Update the data."""
512 gobject.idle_add(self._monitorWindow.setData, state)
513 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
514
515 def resetFlightStatus(self):
516 """Reset the status of the flight."""
517 self._statusbar.resetFlightStatus()
518 self._statusbar.updateTime()
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."""
527 self._statusbar.setStage(stage)
528 self._statusIcon.setStage(stage)
529 self._wizard.setStage(stage)
530 if stage==const.STAGE_END:
531 welcomeMessage = \
532 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
533 self._disconnect(closingMessage =
534 "Flight plan closed. " + welcomeMessage,
535 duration = 5)
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."""
543 self._statusbar.setRating(rating)
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."""
552 self._statusbar.setNoGo(reason)
553 self._statusIcon.setNoGo(reason)
554
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
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:
569 self.hideMainWindow(savePosition = False)
570 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
571 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
572 self._mainWindow.present()
573
574 def raiseCallback(self):
575 """Callback for the singleton handling code."""
576 gobject.idle_add(self.raiseMainWindow)
577
578 def raiseMainWindow(self):
579 """Show the main window if invisible, and raise it."""
580 if not self._mainWindow.get_visible():
581 self.showMainWindow()
582 self._mainWindow.present()
583
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
592 def hideMainWindow(self, savePosition = True):
593 """Hide the main window and save its position."""
594 if savePosition:
595 (self._mainWindowX, self._mainWindowY) = \
596 self._mainWindow.get_window().get_root_origin()
597 else:
598 self._mainWindowX = self._mainWindowY = None
599 self._mainWindow.hide()
600 return True
601
602 def showMainWindow(self):
603 """Show the main window at its former position."""
604 if self._mainWindowX is not None and self._mainWindowY is not None:
605 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
606
607 if pygobject:
608 self._mainWindow.show()
609 else:
610 self._mainWindow.present()
611 self._mainWindow.deiconify()
612
613 def toggleMainWindow(self):
614 """Toggle the main window."""
615 if self._mainWindow.get_visible():
616 self.hideMainWindow()
617 else:
618 self.showMainWindow()
619
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()
629 if self._showMonitorMenuItem.get_active():
630 self._selfToggling = True
631 self._showMonitorMenuItem.set_active(False)
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()
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()
651
652 def restart(self):
653 """Quit and restart the application."""
654 self.toRestart = True
655 self._quit(force = True)
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
662 def writeStdIO(self, text):
663 """Write the given text into standard I/O log."""
664 with self._stdioLock:
665 self._stdioText += text
666
667 gobject.idle_add(self._writeStdIO)
668
669 def beginBusy(self, message):
670 """Begin a period of background processing."""
671 self._wizard.set_sensitive(False)
672 self._weightHelp.set_sensitive(False)
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)
679 self._weightHelp.set_sensitive(True)
680 self._wizard.set_sensitive(True)
681 self._statusbar.updateBusyState(None)
682
683 def initializeWeightHelp(self):
684 """Initialize the weight help tab."""
685 self._weightHelp.reset()
686 self._weightHelp.enable()
687
688 def getFleetAsync(self, callback = None, force = None):
689 """Get the fleet asynchronously."""
690 gobject.idle_add(self.getFleet, callback, force)
691
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
716 dialog = gtk.MessageDialog(parent = self.mainWindow,
717 type = MESSAGETYPE_ERROR,
718 message_format = xstr("fleet_failed"))
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"))
763 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
764 dialog.set_title(WINDOW_TITLE_BASE)
765 dialog.run()
766 dialog.hide()
767
768 success = None
769
770 callback = self._updatePlaneCallback
771 self._updatePlaneCallback = None
772 if callback is not None:
773 callback(success)
774
775 def _writeStdIO(self):
776 """Perform the real writing."""
777 with self._stdioLock:
778 text = self._stdioText
779 self._stdioText = ""
780 if not text: return
781
782 lines = text.splitlines()
783 if text[-1]=="\n":
784 text = ""
785 else:
786 text = lines[-1]
787 lines = lines[:-1]
788
789 now = datetime.datetime.now()
790 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
791
792 for line in lines:
793 #print >> sys.__stdout__, line
794 if self._stdioStartingLine:
795 self._writeLog(timeStr, self._debugLogView)
796 self._writeLog(line + "\n", self._debugLogView)
797 self._stdioStartingLine = True
798
799 if text:
800 #print >> sys.__stdout__, text,
801 if self._stdioStartingLine:
802 self._writeLog(timeStr, self._debugLogView)
803 self._writeLog(text, self._debugLogView)
804 self._stdioStartingLine = False
805
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)
811 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
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)
818 fs.setupMessageSending(self.config, self._simulator)
819 self._setupTimeSync()
820
821 self._flight.simulator = self._simulator
822
823 self.beginBusy(xstr("connect_busy"))
824 self._statusbar.updateConnection(self._connecting, self._connected)
825
826 self._connecting = True
827 self._simulator.connect(self._flight.aircraft)
828
829 def startMonitoring(self):
830 """Start monitoring."""
831 if not self._monitoring:
832 self.simulator.startMonitoring()
833 self._monitoring = True
834
835 def stopMonitoring(self):
836 """Stop monitoring."""
837 if self._monitoring:
838 self.simulator.stopMonitoring()
839 self._monitoring = False
840
841 def _buildMenuBar(self, accelGroup):
842 """Build the main menu bar."""
843 menuBar = gtk.MenuBar()
844
845 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
846 fileMenu = gtk.Menu()
847 fileMenuItem.set_submenu(fileMenu)
848 menuBar.append(fileMenuItem)
849
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
861 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
862 quitMenuItem.set_use_stock(True)
863 quitMenuItem.set_label(xstr("menu_file_quit"))
864 quitMenuItem.add_accelerator("activate", accelGroup,
865 ord(xstr("menu_file_quit_key")),
866 CONTROL_MASK, ACCEL_VISIBLE)
867 quitMenuItem.connect("activate", self._quit)
868 fileMenu.append(quitMenuItem)
869
870 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
871 toolsMenu = gtk.Menu()
872 toolsMenuItem.set_submenu(toolsMenu)
873 menuBar.append(toolsMenuItem)
874
875 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
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
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
893 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
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)
901
902 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
903 viewMenu = gtk.Menu()
904 viewMenuItem.set_submenu(viewMenu)
905 menuBar.append(viewMenuItem)
906
907 self._showMonitorMenuItem = gtk.CheckMenuItem()
908 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
909 self._showMonitorMenuItem.set_use_underline(True)
910 self._showMonitorMenuItem.set_active(False)
911 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
912 ord(xstr("menu_view_monitor_key")),
913 CONTROL_MASK, ACCEL_VISIBLE)
914 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
915 viewMenu.append(self._showMonitorMenuItem)
916
917 showDebugMenuItem = gtk.CheckMenuItem()
918 showDebugMenuItem.set_label(xstr("menu_view_debug"))
919 showDebugMenuItem.set_use_underline(True)
920 showDebugMenuItem.set_active(False)
921 showDebugMenuItem.add_accelerator("activate", accelGroup,
922 ord(xstr("menu_view_debug_key")),
923 CONTROL_MASK, ACCEL_VISIBLE)
924 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
925 viewMenu.append(showDebugMenuItem)
926
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
952 return menuBar
953
954 def _toggleDebugLog(self, menuItem):
955 """Toggle the debug log."""
956 if menuItem.get_active():
957 label = gtk.Label(xstr("tab_debug_log"))
958 label.set_use_underline(True)
959 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
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)
971
972 logScroller = gtk.ScrolledWindow()
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)
982 logView.set_cursor_visible(False)
983 logScroller.add(logView)
984
985 logBox = gtk.VBox()
986 logBox.pack_start(logScroller, True, True, 0)
987 logBox.set_size_request(-1, 200)
988
989 alignment.add(logBox)
990
991 return (alignment, logView)
992
993 def _writeLog(self, msg, logView, isFault = False):
994 """Write the given message to the log."""
995 buffer = logView.get_buffer()
996 appendTextBuffer(buffer, msg, isFault = isFault)
997 logView.scroll_mark_onscreen(buffer.get_insert())
998
999 def _quit(self, what = None, force = False):
1000 """Quit from the application."""
1001 if force:
1002 result=RESPONSETYPE_YES
1003 else:
1004 dialog = gtk.MessageDialog(parent = self._mainWindow,
1005 type = MESSAGETYPE_QUESTION,
1006 message_format = xstr("quit_question"))
1007
1008 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1009 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1010
1011 dialog.set_title(WINDOW_TITLE_BASE)
1012 result = dialog.run()
1013 dialog.hide()
1014
1015 if result==RESPONSETYPE_YES:
1016 self._statusIcon.destroy()
1017 return gtk.main_quit()
1018
1019 def _notebookPageSwitch(self, notebook, page, page_num):
1020 """Called when the current page of the notebook has changed."""
1021 if page_num==0:
1022 gobject.idle_add(self._wizard.grabDefault)
1023 else:
1024 self._mainWindow.set_default(None)
1025
1026 def _editChecklist(self, menuItem):
1027 """Callback for editing the checklists."""
1028 self._checklistEditor.run()
1029
1030 def _editApproachCallouts(self, menuItem):
1031 """Callback for editing the approach callouts."""
1032 self._approachCalloutsEditor.run()
1033
1034 def _editPreferences(self, menuItem):
1035 """Callback for editing the preferences."""
1036 self._clearHotkeys()
1037 self._preferences.run(self.config)
1038 self._setupTimeSync()
1039 self._listenHotkeys()
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()
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:
1066 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
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()
1081 while True:
1082 result = dialog.run()
1083
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()
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()
1112 filter.set_name(xstr("file_filter_pireps"))
1113 filter.add_pattern("*.pirep")
1114 dialog.add_filter(filter)
1115
1116 filter = gtk.FileFilter()
1117 filter.set_name(xstr("file_filter_all"))
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)
1227 dialog.add_button(xstr("viewPIREP"), 1)
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)
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:
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)
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:
1310 for index in hotkeys:
1311 if index==self._pilotHotkeyIndex:
1312 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1313 self._flight.pilotHotkeyPressed()
1314 elif index==self._checklistHotkeyIndex:
1315 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1316 self._flight.checklistHotkeyPressed()
1317 else:
1318 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
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:
1339 dialog = gtk.AboutDialog()
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")
1350 dialog.set_website("http://mlx.varadiistvan.hu")
1351 dialog.set_website_label(xstr("about_website"))
1352
1353 isHungarian = getLanguage()=="hu"
1354 authors = []
1355 for (familyName, firstName, role) in GUI._authors:
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 + ")")
1361 dialog.set_authors(authors)
1362
1363 dialog.set_license(xstr("about_license"))
1364
1365 if not pygobject:
1366 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1367
1368 self._aboutDialog = dialog
1369
1370 return self._aboutDialog
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.