source: src/mlx/gui/gui.py@ 682:08e73d58a9e4

cef
Last change on this file since 682:08e73d58a9e4 was 682:08e73d58a9e4, checked in by István Váradi <ivaradi@…>, 7 years ago

CEF is now started from Selenium

File size: 58.7 KB
Line 
1# -*- coding: utf-8 -*-
2
3from statusicon import StatusIcon
4from statusbar import Statusbar
5from info import FlightInfo
6from update import Updater
7from mlx.gui.common import *
8from mlx.gui.flight import Wizard
9from mlx.gui.monitor import MonitorWindow
10from mlx.gui.weighthelp import WeightHelp
11from mlx.gui.gates import FleetGateStatus
12from mlx.gui.prefs import Preferences
13from mlx.gui.checklist import ChecklistEditor
14from mlx.gui.callouts import ApproachCalloutsEditor
15from mlx.gui.pirep import PIREPViewer
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 endBusy(self):
816 """End a period of background processing."""
817 self._mainWindow.get_window().set_cursor(None)
818 self._weightHelp.set_sensitive(True)
819 self._wizard.set_sensitive(True)
820 self._statusbar.updateBusyState(None)
821
822 def initializeWeightHelp(self):
823 """Initialize the weight help tab."""
824 self._weightHelp.reset()
825 self._weightHelp.enable()
826
827 def getFleetAsync(self, callback = None, force = None):
828 """Get the fleet asynchronously."""
829 gobject.idle_add(self.getFleet, callback, force)
830
831 def getFleet(self, callback = None, force = False):
832 """Get the fleet.
833
834 If force is False, and we already have a fleet retrieved,
835 that one will be used."""
836 if self._fleet is None or force:
837 self._fleetCallback = callback
838 self.beginBusy(xstr("fleet_busy"))
839 self.webHandler.getFleet(self._fleetResultCallback)
840 else:
841 callback(self._fleet)
842
843 def commentsChanged(self):
844 """Indicate that the comments have changed."""
845 self._wizard.commentsChanged()
846
847 def delayCodesChanged(self):
848 """Called when the delay codes have changed."""
849 self._wizard.delayCodesChanged()
850
851 def faultExplanationsChanged(self):
852 """Called when the status of the explanations of the faults have
853 changed."""
854 self._wizard.faultExplanationsChanged()
855
856 def updateRTO(self, inLoop = False):
857 """Indicate that the RTO state should be updated."""
858 if inLoop:
859 self._wizard.updateRTO()
860 else:
861 gobject.idle_add(self.updateRTO, True)
862
863 def rtoToggled(self, indicated):
864 """Called when the user has toggled the RTO checkbox."""
865 self._flight.rtoToggled(indicated)
866
867 def _fleetResultCallback(self, returned, result):
868 """Called when the fleet has been queried."""
869 gobject.idle_add(self._handleFleetResult, returned, result)
870
871 def _handleFleetResult(self, returned, result):
872 """Handle the fleet result."""
873 self.endBusy()
874 if returned:
875 self._fleet = result.fleet
876 else:
877 self._fleet = None
878
879 dialog = gtk.MessageDialog(parent = self.mainWindow,
880 type = MESSAGETYPE_ERROR,
881 message_format = xstr("fleet_failed"))
882 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
883 dialog.set_title(WINDOW_TITLE_BASE)
884 dialog.run()
885 dialog.hide()
886
887 callback = self._fleetCallback
888 self._fleetCallback = None
889 if callback is not None:
890 callback(self._fleet)
891 self._fleetGateStatus.handleFleet(self._fleet)
892
893 def updatePlane(self, tailNumber, status,
894 gateNumber = None, callback = None):
895 """Update the status of the given plane."""
896 self.beginBusy(xstr("fleet_update_busy"))
897
898 self._updatePlaneCallback = callback
899
900 self._updatePlaneTailNumber = tailNumber
901 self._updatePlaneStatus = status
902 self._updatePlaneGateNumber = gateNumber
903
904 self.webHandler.updatePlane(self._updatePlaneResultCallback,
905 tailNumber, status, gateNumber)
906
907 def _updatePlaneResultCallback(self, returned, result):
908 """Called when the status of a plane has been updated."""
909 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
910
911 def _handleUpdatePlaneResult(self, returned, result):
912 """Handle the plane update result."""
913 self.endBusy()
914 if returned:
915 success = result.success
916 if success:
917 if self._fleet is not None:
918 self._fleet.updatePlane(self._updatePlaneTailNumber,
919 self._updatePlaneStatus,
920 self._updatePlaneGateNumber)
921 self._fleetGateStatus.handleFleet(self._fleet)
922 else:
923 dialog = gtk.MessageDialog(parent = self.mainWindow,
924 type = MESSAGETYPE_ERROR,
925 message_format = xstr("fleet_update_failed"))
926 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
927 dialog.set_title(WINDOW_TITLE_BASE)
928 dialog.run()
929 dialog.hide()
930
931 success = None
932
933 callback = self._updatePlaneCallback
934 self._updatePlaneCallback = None
935 if callback is not None:
936 callback(success)
937
938 def _writeStdIO(self):
939 """Perform the real writing."""
940 with self._stdioLock:
941 text = self._stdioText
942 self._stdioText = ""
943 if not text: return
944
945 lines = text.splitlines()
946 if text[-1]=="\n":
947 text = ""
948 else:
949 text = lines[-1]
950 lines = lines[:-1]
951
952 now = datetime.datetime.now()
953 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
954
955 for line in lines:
956 #print >> sys.__stdout__, line
957 if self._stdioStartingLine:
958 self._writeLog(timeStr, self._debugLogView)
959 self._writeLog(line + "\n", self._debugLogView)
960 self._stdioStartingLine = True
961
962 if text:
963 #print >> sys.__stdout__, text,
964 if self._stdioStartingLine:
965 self._writeLog(timeStr, self._debugLogView)
966 self._writeLog(text, self._debugLogView)
967 self._stdioStartingLine = False
968
969 def connectSimulator(self, aircraftType, simulatorType):
970 """Connect to the simulator for the first time."""
971 self._logger.reset()
972
973 self._flight = flight.Flight(self._logger, self)
974 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
975 self._flight.aircraftType = aircraftType
976 self._flight.aircraft = acft.Aircraft.create(self._flight)
977 self._flight.aircraft._checkers.append(self)
978
979 if self._simulator is None:
980 self._simulator = fs.createSimulator(simulatorType, self)
981 fs.setupMessageSending(self.config, self._simulator)
982 self._setupTimeSync()
983
984 self._flight.simulator = self._simulator
985
986 self.beginBusy(xstr("connect_busy"))
987 self._statusbar.updateConnection(self._connecting, self._connected)
988
989 self._connecting = True
990 self._simulator.connect(self._flight.aircraft)
991
992 def startMonitoring(self):
993 """Start monitoring."""
994 if not self._monitoring:
995 self.simulator.startMonitoring()
996 self._monitoring = True
997
998 def stopMonitoring(self):
999 """Stop monitoring."""
1000 if self._monitoring:
1001 self.simulator.stopMonitoring()
1002 self._monitoring = False
1003
1004 def cruiseLevelChanged(self):
1005 """Called when the cruise level is changed in the flight wizard."""
1006 if self._flight is not None:
1007 return self._flight.cruiseLevelChanged()
1008 else:
1009 return False
1010
1011 def _buildMenuBar(self, accelGroup):
1012 """Build the main menu bar."""
1013 menuBar = gtk.MenuBar()
1014
1015 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
1016 fileMenu = gtk.Menu()
1017 fileMenuItem.set_submenu(fileMenu)
1018 menuBar.append(fileMenuItem)
1019
1020 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
1021 loadPIREPMenuItem.set_use_stock(True)
1022 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
1023 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
1024 ord(xstr("menu_file_loadPIREP_key")),
1025 CONTROL_MASK, ACCEL_VISIBLE)
1026 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1027 fileMenu.append(loadPIREPMenuItem)
1028
1029 fileMenu.append(gtk.SeparatorMenuItem())
1030
1031 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
1032 quitMenuItem.set_use_stock(True)
1033 quitMenuItem.set_label(xstr("menu_file_quit"))
1034 quitMenuItem.add_accelerator("activate", accelGroup,
1035 ord(xstr("menu_file_quit_key")),
1036 CONTROL_MASK, ACCEL_VISIBLE)
1037 quitMenuItem.connect("activate", self._quit)
1038 fileMenu.append(quitMenuItem)
1039
1040 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
1041 toolsMenu = gtk.Menu()
1042 toolsMenuItem.set_submenu(toolsMenu)
1043 menuBar.append(toolsMenuItem)
1044
1045 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
1046 checklistMenuItem.set_use_stock(True)
1047 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1048 checklistMenuItem.add_accelerator("activate", accelGroup,
1049 ord(xstr("menu_tools_chklst_key")),
1050 CONTROL_MASK, ACCEL_VISIBLE)
1051 checklistMenuItem.connect("activate", self._editChecklist)
1052 toolsMenu.append(checklistMenuItem)
1053
1054 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
1055 approachCalloutsMenuItem.set_use_stock(True)
1056 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1057 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1058 ord(xstr("menu_tools_callouts_key")),
1059 CONTROL_MASK, ACCEL_VISIBLE)
1060 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1061 toolsMenu.append(approachCalloutsMenuItem)
1062
1063 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
1064 prefsMenuItem.set_use_stock(True)
1065 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1066 prefsMenuItem.add_accelerator("activate", accelGroup,
1067 ord(xstr("menu_tools_prefs_key")),
1068 CONTROL_MASK, ACCEL_VISIBLE)
1069 prefsMenuItem.connect("activate", self._editPreferences)
1070 toolsMenu.append(prefsMenuItem)
1071
1072 toolsMenu.append(gtk.SeparatorMenuItem())
1073
1074 bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
1075 bugReportMenuItem.set_use_stock(True)
1076 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1077 bugReportMenuItem.add_accelerator("activate", accelGroup,
1078 ord(xstr("menu_tools_bugreport_key")),
1079 CONTROL_MASK, ACCEL_VISIBLE)
1080 bugReportMenuItem.connect("activate", self._reportBug)
1081 toolsMenu.append(bugReportMenuItem)
1082
1083 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
1084 viewMenu = gtk.Menu()
1085 viewMenuItem.set_submenu(viewMenu)
1086 menuBar.append(viewMenuItem)
1087
1088 self._showMonitorMenuItem = gtk.CheckMenuItem()
1089 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1090 self._showMonitorMenuItem.set_use_underline(True)
1091 self._showMonitorMenuItem.set_active(False)
1092 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1093 ord(xstr("menu_view_monitor_key")),
1094 CONTROL_MASK, ACCEL_VISIBLE)
1095 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1096 viewMenu.append(self._showMonitorMenuItem)
1097
1098 showDebugMenuItem = gtk.CheckMenuItem()
1099 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1100 showDebugMenuItem.set_use_underline(True)
1101 showDebugMenuItem.set_active(False)
1102 showDebugMenuItem.add_accelerator("activate", accelGroup,
1103 ord(xstr("menu_view_debug_key")),
1104 CONTROL_MASK, ACCEL_VISIBLE)
1105 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1106 viewMenu.append(showDebugMenuItem)
1107
1108 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
1109 helpMenu = gtk.Menu()
1110 helpMenuItem.set_submenu(helpMenu)
1111 menuBar.append(helpMenuItem)
1112
1113 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
1114 manualMenuItem.set_use_stock(True)
1115 manualMenuItem.set_label(xstr("menu_help_manual"))
1116 manualMenuItem.add_accelerator("activate", accelGroup,
1117 ord(xstr("menu_help_manual_key")),
1118 CONTROL_MASK, ACCEL_VISIBLE)
1119 manualMenuItem.connect("activate", self._showManual)
1120 helpMenu.append(manualMenuItem)
1121
1122 helpMenu.append(gtk.SeparatorMenuItem())
1123
1124 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
1125 aboutMenuItem.set_use_stock(True)
1126 aboutMenuItem.set_label(xstr("menu_help_about"))
1127 aboutMenuItem.add_accelerator("activate", accelGroup,
1128 ord(xstr("menu_help_about_key")),
1129 CONTROL_MASK, ACCEL_VISIBLE)
1130 aboutMenuItem.connect("activate", self._showAbout)
1131 helpMenu.append(aboutMenuItem)
1132
1133 return menuBar
1134
1135 def _toggleDebugLog(self, menuItem):
1136 """Toggle the debug log."""
1137 if menuItem.get_active():
1138 label = gtk.Label(xstr("tab_debug_log"))
1139 label.set_use_underline(True)
1140 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1141 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1142 self._notebook.set_current_page(self._debugLogPage)
1143 else:
1144 self._notebook.remove_page(self._debugLogPage)
1145
1146 def _buildLogWidget(self):
1147 """Build the widget for the log."""
1148 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
1149
1150 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1151 padding_left = 16, padding_right = 16)
1152
1153 logScroller = gtk.ScrolledWindow()
1154 # FIXME: these should be constants in common
1155 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1156 else gtk.POLICY_AUTOMATIC,
1157 gtk.PolicyType.AUTOMATIC if pygobject
1158 else gtk.POLICY_AUTOMATIC)
1159 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
1160 else gtk.SHADOW_IN)
1161 logView = gtk.TextView()
1162 logView.set_editable(False)
1163 logView.set_cursor_visible(False)
1164 logScroller.add(logView)
1165
1166 logBox = gtk.VBox()
1167 logBox.pack_start(logScroller, True, True, 0)
1168 logBox.set_size_request(-1, 200)
1169
1170 alignment.add(logBox)
1171
1172 return (alignment, logView)
1173
1174 def _writeLog(self, msg, logView, isFault = False):
1175 """Write the given message to the log."""
1176 buffer = logView.get_buffer()
1177 appendTextBuffer(buffer, msg, isFault = isFault)
1178 logView.scroll_mark_onscreen(buffer.get_insert())
1179
1180 def _quit(self, what = None, force = False):
1181 """Quit from the application."""
1182 if force:
1183 result=RESPONSETYPE_YES
1184 else:
1185 dialog = gtk.MessageDialog(parent = self._mainWindow,
1186 type = MESSAGETYPE_QUESTION,
1187 message_format = xstr("quit_question"))
1188
1189 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1190 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1191
1192 dialog.set_title(WINDOW_TITLE_BASE)
1193 result = dialog.run()
1194 dialog.hide()
1195
1196 if result==RESPONSETYPE_YES:
1197 self._statusIcon.destroy()
1198 return gtk.main_quit()
1199
1200 def _notebookPageSwitch(self, notebook, page, page_num):
1201 """Called when the current page of the notebook has changed."""
1202 if page_num==0:
1203 gobject.idle_add(self._wizard.grabDefault)
1204 else:
1205 self._mainWindow.set_default(None)
1206
1207 def isWizardActive(self):
1208 """Determine if the flight wizard is active."""
1209 return self._notebook.get_current_page()==0
1210
1211 def _editChecklist(self, menuItem):
1212 """Callback for editing the checklists."""
1213 self._checklistEditor.run()
1214
1215 def _editApproachCallouts(self, menuItem):
1216 """Callback for editing the approach callouts."""
1217 self._approachCalloutsEditor.run()
1218
1219 def _editPreferences(self, menuItem):
1220 """Callback for editing the preferences."""
1221 self._clearHotkeys()
1222 self._preferences.run(self.config)
1223 self._setupTimeSync()
1224 self._listenHotkeys()
1225
1226 def _reportBug(self, menuItem):
1227 """Callback for reporting a bug."""
1228 self._bugReportDialog.run()
1229
1230 def _setupTimeSync(self):
1231 """Enable or disable the simulator time synchronization based on the
1232 configuration."""
1233 simulator = self._simulator
1234 if simulator is not None:
1235 if self.config.syncFSTime:
1236 simulator.enableTimeSync()
1237 else:
1238 simulator.disableTimeSync()
1239
1240 def _loadPIREP(self, menuItem):
1241 """Load a PIREP for sending."""
1242 dialog = self._getLoadPirepDialog()
1243
1244 if self._lastLoadedPIREP:
1245 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1246 else:
1247 pirepDirectory = self.config.pirepDirectory
1248 if pirepDirectory is not None:
1249 dialog.set_current_folder(pirepDirectory)
1250
1251 result = dialog.run()
1252 dialog.hide()
1253
1254 if result==RESPONSETYPE_OK:
1255 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
1256
1257 pirep = PIREP.load(self._lastLoadedPIREP)
1258 if pirep is None:
1259 dialog = gtk.MessageDialog(parent = self._mainWindow,
1260 type = MESSAGETYPE_ERROR,
1261 message_format = xstr("loadPIREP_failed"))
1262 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1263 dialog.set_title(WINDOW_TITLE_BASE)
1264 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1265 dialog.run()
1266 dialog.hide()
1267 else:
1268 dialog = self._getSendLoadedDialog(pirep)
1269 dialog.show_all()
1270 while True:
1271 result = dialog.run()
1272
1273 if result==RESPONSETYPE_OK:
1274 self.sendPIREP(pirep)
1275 elif result==1:
1276 self._pirepViewer.setPIREP(pirep)
1277 self._pirepViewer.show_all()
1278 self._pirepViewer.run()
1279 self._pirepViewer.hide()
1280 else:
1281 break
1282
1283 dialog.hide()
1284
1285 def _getLoadPirepDialog(self):
1286 """Get the PIREP loading file chooser dialog.
1287
1288 If it is not created yet, it will be created."""
1289 if self._loadPIREPDialog is None:
1290 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1291 xstr("loadPIREP_browser_title"),
1292 action = FILE_CHOOSER_ACTION_OPEN,
1293 buttons = (gtk.STOCK_CANCEL,
1294 RESPONSETYPE_CANCEL,
1295 gtk.STOCK_OK, RESPONSETYPE_OK),
1296 parent = self._mainWindow)
1297 dialog.set_modal(True)
1298
1299
1300 filter = gtk.FileFilter()
1301 filter.set_name(xstr("file_filter_pireps"))
1302 filter.add_pattern("*.pirep")
1303 dialog.add_filter(filter)
1304
1305 filter = gtk.FileFilter()
1306 filter.set_name(xstr("file_filter_all"))
1307 filter.add_pattern("*.*")
1308 dialog.add_filter(filter)
1309
1310 self._loadPIREPDialog = dialog
1311
1312 return self._loadPIREPDialog
1313
1314 def _getSendLoadedDialog(self, pirep):
1315 """Get a dialog displaying the main information of the flight from the
1316 PIREP and providing Cancel and Send buttons."""
1317 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1318 xstr("loadPIREP_send_title"),
1319 parent = self._mainWindow,
1320 flags = DIALOG_MODAL)
1321
1322 contentArea = dialog.get_content_area()
1323
1324 label = gtk.Label(xstr("loadPIREP_send_help"))
1325 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1326 xscale = 0.0, yscale = 0.0)
1327 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1328 padding_left = 48, padding_right = 48)
1329 alignment.add(label)
1330 contentArea.pack_start(alignment, False, False, 8)
1331
1332 table = gtk.Table(5, 2)
1333 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1334 xscale = 0.0, yscale = 0.0)
1335 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1336 padding_left = 48, padding_right = 48)
1337 table.set_row_spacings(4)
1338 table.set_col_spacings(16)
1339 tableAlignment.add(table)
1340 contentArea.pack_start(tableAlignment, True, True, 8)
1341
1342 bookedFlight = pirep.bookedFlight
1343
1344 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1345 label.set_use_markup(True)
1346 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1347 xscale = 0.0, yscale = 0.0)
1348 labelAlignment.add(label)
1349 table.attach(labelAlignment, 0, 1, 0, 1)
1350
1351 label = gtk.Label(bookedFlight.callsign)
1352 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1353 xscale = 0.0, yscale = 0.0)
1354 labelAlignment.add(label)
1355 table.attach(labelAlignment, 1, 2, 0, 1)
1356
1357 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1358 label.set_use_markup(True)
1359 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1360 xscale = 0.0, yscale = 0.0)
1361 labelAlignment.add(label)
1362 table.attach(labelAlignment, 0, 1, 1, 2)
1363
1364 label = gtk.Label(str(bookedFlight.departureTime.date()))
1365 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1366 xscale = 0.0, yscale = 0.0)
1367 labelAlignment.add(label)
1368 table.attach(labelAlignment, 1, 2, 1, 2)
1369
1370 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1371 label.set_use_markup(True)
1372 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1373 xscale = 0.0, yscale = 0.0)
1374 labelAlignment.add(label)
1375 table.attach(labelAlignment, 0, 1, 2, 3)
1376
1377 label = gtk.Label(bookedFlight.departureICAO)
1378 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1379 xscale = 0.0, yscale = 0.0)
1380 labelAlignment.add(label)
1381 table.attach(labelAlignment, 1, 2, 2, 3)
1382
1383 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1384 label.set_use_markup(True)
1385 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1386 xscale = 0.0, yscale = 0.0)
1387 labelAlignment.add(label)
1388 table.attach(labelAlignment, 0, 1, 3, 4)
1389
1390 label = gtk.Label(bookedFlight.arrivalICAO)
1391 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1392 xscale = 0.0, yscale = 0.0)
1393 labelAlignment.add(label)
1394 table.attach(labelAlignment, 1, 2, 3, 4)
1395
1396 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1397 label.set_use_markup(True)
1398 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1399 xscale = 0.0, yscale = 0.0)
1400 labelAlignment.add(label)
1401 table.attach(labelAlignment, 0, 1, 4, 5)
1402
1403 rating = pirep.rating
1404 label = gtk.Label()
1405 if rating<0:
1406 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1407 else:
1408 label.set_text("%.1f %%" % (rating,))
1409
1410 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1411 xscale = 0.0, yscale = 0.0)
1412 labelAlignment.add(label)
1413 table.attach(labelAlignment, 1, 2, 4, 5)
1414
1415 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
1416 dialog.add_button(xstr("viewPIREP"), 1)
1417 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1418
1419 return dialog
1420
1421 def sendPIREP(self, pirep, callback = None):
1422 """Send the given PIREP."""
1423 self.beginBusy(xstr("sendPIREP_busy"))
1424 self._sendPIREPCallback = callback
1425 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1426
1427 def _pirepSentCallback(self, returned, result):
1428 """Callback for the PIREP sending result."""
1429 gobject.idle_add(self._handlePIREPSent, returned, result)
1430
1431 def _handlePIREPSent(self, returned, result):
1432 """Callback for the PIREP sending result."""
1433 self.endBusy()
1434 secondaryMarkup = None
1435 type = MESSAGETYPE_ERROR
1436 if returned:
1437 if result.success:
1438 type = MESSAGETYPE_INFO
1439 messageFormat = xstr("sendPIREP_success")
1440 secondaryMarkup = xstr("sendPIREP_success_sec")
1441 elif result.alreadyFlown:
1442 messageFormat = xstr("sendPIREP_already")
1443 secondaryMarkup = xstr("sendPIREP_already_sec")
1444 elif result.notAvailable:
1445 messageFormat = xstr("sendPIREP_notavail")
1446 else:
1447 messageFormat = xstr("sendPIREP_unknown")
1448 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1449 else:
1450 print "PIREP sending failed", result
1451 messageFormat = xstr("sendPIREP_failed")
1452 secondaryMarkup = xstr("sendPIREP_failed_sec")
1453
1454 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1455 type = type, message_format = messageFormat)
1456 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1457 dialog.set_title(WINDOW_TITLE_BASE)
1458 if secondaryMarkup is not None:
1459 dialog.format_secondary_markup(secondaryMarkup)
1460
1461 dialog.run()
1462 dialog.hide()
1463
1464 callback = self._sendPIREPCallback
1465 self._sendPIREPCallback = None
1466 if callback is not None:
1467 callback(returned, result)
1468
1469 def sendBugReport(self, summary, description, email, callback = None):
1470 """Send the bug report with the given data."""
1471 description += "\n\n" + ("=" * 40)
1472 description += "\n\nThe contents of the log:\n\n"
1473
1474 for (timestampString, text) in self._logger.lines:
1475 description += unicode(formatFlightLogLine(timestampString, text))
1476
1477 description += "\n\n" + ("=" * 40)
1478 description += "\n\nThe contents of the debug log:\n\n"
1479
1480 buffer = self._debugLogView.get_buffer()
1481 description += buffer.get_text(buffer.get_start_iter(),
1482 buffer.get_end_iter(), True)
1483
1484 self.beginBusy(xstr("sendBugReport_busy"))
1485 self._sendBugReportCallback = callback
1486 self.webHandler.sendBugReport(self._bugReportSentCallback,
1487 summary, description, email)
1488
1489 def _cefInitialized(self):
1490 """Called when CEF has been initialized."""
1491 self._acars.start()
1492
1493 def _bugReportSentCallback(self, returned, result):
1494 """Callback function for the bug report sending result."""
1495 gobject.idle_add(self._handleBugReportSent, returned, result)
1496
1497 def _handleBugReportSent(self, returned, result):
1498 """Callback for the bug report sending result."""
1499 self.endBusy()
1500 secondaryMarkup = None
1501 type = MESSAGETYPE_ERROR
1502 if returned:
1503 if result.success:
1504 type = MESSAGETYPE_INFO
1505 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1506 secondaryMarkup = xstr("sendBugReport_success_sec")
1507 else:
1508 messageFormat = xstr("sendBugReport_error")
1509 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1510 else:
1511 messageFormat = xstr("sendBugReport_error")
1512 secondaryMarkup = xstr("sendBugReport_error_sec")
1513
1514 dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1515 type = type, message_format = messageFormat)
1516 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1517 dialog.set_title(WINDOW_TITLE_BASE)
1518 if secondaryMarkup is not None:
1519 dialog.format_secondary_markup(secondaryMarkup)
1520
1521 dialog.run()
1522 dialog.hide()
1523
1524 callback = self._sendBugReportCallback
1525 self._sendBugReportCallback = None
1526 if callback is not None:
1527 callback(returned, result)
1528
1529 def _listenHotkeys(self):
1530 """Setup the hotkeys based on the configuration."""
1531 if self._hotkeySetID is None and self._simulator is not None:
1532 self._pilotHotkeyIndex = None
1533 self._checklistHotkeyIndex = None
1534
1535 hotkeys = []
1536
1537 config = self.config
1538 if config.enableSounds and config.pilotControlsSounds:
1539 self._pilotHotkeyIndex = len(hotkeys)
1540 hotkeys.append(config.pilotHotkey)
1541
1542 if config.enableChecklists:
1543 self._checklistHotkeyIndex = len(hotkeys)
1544 hotkeys.append(config.checklistHotkey)
1545
1546 if hotkeys:
1547 self._hotkeySetID = \
1548 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1549
1550 def _clearHotkeys(self):
1551 """Clear the hotkeys."""
1552 if self._hotkeySetID is not None:
1553 self._hotkeySetID=None
1554 self._simulator.clearHotkeys()
1555
1556 def _handleHotkeys(self, id, hotkeys):
1557 """Handle the hotkeys."""
1558 if id==self._hotkeySetID:
1559 for index in hotkeys:
1560 if index==self._pilotHotkeyIndex:
1561 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1562 self._flight.pilotHotkeyPressed()
1563 elif index==self._checklistHotkeyIndex:
1564 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1565 self._flight.checklistHotkeyPressed()
1566 else:
1567 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
1568
1569 def _showManual(self, menuitem):
1570 """Show the user's manual."""
1571 webbrowser.open(url ="file://" +
1572 os.path.join(self._programDirectory, "doc", "manual",
1573 getLanguage(), "index.html"),
1574 new = 1)
1575
1576 def _showAbout(self, menuitem):
1577 """Show the about dialog."""
1578 dialog = self._getAboutDialog()
1579 dialog.show_all()
1580 dialog.run()
1581 dialog.hide()
1582
1583 def _getAboutDialog(self):
1584 """Get the about dialog.
1585
1586 If it does not exist yet, it will be created."""
1587 if self._aboutDialog is None:
1588 dialog = gtk.AboutDialog()
1589 dialog.set_transient_for(self._mainWindow)
1590 dialog.set_modal(True)
1591
1592 logoPath = os.path.join(self._programDirectory, "logo.png")
1593 logo = pixbuf_new_from_file(logoPath)
1594 dialog.set_logo(logo)
1595
1596 dialog.set_program_name(PROGRAM_NAME)
1597 dialog.set_version(const.VERSION)
1598 dialog.set_copyright("(c) 2012 by István Váradi")
1599 dialog.set_website("http://mlx.varadiistvan.hu")
1600 dialog.set_website_label(xstr("about_website"))
1601
1602 isHungarian = getLanguage()=="hu"
1603 authors = []
1604 for (familyName, firstName, role) in GUI._authors:
1605 author = "%s %s" % \
1606 (familyName if isHungarian else firstName,
1607 firstName if isHungarian else familyName)
1608 role = xstr("about_role_" + role)
1609 authors.append(author + " (" + role + ")")
1610 dialog.set_authors(authors)
1611
1612 dialog.set_license(xstr("about_license"))
1613
1614 if not pygobject:
1615 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1616
1617 self._aboutDialog = dialog
1618
1619 return self._aboutDialog
1620
1621 def _showAboutURL(self, dialog, link, user_data):
1622 """Show the about URL."""
1623 webbrowser.open(url = link, new = 1)
1624
1625 def _setTakeoffAntiIceOn(self, value):
1626 """Set the anti-ice on indicator."""
1627 self._wizard.takeoffAntiIceOn = value
1628
1629 def _setLandingAntiIceOn(self, value):
1630 """Set the anti-ice on indicator."""
1631 self._wizard.landingAntiIceOn = value
Note: See TracBrowser for help on using the repository browser.