source: src/mlx/gui/gui.py@ 702:b76cdc5ff2dc

cef
Last change on this file since 702:b76cdc5ff2dc was 687:bb05f0618b5b, checked in by István Váradi <ivaradi@…>, 9 years ago

SimBrief works basically (re #279)

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