source: src/mlx/gui/gui.py@ 393:977efa7a177b

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

Added the automatic saving of the PIREP (re #163)

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