source: src/mlx/gui/gui.py@ 303:8d5607e36aed

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

The number of the crew and the passengers as well as all payload weights can be edited

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