source: src/mlx/gui/gui.py@ 363:2fa78f40f929

Last change on this file since 363:2fa78f40f929 was 349:41c486c8a0b4, checked in by István Váradi <ivaradi@…>, 12 years ago

The basic strobe-less RTO handling logic works (#143)

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