source: src/mlx/gui/gui.py@ 391:0f2e90eae832

Last change on this file since 391:0f2e90eae832 was 391:0f2e90eae832, checked in by István Váradi <ivaradi@…>, 11 years ago

Added support for logging the state of the anti-ice system (re #159)

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